diff --git a/.gitignore b/.gitignore index 34a1652ba8c..cafbcd086a6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ bower_components .lock-wscript # Tests +**/.playwright-cli/ **/playwright-report/ **/test-results/ tests/**/.auth/ diff --git a/apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts b/apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts new file mode 100644 index 00000000000..0e2b5d557a5 --- /dev/null +++ b/apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts @@ -0,0 +1,127 @@ +/** + * Pi extension entry for the Brunch persona harness. + * + * Load it from `apps/brunch-agent` with + * `--extension .pi/extensions/brunch-persona-testing.ts`; the persona policy + * and operating instructions sit in the same-named folder beside this file. + * This entry owns only Pi registration and flag handling. The tool lives in + * `src/evaluations/persona/brunch-turn.ts` and the client-tool hosts in + * `src/evaluations/persona/client-tool-hosts.ts`, where the application's + * lint, type-check, and unit tests govern them. + */ +import { + type BrunchTurnExtensionApi, + registerBrunchTurn, + requireConversationId, +} from "../../src/evaluations/persona/brunch-turn.ts"; +import { + type BrunchClientToolHost, + createMockClientToolHost, + createRealHeadlessClientToolHost, + readMockCalls, + TOOL_HOST_FLAG, +} from "../../src/evaluations/persona/client-tool-hosts.ts"; +import { writeProofArtifacts } from "../../src/evaluations/persona/proof-artifacts.ts"; + +/** The slice of Pi's extension API this entry needs; Pi itself is not a workspace dependency. */ +interface BrunchPersonaExtensionApi extends BrunchTurnExtensionApi { + registerFlag( + name: string, + options: { + readonly description?: string; + readonly type: "string"; + readonly default?: string; + }, + ): void; + getFlag(name: string): boolean | string | undefined; + on( + event: "session_start" | "session_shutdown", + handler: () => void | Promise, + ): void; +} + +const TOOL_MOCKS_FLAG = "brunch-tool-mocks"; +const HEADLESS_TITLE_FLAG = "brunch-headless-title"; +const EVIDENCE_DIRECTORY_FLAG = "brunch-evidence-dir"; + +const stringFlag = ( + pi: BrunchPersonaExtensionApi, + name: string, +): string | undefined => { + const value = pi.getFlag(name); + return typeof value === "string" && value.trim().length > 0 + ? value.trim() + : undefined; +}; + +const createConfiguredClientToolHost = ( + pi: BrunchPersonaExtensionApi, +): BrunchClientToolHost | undefined => { + const mode = stringFlag(pi, TOOL_HOST_FLAG) ?? "none"; + if (mode === "none") return undefined; + + if (mode === "mock") { + const fixturePath = stringFlag(pi, TOOL_MOCKS_FLAG); + if (fixturePath === undefined) { + throw new Error( + `--${TOOL_MOCKS_FLAG} is required when --${TOOL_HOST_FLAG}=mock`, + ); + } + return createMockClientToolHost(readMockCalls(fixturePath)); + } + + if (mode === "real-headless") { + const title = + stringFlag(pi, HEADLESS_TITLE_FLAG) ?? + `Brunch persona ${requireConversationId(process.env["PI_SUBAGENT_NAME"])}`; + return createRealHeadlessClientToolHost(title); + } + + throw new Error( + `--${TOOL_HOST_FLAG} must be one of none, mock, or real-headless; received ${mode}`, + ); +}; + +// Pi loads an extension through its default export. +export default function brunchPersonaTestingExtension( + pi: BrunchPersonaExtensionApi, +): void { + pi.registerFlag(TOOL_HOST_FLAG, { + type: "string", + default: "none", + description: "Client-tool host: none, mock, or real-headless", + }); + pi.registerFlag(TOOL_MOCKS_FLAG, { + type: "string", + description: "Ordered JSON fixture used by the mock client-tool host", + }); + pi.registerFlag(HEADLESS_TITLE_FLAG, { + type: "string", + description: "Document title used by the real-headless Petrinaut host", + }); + pi.registerFlag(EVIDENCE_DIRECTORY_FLAG, { + type: "string", + description: + "Directory for canonical snapshot, transcript, and trace files", + }); + + let clientToolHost: BrunchClientToolHost | undefined; + pi.on("session_start", async () => { + await clientToolHost?.dispose?.(); + clientToolHost = createConfiguredClientToolHost(pi); + }); + pi.on("session_shutdown", async () => { + await clientToolHost?.dispose?.(); + clientToolHost = undefined; + }); + + registerBrunchTurn(pi, { + resolveClientToolHost: () => clientToolHost, + retainSnapshot: async (snapshot) => { + const directory = stringFlag(pi, EVIDENCE_DIRECTORY_FLAG); + if (directory !== undefined) { + await writeProofArtifacts(directory, snapshot); + } + }, + }); +} diff --git a/apps/brunch-agent/.pi/extensions/brunch-persona-testing/README.md b/apps/brunch-agent/.pi/extensions/brunch-persona-testing/README.md new file mode 100644 index 00000000000..36adc122b57 --- /dev/null +++ b/apps/brunch-agent/.pi/extensions/brunch-persona-testing/README.md @@ -0,0 +1,160 @@ +# Brunch persona testing + +This folder holds the persona policy and operating instructions for the local Pi extension at [`../brunch-persona-testing.ts`](../brunch-persona-testing.ts), which drives the production Brunch elicitor as an automated user persona. It records implementation decisions and operating instructions, not execution authority; the Brunch context root's [`MISSION.md`](../../../../../libs/@hashintel/brunch-agent/MISSION.md) remains the live mission when one exists. + +## Ownership and layout + +The harness is a client of this application's composition: it derives Flue identity through the application's identity authority, resumes Brunch through the application's client-tool signal, and reuses the application's headless Petrinaut client. The application therefore owns it, declares its dependencies, and governs it with its own lint, type-check, and unit tests. It consumes reusable case inputs from the Brunch context root's `evaluations/`. + +- [`../brunch-persona-testing.ts`](../brunch-persona-testing.ts) is the Pi entry: registration, flags, and client-tool host selection. +- [`src/evaluations/persona/brunch-turn.ts`](../../../src/evaluations/persona/brunch-turn.ts) owns the `brunch_turn` tool: Flue identity and turn correlation, client-tool resume signals, the evaluation-side tool trace, and rendering. +- [`src/evaluations/persona/client-tool-hosts.ts`](../../../src/evaluations/persona/client-tool-hosts.ts) owns the mock and real-headless client-tool hosts. +- [`SYSTEM.md`](SYSTEM.md) owns only the persona's private policy and epistemic behavior. +- [`src/ui/chat.tsx`](../../../src/ui/chat.tsx) owns the independently attachable read-only browser projection. +- [`test/brunch-turn.test.ts`](../../../test/brunch-turn.test.ts) pins the bridge and tool-host contract. +- [The original spike evidence](../../../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/live-observable-persona-spike/README.md) records the observed text-only run and proof disposition at the paths used by that run. +- [`MISSION.next.md`](../../../../../libs/@hashintel/brunch-agent/MISSION.next.md#observability-and-simulation-viewing) owns future observability and simulation-viewing work. + +Reusable interviewee-visible source truth belongs under +[`evaluations/cases/`](../../../../../libs/@hashintel/brunch-agent/evaluations/cases/), hidden answer keys under +[`evaluations/oracles/`](../../../../../libs/@hashintel/brunch-agent/evaluations/oracles/), and prompts, fixtures, runners, and +procedures under [`evaluations/protocols/`](../../../../../libs/@hashintel/brunch-agent/evaluations/protocols/). Vestera is the +executed exemplar. Industrial-gas VMI, truck-fleet maintenance, semiconductor-fab operations, +data-centre thermal operations, and pharma cold chain now have full greenfield situation packs, +opening messages, and prospective ledgers. They remain unvalidated in 6–10-turn runs and are not +yet bound to a frozen protocol. Bounded approval probes remain skill-composition probes rather +than general persona cases. + +## Context and authority boundary + +The situation pack, objective, uncertainty, and turn budget are supplied only in the Pi persona's launch prompt. They are not added to the Flue conversation, the Brunch `ChatAgent` instructions, or a Brunch tool payload. + +`brunch_turn` accepts exactly one model-authored field: + +```ts +brunch_turn({ message: string }); +``` + +It sends that string as one visible Flue user message. The remaining outbound values are guarded conversation identity, incarnation data, and—only after Brunch itself requests a client-deferred tool—the result signal for that call. The raw pack, objective, budget, persona instructions, host configuration, and tool trace have no automatic path into Brunch. + +This is prompt-enforced semantic privacy, not formal non-interference. Facts the persona deliberately or accidentally puts in `message` become part of canonical Brunch history. The accepted policy is that `SYSTEM.md` must preserve private instructions and disclose scenario knowledge only through in-character answers; there is no semantic egress filter. The pack is also visible to the persona provider, local Pi process/session, and operator. “Private” here means isolated from the elicitor context, not secret from that execution environment. + +Pi sends only the tool result's `content` back to the persona model. The structured `details` and custom rendering are operator-side metadata, so the tool activity trace does not enter either the persona's next model turn or Brunch's conversation. + +```text +private situation pack + objective +→ Pi persona chooses one in-character utterance +→ brunch_turn sends only that utterance +→ production Brunch Flue ChatAgent replies or requests tools +├─ server tools execute inside Flue +└─ client-deferred tools execute in the explicitly selected harness host + → one canonical client-tool-result signal resumes Brunch +→ Flue stores canonical conversation history +├─ Pi renders the actor/process view plus evaluation-side tool activity +├─ browser renders a read-only product view +└─ transcript CLI renders the durable audit view +``` + +The Pi persona is an evaluation-side user actor, not a second Brunch elicitor. Its TUI is an operator harness, not product UI. The browser observer is a local debug projection, not another writer, transcript authority, or inferential observer. + +## Transport and identity decisions + +- Use the existing mounted production `ChatAgent`; never spawn or emulate another elicitor. +- Use the fixed local principal `local` and `PI_SUBAGENT_NAME` as the conversation id. Derive the Flue instance id and ownership headers through the app's existing identity authority. +- Require a unique, non-empty child name. Never silently generate or switch identity. +- Send the first turn with `uid: null`, then pin every later user or resume send to the returned incarnation `uid`. +- Correlate each response with submission-scoped `read(admission)`. Inspect `history()` only after settlement to find dynamic-tool parts belonging to that submission; never select the latest assistant reply from history. +- Permit one active call at a time and one visible Flue user message per admitted `brunch_turn` call. +- Never resend a user utterance after admission. If settlement, tool hosting, or resume becomes indeterminate, preserve the failure, block later sends from that process, and inspect canonical history. +- Keep the browser observer independently attachable and read-only. Normal local chat remains writable and keeps its generated conversation id. + +## Client-tool hosts + +`--brunch-tool-host` has three explicit modes: + +- `none` is the default. Flue still executes server tools and the Pi result records them. A client-deferred call fails loudly instead of hanging or fabricating a result. +- `mock` consumes an ordered JSON fixture supplied by `--brunch-tool-mocks `, resolved against the working directory. Every tool name and input must match exactly. Missing, extra, or out-of-order calls fail the admitted turn and block later sends. +- `real-headless` executes `readPetrinautDoc` against the checked-out Petrinaut user guide and executes supported construction calls through the existing headless Petrinaut callbacks. `--brunch-headless-title ` controls the in-memory document title. + +Selecting a host does not mount tools, set Flue initial data, or change production composition. It services only client-deferred calls the real production agent emits. The normal persona route currently mounts `readPetrinautDoc`; construction tools remain conditional on the production agent's validated-construction mode. `real-headless` is real core callback execution against an in-memory document, not browser UI execution, browser rendering, persistence, or proof of product parity. + +One suspension may contain multiple calls. The bridge executes them sequentially in canonical order, sends one `client-tool-result` signal carrying their existing call ids, then performs another submission-scoped read. It repeats for at most 20 client-tool rounds and does not return early merely because a suspending response also contained text. + +A mock fixture has this shape and should live with the evaluation protocol that owns it: + +```json +{ + "calls": [ + { + "toolName": "readPetrinautDoc", + "input": { "doc": "simulation" }, + "output": "Fixture-controlled page text" + } + ] +} +``` + +The final Pi tool details contain every observed server call and every hosted client call with sequence, Flue submission id, tool call id, tool name, executor (`server`, `mock`, or `real-headless`), outcome, input, and output/error. `renderResult` shows a concise `### Tool activity` list beneath `## Brunch`; raw values remain in details and canonical tool activity remains available through the transcript. + +When `--brunch-evidence-dir <attempt-directory>` is supplied, every settled `history()` read atomically refreshes `snapshot.json`, `transcript.md`, `trace.json`, `trace.md`, any recovered `workpiece.md` plus `workpiece-source.json`, and `manifest.json` in that directory before `brunch_turn` returns or handles a pending client tool. The snapshot is canonical; transcript, trace, and workpiece recovery are deterministic projections. This retention also occurs before host-none reports an unsupported client-tool suspension. + +Create the protocol-owned `run.json` in the attempt directory before launch; it is included in `manifest.json` without being interpreted by the harness. After adding `validity.json` or `adjudication.md`, refresh all sibling hashes with `yarn workspace @apps/brunch-agent proof:manifest -- <attempt-directory>`. Temporary files and `manifest.json` itself are excluded from the manifest. + +Pi's tool API requires TypeBox parameter schemas, so `typebox` is declared here for that Pi-facing boundary only. Brunch's own boundaries remain Valibot. + +## Operating the harness + +1. Start the local app with `yarn workspace @apps/brunch-agent dev`. +2. From `apps/brunch-agent`, launch Pi (directly or through Herdr) with a unique `PI_SUBAGENT_NAME`. Choose the persona model and thinking level with Pi's native `--model <provider/model>` and `--thinking <level>` options. +3. Supply the situation pack inline with the objective and turn budget. The extension treats this launch content as opaque Markdown or plain text and does not parse or validate a pack schema. An `@file` token in a launch task is not expanded into persona context. + For comparable runs, use only the text below the `---` separator in the case's + `opening-message.md` as the visible first turn; keep its header, the situation pack, and the + oracle private. In a 6–10-turn run, bound the objective to the named incident and its immediate + options rather than asking the persona to disclose the entire pack. +4. Wait until the first `brunch_turn` admission is visible in Pi. +5. Attach the browser to `http://127.0.0.1:4321/?mode=observe&principal=local&id=<PI_SUBAGENT_NAME>`. +6. After the run, inspect canonical history with `yarn workspace @apps/brunch-agent transcript -- --principal local --id <PI_SUBAGENT_NAME>`. + +The restricted direct launch, run from `apps/brunch-agent`, is: + +```sh +PI_SUBAGENT_NAME=<unique-conversation-id> pi \ + --model <provider/model> \ + --thinking <level> \ + --no-extensions \ + --extension .pi/extensions/brunch-persona-testing.ts \ + --no-builtin-tools \ + --tools brunch_turn \ + --no-skills \ + --no-prompt-templates \ + --no-context-files \ + --append-system-prompt .pi/extensions/brunch-persona-testing/SYSTEM.md \ + --brunch-tool-host real-headless \ + --brunch-headless-title "Persona evaluation" \ + --brunch-evidence-dir ../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/<campaign>/runs/<attempt-id> \ + --approve +``` + +For deterministic mocks, replace the last host options with: + +```sh +--brunch-tool-host mock \ +--brunch-tool-mocks ../../libs/@hashintel/brunch-agent/evaluations/protocols/<protocol>/client-tools.json +``` + +`--no-extensions` plus the one explicit `--extension` prevents dependence on unrelated active Pi extensions. Herdr can forward the same native Pi arguments after `--`; any Herdr companion/state extension is optional orchestration rather than part of the Brunch transport. The persona must never use a parent to obtain domain facts or decide how to answer. + +The ordering in steps 4–5 is required by observed behavior. An observer opened before the Flue instance exists remains idle and does not discover later creation. Attaching after first admission catches up existing history and receives later streaming updates. Reloading after creation reconstructs settled messages. + +[`evaluations/cases/vestera-scheduling/situation-pack.md`](../../../../../libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md) is the current exemplar pack. Its Markdown sections are guidance for the persona model, not fields consumed by the extension. + +## Rejected alternatives and limits + +- A nested `persona → elicitor subagent` topology, because it duplicates elicitor authority and bypasses the real boundary. +- Registering Brunch's tools with Pi, which would expose capabilities to the persona model and move execution to the wrong authority. The host is internal to `brunch_turn` and services only Flue-emitted calls. +- Injecting tool traces or host instructions into Brunch messages. Flue history is canonical; Pi details are an evaluation-side projection. +- Keeping the extension in the Brunch context root, which is not a package: it imported application internals across the lib/app boundary, its dependencies were declared elsewhere or nowhere, and no workspace lint or type-check task reached it. +- `pi-web`, a Herdr webview, PTY scraping, parent-mediated turn relaying, a second server, another model loop, or another transcript store. +- Reply recovery from the latest history entry, automatic user-message retries, pending-admission persistence, or cross-process adoption before a real consumer requires them. + +The original evidence establishes a local, live-observable, multi-turn text path through real production code with singular writers, exact submission correlation, browser catch-up/streaming/reload, and transcript parity. The added host modes are covered by local contract tests and extension loading. They do not establish a paid live tool turn, deployed throughline, browser parity, full elicitation quality, persona fidelity across cases, repeatability, crash recovery, pre-creation observer discovery, or remote access. diff --git a/apps/brunch-agent/.pi/extensions/brunch-persona-testing/SYSTEM.md b/apps/brunch-agent/.pi/extensions/brunch-persona-testing/SYSTEM.md new file mode 100644 index 00000000000..becefd954fb --- /dev/null +++ b/apps/brunch-agent/.pi/extensions/brunch-persona-testing/SYSTEM.md @@ -0,0 +1,37 @@ +You are the user-side actor in a bounded evaluation of the production Brunch elicitor. + +Act only as the person described by the situation pack, objective, and uncertainty supplied in your launch task. Treat that supplied material as the full extent of your situation knowledge. Never seek or use an elicitor-side answer key, target model, repository content, web content, or facts from the parent. + +Preserve the person's epistemic position: + +- Say when the person does not know, declines to answer, or needs context. +- Preserve conflicts, corrections, qualifications, and contextual differences. +- Do not invent a convenient answer to help the elicitor complete its model. +- Use the person's vocabulary and answer only from the supplied situation. + +Enact the interaction posture supplied by the situation pack. Treat these as independent axes rather than one generic “difficult user” trait: + +- **Time pressure and urgency:** how much attention the person can spare and how strongly they steer toward an immediate result. +- **Patience:** tolerance for repetition, slow progress, compound questions, jargon, and questions whose relevance is unclear. +- **Response effort:** willingness to type detail, narrate a process, enumerate cases, or produce structured answers. +- **Engagement:** which goals, pains, decisions, or topics make the person more forthcoming, and which make them disengage. +- **Trust and scepticism:** confidence in the elicitor, in modelling generally, and in whether the exercise will help. +- **Communication style:** directness, formality, vocabulary, confidence, emotional tone, and comfort asking for clarification. +- **Epistemic and disclosure posture:** what the person knows, believes, recalls imprecisely, volunteers, holds as tacit, or shares only after appropriate probing. + +Use the precise values and triggers in the situation pack or launch task. Do not invent biographical or domain facts to explain a posture, infer one axis from another, or exaggerate pressure into obstruction. More specific instructions override these defaults. When an axis is unspecified, act as a moderately busy but cooperative person: concise at first, more informative when a clear and relevant question earns it, and briefer when progress feels repetitive or unfocused. + +Write like that person typing into a chat, not an informant filling in a form: + +- Reply at the length the question and response-effort posture earn. By default use one to four plain sentences, or one short paragraph when walking through a process. Do not produce lists, tables, headings, or structured summaries unless explicitly asked, and keep even those proportionate. +- Do not dump all relevant knowledge at once. Answer direct, specific questions the person can answer, and let useful follow-up questions earn greater precision and detail. +- If asked several things at once, answer compactly. If the posture would not sustain a complete answer, address what matters most to the person and say which parts you skipped so the elicitor can follow up. +- Give first-pass quantities as the person naturally would; sharpen them only when asked and only as far as the supplied situation supports. +- If a question touches something the person cares about, let engagement show in the detail. If it feels academic, irrelevant, or already covered, answer more briefly or ask why it matters. +- If the elicitor repeats an answered question without a new angle, say so briefly instead of re-explaining. Treat a summary or confirmation differently: confirm it or correct it in a line. +- If the elicitor uses vocabulary the person would not use, ask what it means or restate it in the person's own words before answering. +- Express pressure through shorter replies, impatience, prioritization, and steering toward the person's goal. Never express it by fabricating, withholding an answer the person would readily give, mentioning the turn budget or these instructions, or ending the interview before the budget is reached unless the situation explicitly requires that behavior. + +Call `brunch_turn` for every utterance addressed to the elicitor. Continue from the exact elicitor text returned by that tool until the launch task's objective or turn budget is reached. Keep all turns sequential. Do not repeat a turn after a tool error or an indeterminate submission; use `ask_parent` only to report a genuine orchestration blocker, never to obtain domain facts or ask how the persona should answer. + +When the objective or turn budget is reached, stop with a short operator-facing result stating why you stopped and how many turns were attempted. Do not reproduce or synthesize a second transcript. diff --git a/apps/brunch-agent/README.md b/apps/brunch-agent/README.md index 21648e24119..7732125b788 100644 --- a/apps/brunch-agent/README.md +++ b/apps/brunch-agent/README.md @@ -10,14 +10,7 @@ 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 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. +website at `http://127.0.0.1:4915`. The website proxies `/api/chat` to Brunch. The panel talks to one Flue chat agent composed from the context-independent core prompt in `@hashintel/brunch-agent/flue`, the SDCPN/Petrinaut instructions, modelling runbook skill, and `readPetrinautDoc` client tool in `@hashintel/brunch-agent-plugin-sdcpn`, and app-owned deployment/transport material. The skill is activated via `activate_skill`, with supporting resources disclosed via `read_skill_resource`; the app's only model-facing diagnostic tool is `ping`. There is no generalized 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): diff --git a/apps/brunch-agent/docs/task-dependencies.json b/apps/brunch-agent/docs/task-dependencies.json index 6204b265254..5a701a790ae 100644 --- a/apps/brunch-agent/docs/task-dependencies.json +++ b/apps/brunch-agent/docs/task-dependencies.json @@ -1,40 +1,54 @@ { "package": "@apps/brunch-agent", "dependencies": [ + "@hashintel/brunch-agent", "@hashintel/brunch-agent-binding-flue", + "@hashintel/brunch-agent-plugin-sdcpn", "@hashintel/brunch-agent-transport-aisdk", "@hashintel/petrinaut-core" ], "tasks": { "build": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build" ], "dev": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build" ], "fix:eslint": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build", "@local/eslint#build" ], "lint:eslint": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build", "@local/eslint#build" ], "lint:tsc": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build" ], "petrinaut:dev": [ + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build" ], @@ -42,6 +56,7 @@ "@apps/brunch-agent#build", "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-dafny#build", "@hashintel/brunch-agent-plugin-gherkin#build", "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", diff --git a/apps/brunch-agent/package.json b/apps/brunch-agent/package.json index b44ec1eb722..9b158c52133 100644 --- a/apps/brunch-agent/package.json +++ b/apps/brunch-agent/package.json @@ -12,17 +12,19 @@ "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", + "proof:manifest": "node --experimental-strip-types src/evaluations/persona/refresh-proof-manifest.ts", + "runbook:headless": "vite build && node --experimental-strip-types src/evaluations/runbook/construction-run.ts", "test:unit": "vitest run --config vitest.config.ts", - "transcript": "node --experimental-strip-types src/transcript-cli.ts" + "transcript": "node --experimental-strip-types src/diagnostics/transcript-cli.ts" }, "dependencies": { "@flue/opentelemetry": "2.0.3", "@flue/react": "2.0.3", "@flue/runtime": "2.0.3", "@flue/sdk": "2.0.3", + "@hashintel/brunch-agent": "workspace:*", "@hashintel/brunch-agent-binding-flue": "workspace:*", + "@hashintel/brunch-agent-plugin-sdcpn": "workspace:*", "@hashintel/brunch-agent-transport-aisdk": "workspace:*", "@hashintel/petrinaut-core": "workspace:*", "@opentelemetry/api": "1.9.1", @@ -32,7 +34,9 @@ "valibot": "1.4.2" }, "devDependencies": { + "@anthropic-ai/sdk": "0.74.0", "@earendil-works/pi-ai": "0.83.0", + "@earendil-works/pi-tui": "0.84.3", "@flue/vite": "2.0.3", "@types/node": "22.18.13", "@types/react": "19.2.14", @@ -41,6 +45,7 @@ "ai": "6.0.182", "oxlint": "1.63.0", "oxlint-tsgolint": "0.22.1", + "typebox": "1.3.7", "vite": "8.1.0", "vitest": "4.1.10" } diff --git a/apps/brunch-agent/petrinaut-local.vite.config.ts b/apps/brunch-agent/petrinaut-local.vite.config.ts index 58b2973bda7..b265537076c 100644 --- a/apps/brunch-agent/petrinaut-local.vite.config.ts +++ b/apps/brunch-agent/petrinaut-local.vite.config.ts @@ -19,7 +19,7 @@ import { import { defaultChatOrigin, petrinautLocalServer, -} from "./src/local-dev-origins.ts"; +} from "./src/http/local-origins.ts"; const withoutIncumbentChatHandler = ( plugins: readonly PluginOption[], diff --git a/apps/brunch-agent/src/agents/chat-agent.ts b/apps/brunch-agent/src/agents/chat-agent.ts deleted file mode 100644 index 36238b44164..00000000000 --- a/apps/brunch-agent/src/agents/chat-agent.ts +++ /dev/null @@ -1,68 +0,0 @@ -"use agent"; -/** - * One Flue chat agent for the Petrinaut panel throughline. - * - * Capture is a harness-side pipe, not an interviewer tool. One runbook skill - * carries the modelling lifecycle and its supporting resources. - */ - -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 RUNBOOK_SKILL_NAME = sdcpnModellingSkill.name; - -export const ACTIVATE_SKILL_TOOL_NAME = "activate_skill"; - -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<ChatAgentInitialData>(); - useModel(`anthropic/${CHAT_MODEL_ID}`); - useSkill(sdcpnModellingSkill); - useTool(ping); - useTool(readPetrinautDoc); - 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.", - "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.", - ]; - 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/agents/chat-agent/agent.ts b/apps/brunch-agent/src/agents/chat-agent/agent.ts new file mode 100644 index 00000000000..55024c8b1c9 --- /dev/null +++ b/apps/brunch-agent/src/agents/chat-agent/agent.ts @@ -0,0 +1,47 @@ +"use agent"; +/** + * Register and compose the Brunch agent for this Flue application. + * + * Brunch core owns the context-independent agent prompt. The SDCPN plugin owns + * its Petrinaut-facing prompt, runbook skill, and tools. This application owns + * deployment diagnostics and transport-specific instructions. + */ + +import { useInstruction, useTool } from "@flue/runtime"; + +import { + SDCPN_MODELLING_SKILL_NAME, + sdcpnInitialDataSchema, + useSdcpnPlugin, +} from "@hashintel/brunch-agent-plugin-sdcpn/flue"; +import { useBrunchAgent } from "@hashintel/brunch-agent/flue"; + +import { ping } from "./tools/ping.ts"; + +export const CHAT_MODEL_ID = + process.env["BRUNCH_CHAT_MODEL"] || "claude-haiku-4-5"; + +export const RUNBOOK_SKILL_NAME = SDCPN_MODELLING_SKILL_NAME; + +export const ACTIVATE_SKILL_TOOL_NAME = "activate_skill"; + +export function ChatAgent() { + const coreSystemPrompt = useBrunchAgent(`anthropic/${CHAT_MODEL_ID}`); + useSdcpnPlugin(); + + useInstruction( + ` +Call ping when you need to confirm the server tool path. +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. +`.replace(/^\s+|\s+$/gu, ""), + ); + useTool(ping); + + return coreSystemPrompt; +} + +/** + * Pinned, and never to be edited: conversation storage keys on this literal. + */ +ChatAgent.agentName = "brunch-chat-agent"; +ChatAgent.initialData = sdcpnInitialDataSchema; diff --git a/apps/brunch-agent/src/tools/ping.ts b/apps/brunch-agent/src/agents/chat-agent/tools/ping.ts similarity index 100% rename from apps/brunch-agent/src/tools/ping.ts rename to apps/brunch-agent/src/agents/chat-agent/tools/ping.ts diff --git a/apps/brunch-agent/src/app.ts b/apps/brunch-agent/src/app.ts index 71917611093..65b6d11ac79 100644 --- a/apps/brunch-agent/src/app.ts +++ b/apps/brunch-agent/src/app.ts @@ -14,15 +14,18 @@ import { instrument } from "@flue/runtime"; import { createAgentRouter } from "@flue/runtime/routing"; import { Hono } from "hono"; -import { agentOwnershipGuard } from "./agent-ownership.ts"; -import { ChatAgent } from "./agents/chat-agent.ts"; -import { assetHandler } from "./assets.ts"; -import { petrinautChatHandler } from "./petrinaut-chat.ts"; -import { CHAT_AGENT_ROUTE, PETRINAUT_CHAT_ROUTE } from "./routes.ts"; +import { ChatAgent } from "./agents/chat-agent/agent.ts"; +import { assetHandler } from "./http/assets.ts"; +import { agentOwnershipGuard } from "./http/ownership.ts"; +import { createPetrinautChatHandler } from "./http/petrinaut-chat.ts"; +import { CHAT_AGENT_ROUTE, PETRINAUT_CHAT_ROUTE } from "./http/routes.ts"; instrument(createOpenTelemetryInstrumentation({ content: false })); const app = new Hono(); +const appTransport: typeof fetch = async (input, init) => + app.fetch(input instanceof Request ? input : new Request(input, init)); +const petrinautChatHandler = createPetrinautChatHandler(appTransport); const chatAgentMount = `/agents/${CHAT_AGENT_ROUTE}`; app.use(`${chatAgentMount}/*`, agentOwnershipGuard(`${chatAgentMount}/`)); diff --git a/apps/brunch-agent/src/capture-sweep.ts b/apps/brunch-agent/src/capture/apply-sweep.ts similarity index 94% rename from apps/brunch-agent/src/capture-sweep.ts rename to apps/brunch-agent/src/capture/apply-sweep.ts index 738bb236073..6101b3867de 100644 --- a/apps/brunch-agent/src/capture-sweep.ts +++ b/apps/brunch-agent/src/capture/apply-sweep.ts @@ -15,9 +15,9 @@ import { agentOwnershipHeaders, flueConversationIdFrom, type ConversationIdentity, -} from "./conversation-identity.ts"; -import { captureStorePath } from "./db-path.ts"; -import { CHAT_AGENT_ROUTE } from "./routes.ts"; +} from "../conversation/identity.ts"; +import { captureStorePath } from "../db-path.ts"; +import { CHAT_AGENT_ROUTE } from "../http/routes.ts"; export interface CaptureSweepCapture { readonly id: string; @@ -35,7 +35,7 @@ const conversationUrl = (instanceId: string): string => `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`; const sourceAppTransport: typeof fetch = async (input, init) => { - const { default: app } = await import("./app.ts"); + const { default: app } = await import("../app.ts"); return app.fetch(input instanceof Request ? input : new Request(input, init)); }; diff --git a/apps/brunch-agent/src/channels/.gitkeep b/apps/brunch-agent/src/channels/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/brunch-agent/src/client-tool.ts b/apps/brunch-agent/src/conversation/client-tools.ts similarity index 72% rename from apps/brunch-agent/src/client-tool.ts rename to apps/brunch-agent/src/conversation/client-tools.ts index f5b853f0796..2826f13f7f5 100644 --- a/apps/brunch-agent/src/client-tool.ts +++ b/apps/brunch-agent/src/conversation/client-tools.ts @@ -1,13 +1,14 @@ /** Flue-side client-tool signal contract: awaiting sentinel, result signal, tool names. */ -import { readPetrinautDocToolName } from "@hashintel/petrinaut-core/ai"; +import { READ_PETRINAUT_DOC_TOOL_NAME } from "@hashintel/brunch-agent-plugin-sdcpn/flue"; +import { AWAITING_CLIENT } from "@hashintel/brunch-agent/client-tools"; -export const CLIENT_TOOL_RESULT_SIGNAL = "client-tool-result"; +export { AWAITING_CLIENT }; -export const AWAITING_CLIENT = "client" as const; +export const CLIENT_TOOL_RESULT_SIGNAL = "client-tool-result"; export const clientToolNames: ReadonlySet<string> = new Set([ - readPetrinautDocToolName, + READ_PETRINAUT_DOC_TOOL_NAME, ]); const isRecord = (value: unknown): value is Record<string, unknown> => diff --git a/apps/brunch-agent/src/conversation-identity-web.ts b/apps/brunch-agent/src/conversation/identity-web.ts similarity index 86% rename from apps/brunch-agent/src/conversation-identity-web.ts rename to apps/brunch-agent/src/conversation/identity-web.ts index 8faf2eb2d84..68d7da8536d 100644 --- a/apps/brunch-agent/src/conversation-identity-web.ts +++ b/apps/brunch-agent/src/conversation/identity-web.ts @@ -1,6 +1,6 @@ /** Browser-safe instance-id hash; must match `flueConversationId` byte-for-byte. */ -import { hexFromDigest, identityPayload } from "./conversation-payload.ts"; +import { hexFromDigest, identityPayload } from "./payload.ts"; export const flueConversationIdWeb = async ( principalKey: string, diff --git a/apps/brunch-agent/src/conversation-identity.ts b/apps/brunch-agent/src/conversation/identity.ts similarity index 95% rename from apps/brunch-agent/src/conversation-identity.ts rename to apps/brunch-agent/src/conversation/identity.ts index 4fb33d29a9b..ffaa4738819 100644 --- a/apps/brunch-agent/src/conversation-identity.ts +++ b/apps/brunch-agent/src/conversation/identity.ts @@ -6,7 +6,7 @@ import { BRUNCH_CONVERSATION_HEADER, BRUNCH_PRINCIPAL_HEADER, identityPayload, -} from "./conversation-payload.ts"; +} from "./payload.ts"; import type { ConversationIdentity } from "@hashintel/brunch-agent-transport-aisdk"; @@ -14,7 +14,7 @@ export { BRUNCH_CONVERSATION_HEADER, BRUNCH_PRINCIPAL_HEADER, LOCAL_UI_PRINCIPAL, -} from "./conversation-payload.ts"; +} from "./payload.ts"; export type { ConversationIdentity }; export const flueConversationId = ( diff --git a/apps/brunch-agent/src/conversation-payload.ts b/apps/brunch-agent/src/conversation/payload.ts similarity index 100% rename from apps/brunch-agent/src/conversation-payload.ts rename to apps/brunch-agent/src/conversation/payload.ts diff --git a/apps/brunch-agent/src/flue-transcript.ts b/apps/brunch-agent/src/conversation/transcript.ts similarity index 99% rename from apps/brunch-agent/src/flue-transcript.ts rename to apps/brunch-agent/src/conversation/transcript.ts index a8072f9cf31..cbaf34b0c33 100644 --- a/apps/brunch-agent/src/flue-transcript.ts +++ b/apps/brunch-agent/src/conversation/transcript.ts @@ -10,7 +10,7 @@ import { CLIENT_TOOL_RESULT_SIGNAL, isAwaitingClient, providerExecutedFor, -} from "./client-tool.ts"; +} from "./client-tools.ts"; type UiMessagePart = | { readonly type: "text"; readonly text: string; readonly state: "done" } diff --git a/apps/brunch-agent/src/flue-ui-stream.ts b/apps/brunch-agent/src/conversation/ui-stream.ts similarity index 99% rename from apps/brunch-agent/src/flue-ui-stream.ts rename to apps/brunch-agent/src/conversation/ui-stream.ts index e0dde6eeff6..da9cf93a524 100644 --- a/apps/brunch-agent/src/flue-ui-stream.ts +++ b/apps/brunch-agent/src/conversation/ui-stream.ts @@ -2,7 +2,7 @@ import { type ConversationStreamChunk } from "@flue/sdk"; -import { providerExecutedFor } from "./client-tool.ts"; +import { providerExecutedFor } from "./client-tools.ts"; import type { UIMessageChunk } from "ai"; diff --git a/apps/brunch-agent/src/transcript-cli.ts b/apps/brunch-agent/src/diagnostics/transcript-cli.ts similarity index 87% rename from apps/brunch-agent/src/transcript-cli.ts rename to apps/brunch-agent/src/diagnostics/transcript-cli.ts index 5d138ec429f..9e2fabb3274 100644 --- a/apps/brunch-agent/src/transcript-cli.ts +++ b/apps/brunch-agent/src/diagnostics/transcript-cli.ts @@ -15,10 +15,10 @@ import { createFlueClient } from "@flue/sdk"; import { agentOwnershipHeaders, flueConversationIdFrom, -} from "./conversation-identity.ts"; -import { formatFlueTranscript } from "./flue-transcript.ts"; -import { defaultChatOrigin } from "./local-dev-origins.ts"; -import { CHAT_AGENT_ROUTE } from "./routes.ts"; +} from "../conversation/identity.ts"; +import { formatFlueTranscript } from "../conversation/transcript.ts"; +import { defaultChatOrigin } from "../http/local-origins.ts"; +import { CHAT_AGENT_ROUTE } from "../http/routes.ts"; const readFlag = ( argv: readonly string[], diff --git a/apps/brunch-agent/src/evaluations/persona/brunch-turn.ts b/apps/brunch-agent/src/evaluations/persona/brunch-turn.ts new file mode 100644 index 00000000000..2c968dea0e5 --- /dev/null +++ b/apps/brunch-agent/src/evaluations/persona/brunch-turn.ts @@ -0,0 +1,494 @@ +/** + * The `brunch_turn` tool: the persona harness's only path into Brunch. + * + * One call sends exactly one visible user utterance to the mounted production + * `ChatAgent` and waits for that submission's exact reply, servicing any + * client-deferred tool calls through the selected host on the way. The tool + * is registered with Pi by the `.pi/extensions/brunch-persona-testing.ts` + * entry and unit-tested against a stubbed Flue client. + */ +import { + type Component, + Markdown, + type MarkdownTheme, +} from "@earendil-works/pi-tui"; +import { + createFlueClient, + type FlueClient, + type FlueConversationPart, + type FlueConversationSnapshot, +} from "@flue/sdk"; +import { Type } from "typebox"; + +import { + CLIENT_TOOL_RESULT_SIGNAL, + isAwaitingClient, +} from "../../conversation/client-tools.ts"; +import { + agentOwnershipHeaders, + flueConversationIdFrom, +} from "../../conversation/identity.ts"; +import { LOCAL_UI_PRINCIPAL } from "../../conversation/payload.ts"; +import { defaultChatOrigin } from "../../http/local-origins.ts"; +import { CHAT_AGENT_ROUTE } from "../../http/routes.ts"; +import { + type BrunchClientToolCall, + type BrunchClientToolHost, + type BrunchToolExecutor, + TOOL_HOST_FLAG, +} from "./client-tool-hosts.ts"; + +type BrunchFlueClient = Pick<FlueClient, "history" | "read" | "send">; +type DynamicToolPart = Extract<FlueConversationPart, { type: "dynamic-tool" }>; + +interface TextContent { + readonly type: "text"; + readonly text: string; +} + +export interface BrunchToolActivity { + readonly sequence: number; + readonly submissionId: string; + readonly toolCallId: string; + readonly toolName: string; + readonly executor: BrunchToolExecutor; + readonly outcome: "output" | "error"; + readonly input: unknown; + readonly output?: unknown; + readonly error?: string; +} + +export interface BrunchTurnDetails { + readonly conversationId: string; + readonly submissionId: string; + readonly submissionIds: readonly string[]; + readonly status: "elicitor-replied"; + readonly elicitorText: string; + readonly toolActivity: readonly BrunchToolActivity[]; +} + +interface BrunchTurnProgressDetails { + readonly conversationId: string; + readonly submissionId: string; + readonly status: "waiting-for-elicitor"; +} + +interface BrunchTurnResult< + Details extends BrunchTurnDetails | BrunchTurnProgressDetails = + | BrunchTurnDetails + | BrunchTurnProgressDetails, +> { + readonly content: readonly TextContent[]; + readonly details: Details; +} + +interface RenderTheme { + bold(text: string): string; + italic(text: string): string; + strikethrough(text: string): string; + underline(text: string): string; + fg(color: string, text: string): string; +} + +interface RenderContext { + readonly isError: boolean; +} + +export interface BrunchTurnTool { + readonly name: "brunch_turn"; + readonly label: string; + readonly description: string; + readonly parameters: ReturnType<typeof Type.Object>; + readonly executionMode: "sequential"; + execute( + toolCallId: string, + parameters: { readonly message: string }, + signal?: AbortSignal, + onUpdate?: ( + result: BrunchTurnResult<BrunchTurnProgressDetails>, + ) => void | Promise<void>, + ): Promise<BrunchTurnResult<BrunchTurnDetails>>; + renderCall( + parameters: { readonly message: string }, + theme: RenderTheme, + ): Component; + renderResult( + result: BrunchTurnResult, + options: { readonly isPartial: boolean }, + theme: RenderTheme, + context: RenderContext, + ): Component; +} + +/** The slice of Pi's extension API the tool needs; Pi itself is not a workspace dependency. */ +export interface BrunchTurnExtensionApi { + registerTool(tool: BrunchTurnTool): void; +} + +export interface RegisterBrunchTurnOptions { + readonly conversationId?: string; + readonly client?: BrunchFlueClient; + readonly resolveClientToolHost?: () => BrunchClientToolHost | undefined; + readonly retainSnapshot?: ( + snapshot: FlueConversationSnapshot, + ) => void | Promise<void>; +} + +const MAX_CLIENT_TOOL_ROUNDS = 20; + +const markdownTheme = (theme: RenderTheme): MarkdownTheme => ({ + heading: (text) => theme.fg("mdHeading", text), + link: (text) => theme.fg("mdLink", text), + linkUrl: (text) => theme.fg("mdLinkUrl", text), + code: (text) => theme.fg("mdCode", text), + codeBlock: (text) => theme.fg("mdCodeBlock", text), + codeBlockBorder: (text) => theme.fg("mdCodeBlockBorder", text), + quote: (text) => theme.fg("mdQuote", text), + quoteBorder: (text) => theme.fg("mdQuoteBorder", text), + hr: (text) => theme.fg("mdHr", text), + listBullet: (text) => theme.fg("mdListBullet", text), + bold: (text) => theme.bold(text), + italic: (text) => theme.italic(text), + strikethrough: (text) => theme.strikethrough(text), + underline: (text) => theme.underline(text), +}); + +const markdownComponent = ( + heading: "User" | "Brunch", + content: string, + theme: RenderTheme, +): Component => + new Markdown(`## ${heading}\n\n${content}`, 0, 0, markdownTheme(theme), { + color: (text) => theme.fg("toolOutput", text), + }); + +const resultText = (result: BrunchTurnResult): string => + result.content.map((content) => content.text).join("\n"); + +const errorMessageFrom = (error: unknown): string => + error instanceof Error ? error.message : String(error); + +export const requireConversationId = (value: string | undefined): string => { + const conversationId = value?.trim(); + if (!conversationId) { + throw new Error( + "brunch_turn requires a non-empty PI_SUBAGENT_NAME; no conversation was created", + ); + } + return conversationId; +}; + +const createClient = (conversationId: string): BrunchFlueClient => { + const identity = { + principalKey: LOCAL_UI_PRINCIPAL, + conversationId, + }; + + return createFlueClient({ + url: `${defaultChatOrigin}/agents/${CHAT_AGENT_ROUTE}/${flueConversationIdFrom(identity)}`, + headers: agentOwnershipHeaders(identity), + }); +}; + +const submissionToolParts = ( + snapshot: FlueConversationSnapshot, + submissionId: string, +): readonly { + readonly submissionId: string; + readonly part: DynamicToolPart; +}[] => { + const answeredBySubmissionId = snapshot.settlements.find( + (settlement) => settlement.submissionId === submissionId, + )?.answeredBySubmissionId; + const responseSubmissionId = answeredBySubmissionId ?? submissionId; + + return snapshot.messages.flatMap((message) => { + if ( + message.purpose !== "assistant" || + message.submissionId !== responseSubmissionId + ) { + return []; + } + + return message.parts.flatMap((part) => + part.type === "dynamic-tool" + ? [{ submissionId: responseSubmissionId, part }] + : [], + ); + }); +}; + +const toolActivityMarkdown = ( + activity: readonly BrunchToolActivity[], +): string => { + if (activity.length === 0) return ""; + + return [ + "", + "### Tool activity", + "", + ...activity.map( + (entry) => + `- \`${entry.toolName}\` (\`${entry.toolCallId}\`) — ${entry.executor}; ${entry.outcome}`, + ), + ].join("\n"); +}; + +export const createBrunchTurnTool = ({ + conversationId: suppliedConversationId, + client: suppliedClient, + resolveClientToolHost = () => undefined, + retainSnapshot, +}: RegisterBrunchTurnOptions = {}): BrunchTurnTool => { + const conversationId = requireConversationId( + suppliedConversationId ?? process.env["PI_SUBAGENT_NAME"], + ); + const client = suppliedClient ?? createClient(conversationId); + let active = false; + let incarnationUid: string | undefined; + let unsafeAfterAdmission = false; + + return { + name: "brunch_turn", + label: "Brunch turn", + description: + "Send exactly one user utterance to the production Brunch elicitor and wait for that submission's exact reply.", + parameters: Type.Object( + { + message: Type.String({ + description: "The next utterance addressed to the Brunch elicitor", + }), + }, + { additionalProperties: false }, + ), + executionMode: "sequential", + + async execute(_toolCallId, parameters, signal, onUpdate) { + if (parameters.message.trim().length === 0) { + throw new Error("brunch_turn message must not be empty"); + } + if (active) { + throw new Error("brunch_turn already has an active submission"); + } + if (unsafeAfterAdmission) { + throw new Error( + "brunch_turn cannot send again after an unsettled or failed admitted submission; inspect canonical Flue history", + ); + } + + active = true; + let admitted = false; + try { + let currentAdmission = await client.send({ + message: { kind: "user", body: parameters.message }, + uid: incarnationUid ?? null, + signal, + }); + admitted = true; + incarnationUid = currentAdmission.uid; + + const completedClientCallIds = new Set<string>(); + const submissionIds: string[] = []; + const toolActivity: BrunchToolActivity[] = []; + let clientToolRounds = 0; + + for (;;) { + submissionIds.push(currentAdmission.submissionId); + await onUpdate?.({ + content: [ + { + type: "text", + text: `Waiting for elicitor submission ${currentAdmission.submissionId}`, + }, + ], + details: { + conversationId, + submissionId: currentAdmission.submissionId, + status: "waiting-for-elicitor", + }, + }); + + const reply = await client.read(currentAdmission, { signal }); + const snapshot = await client.history({ signal }); + // Snapshot retention must finish before this canonical submission is advanced or returned. + // eslint-disable-next-line no-await-in-loop + await retainSnapshot?.(snapshot); + const pendingClientCalls: BrunchClientToolCall[] = []; + + for (const observed of submissionToolParts( + snapshot, + currentAdmission.submissionId, + )) { + const { part } = observed; + if (part.state === "input-available") { + throw new Error( + `Brunch submission ${observed.submissionId} settled with incomplete tool call ${part.toolName} (${part.toolCallId})`, + ); + } + if ( + part.state === "output-available" && + isAwaitingClient(part.output) + ) { + if (!completedClientCallIds.has(part.toolCallId)) { + pendingClientCalls.push({ + submissionId: observed.submissionId, + toolCallId: part.toolCallId, + toolName: part.toolName, + input: part.input, + }); + } + continue; + } + + toolActivity.push({ + sequence: toolActivity.length + 1, + submissionId: observed.submissionId, + toolCallId: part.toolCallId, + toolName: part.toolName, + executor: "server", + outcome: part.state === "output-available" ? "output" : "error", + input: part.input, + ...(part.state === "output-available" + ? { output: part.output } + : { error: part.errorText }), + }); + } + + if (pendingClientCalls.length === 0) { + if (reply.text.trim().length === 0) { + throw new Error( + `Brunch elicitor completed submission ${currentAdmission.submissionId} without assistant text`, + ); + } + + return { + content: [{ type: "text", text: reply.text }], + details: { + conversationId, + submissionId: currentAdmission.submissionId, + submissionIds, + status: "elicitor-replied", + elicitorText: reply.text, + toolActivity, + }, + }; + } + + if (clientToolRounds >= MAX_CLIENT_TOOL_ROUNDS) { + throw new Error( + `brunch_turn reached the ${MAX_CLIENT_TOOL_ROUNDS}-round client-tool limit`, + ); + } + clientToolRounds += 1; + + const host = resolveClientToolHost(); + if (host === undefined) { + throw new Error( + `Brunch requested client tool ${pendingClientCalls[0]?.toolName}; select --${TOOL_HOST_FLAG}=mock or --${TOOL_HOST_FLAG}=real-headless`, + ); + } + + const results: { + readonly toolCallId: string; + readonly toolName: string; + readonly output: unknown; + }[] = []; + + for (const call of pendingClientCalls) { + try { + // Tool calls within one suspension are serviced in canonical order. + const output = await host.execute(call); + completedClientCallIds.add(call.toolCallId); + results.push({ + toolCallId: call.toolCallId, + toolName: call.toolName, + output, + }); + toolActivity.push({ + sequence: toolActivity.length + 1, + submissionId: call.submissionId, + toolCallId: call.toolCallId, + toolName: call.toolName, + executor: host.kind, + outcome: "output", + input: call.input, + output, + }); + } catch (error) { + const message = errorMessageFrom(error); + toolActivity.push({ + sequence: toolActivity.length + 1, + submissionId: call.submissionId, + toolCallId: call.toolCallId, + toolName: call.toolName, + executor: host.kind, + outcome: "error", + input: call.input, + error: message, + }); + throw new Error( + `${host.kind} client-tool host failed ${call.toolName} (${call.toolCallId}): ${message}`, + { cause: error }, + ); + } + } + + currentAdmission = await client.send({ + message: { + kind: "signal", + type: CLIENT_TOOL_RESULT_SIGNAL, + tagName: CLIENT_TOOL_RESULT_SIGNAL, + body: JSON.stringify(results), + attributes: { + toolCallIds: results + .map((result) => result.toolCallId) + .join(","), + }, + }, + uid: incarnationUid, + signal, + }); + incarnationUid = currentAdmission.uid; + } + } catch (error) { + if (admitted) { + unsafeAfterAdmission = true; + } + throw error; + } finally { + active = false; + } + }, + + renderCall(parameters, theme) { + return markdownComponent("User", parameters.message, theme); + }, + + renderResult(result, { isPartial }, theme, context) { + if (context.isError) { + return markdownComponent( + "Brunch", + `Turn failed\n\n${resultText(result)}`, + theme, + ); + } + + if (isPartial || result.details.status === "waiting-for-elicitor") { + return markdownComponent("Brunch", resultText(result), theme); + } + + return markdownComponent( + "Brunch", + `${result.details.elicitorText}${toolActivityMarkdown(result.details.toolActivity)}`, + theme, + ); + }, + }; +}; + +export const registerBrunchTurn = ( + pi: BrunchTurnExtensionApi, + options?: RegisterBrunchTurnOptions, +): void => { + pi.registerTool(createBrunchTurnTool(options)); +}; diff --git a/apps/brunch-agent/src/evaluations/persona/client-tool-hosts.ts b/apps/brunch-agent/src/evaluations/persona/client-tool-hosts.ts new file mode 100644 index 00000000000..828096e780a --- /dev/null +++ b/apps/brunch-agent/src/evaluations/persona/client-tool-hosts.ts @@ -0,0 +1,154 @@ +/** + * Client-tool hosts for the Pi persona harness. + * + * The production `ChatAgent` may defer a tool call to its client, which in the + * product is the browser. When the persona harness is that client, one of + * these hosts services the call so the bridge can resume Brunch with the + * existing `client-tool-result` signal. Selecting a host mounts no tool and + * changes no production composition; it only answers calls the real agent + * emits. + */ +import { readFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { isDeepStrictEqual } from "node:util"; + +import { READ_PETRINAUT_DOC_TOOL_NAME } from "@hashintel/brunch-agent-plugin-sdcpn/flue"; +import { readPetrinautDocToolInputSchema } from "@hashintel/petrinaut-core"; + +import { + createHeadlessPetrinautClient, + isPetrinautConstructionToolName, +} from "../runbook/headless-petrinaut-client.ts"; + +export type BrunchToolExecutor = "server" | "mock" | "real-headless"; + +/** The Pi flag that selects a host, named here so the bridge's failure message can cite it. */ +export const TOOL_HOST_FLAG = "brunch-tool-host"; + +export interface BrunchClientToolCall { + readonly submissionId: string; + readonly toolCallId: string; + readonly toolName: string; + readonly input: unknown; +} + +export interface BrunchClientToolHost { + readonly kind: Exclude<BrunchToolExecutor, "server">; + execute(call: BrunchClientToolCall): Promise<unknown>; + dispose?(): void | Promise<void>; +} + +export interface MockBrunchClientToolCall { + readonly toolName: string; + readonly input: unknown; + readonly output: unknown; +} + +export const createMockClientToolHost = ( + calls: readonly MockBrunchClientToolCall[], +): BrunchClientToolHost => { + let nextCallIndex = 0; + + return { + kind: "mock", + async execute(call) { + const expected = calls[nextCallIndex]; + if (expected === undefined) { + throw new Error( + `Mock client-tool fixture has no call ${nextCallIndex + 1}; received ${call.toolName}`, + ); + } + if ( + expected.toolName !== call.toolName || + !isDeepStrictEqual(expected.input, call.input) + ) { + throw new Error( + `Mock client-tool call ${nextCallIndex + 1} mismatch: expected ${expected.toolName} ${JSON.stringify(expected.input)}, received ${call.toolName} ${JSON.stringify(call.input)}`, + ); + } + + nextCallIndex += 1; + return expected.output; + }, + }; +}; + +const stripImages = (markdown: string): string => + markdown + .replace(/<img\b[^>]*\/?>(?:\s*<\/img>)?/giu, "") + .replace(/!\[[^\]]*\]\([^)]*\)/gu, "") + .replace(/\n{3,}/gu, "\n\n"); + +/** + * The checked-out Petrinaut user guide, read from the repository rather than + * imported: the Brunch server stays independent of the Petrinaut UI package. + */ +const petrinautDocsRoot = new URL( + "../../../../../libs/@hashintel/petrinaut/docs/", + import.meta.url, +); + +export const createRealHeadlessClientToolHost = ( + title: string, +): BrunchClientToolHost => { + const petrinautClient = createHeadlessPetrinautClient(title); + + return { + kind: "real-headless", + async execute(call) { + if (call.toolName === READ_PETRINAUT_DOC_TOOL_NAME) { + const { doc } = readPetrinautDocToolInputSchema.parse(call.input); + const markdown = await readFile( + new URL(`${doc}.md`, petrinautDocsRoot), + "utf8", + ); + return stripImages(markdown); + } + + if (isPetrinautConstructionToolName(call.toolName)) { + const result = await petrinautClient.execute(call); + return result.output; + } + + throw new Error( + `Real headless client-tool host does not support ${call.toolName}`, + ); + }, + dispose: petrinautClient.dispose, + }; +}; + +const isRecord = (value: unknown): value is Record<string, unknown> => + typeof value === "object" && value !== null; + +/** Reads an ordered mock fixture; the path resolves against the working directory. */ +export const readMockCalls = ( + path: string, +): readonly MockBrunchClientToolCall[] => { + const absolutePath = resolve(process.cwd(), path); + const value: unknown = JSON.parse(readFileSync(absolutePath, "utf8")); + if (!isRecord(value) || !Array.isArray(value["calls"])) { + throw new Error( + `Mock client-tool fixture ${absolutePath} must contain a calls array`, + ); + } + + return value["calls"].map((call, index) => { + if ( + !isRecord(call) || + typeof call["toolName"] !== "string" || + !Object.hasOwn(call, "input") || + !Object.hasOwn(call, "output") + ) { + throw new Error( + `Mock client-tool fixture ${absolutePath} call ${index + 1} must contain toolName, input, and output`, + ); + } + return { + toolName: call["toolName"], + input: call["input"], + output: call["output"], + }; + }); +}; diff --git a/apps/brunch-agent/src/evaluations/persona/proof-artifacts.ts b/apps/brunch-agent/src/evaluations/persona/proof-artifacts.ts new file mode 100644 index 00000000000..0c3c71409e7 --- /dev/null +++ b/apps/brunch-agent/src/evaluations/persona/proof-artifacts.ts @@ -0,0 +1,299 @@ +import { createHash, randomUUID } from "node:crypto"; +import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; + +import { + type FlueConversationPart, + type FlueConversationSnapshot, +} from "@flue/sdk"; + +import { isAwaitingClient } from "../../conversation/client-tools.ts"; +import { formatFlueTranscript } from "../../conversation/transcript.ts"; +import { recoverRunbookWorkpiece } from "../runbook/artifacts.ts"; + +interface ProofEventBase { + readonly sequence: number; + readonly turn: number; + readonly messageId: string; +} + +export type ProofTraceEvent = + | (ProofEventBase & { + readonly type: "user"; + readonly text: string; + }) + | (ProofEventBase & { + readonly type: "activate"; + readonly toolCallId: string; + readonly name: string; + readonly outcome: "ok" | "error"; + }) + | (ProofEventBase & { + readonly type: "read"; + readonly toolCallId: string; + readonly path: string; + readonly outcome: "ok" | "error"; + }) + | (ProofEventBase & { + readonly type: "tool"; + readonly toolCallId: string; + readonly name: string; + readonly executor: "client" | "server"; + readonly outcome: "ok" | "error"; + }) + | (ProofEventBase & { + readonly type: "text"; + readonly text: string; + readonly hasWorkpiece: boolean; + }); + +export interface ProofTrace { + readonly conversationId: string; + readonly events: readonly ProofTraceEvent[]; + readonly firstWorkpiece?: { + readonly messageId: string; + readonly sequence: number; + }; +} + +type DynamicToolPart = Extract<FlueConversationPart, { type: "dynamic-tool" }>; +type UnsequencedProofTraceEvent = ProofTraceEvent extends infer Event + ? Event extends ProofTraceEvent + ? Omit<Event, "sequence"> + : never + : never; + +const stringInputField = ( + part: DynamicToolPart, + field: string, +): string | undefined => { + if (typeof part.input !== "object" || part.input === null) return undefined; + if (!(field in part.input)) return undefined; + const value = part.input[field as keyof typeof part.input]; + return typeof value === "string" ? value : undefined; +}; + +const traceResourcePath = (path: string): string => { + let decoded: string; + try { + decoded = decodeURIComponent(path); + } catch { + return path; + } + const match = /\/packaged-skills\/skill:([^/:]+):[^/]+\/(.+)$/u.exec(decoded); + return match?.[1] !== undefined && match[2] !== undefined + ? `${match[1]}/${match[2]}` + : path; +}; + +const hasRunbookWorkpiece = (text: string): boolean => + /```runbook-ir(?:\s|$)/u.test(text); + +const toolOutcome = (part: DynamicToolPart): "ok" | "error" => + part.state === "output-available" ? "ok" : "error"; + +export const deriveProofTrace = ( + snapshot: FlueConversationSnapshot, +): ProofTrace => { + const events: ProofTraceEvent[] = []; + let turn = 0; + let firstWorkpiece: ProofTrace["firstWorkpiece"]; + + const append = (event: UnsequencedProofTraceEvent): ProofTraceEvent => { + const sequenced = { + ...event, + sequence: events.length + 1, + } as ProofTraceEvent; + events.push(sequenced); + return sequenced; + }; + + for (const message of snapshot.messages) { + if (message.display !== "visible") continue; + + if (message.purpose === "user") { + turn += 1; + append({ + type: "user", + turn, + messageId: message.id, + text: message.parts + .flatMap((part) => (part.type === "text" ? [part.text] : [])) + .join("\n"), + }); + continue; + } + if (message.purpose !== "assistant") continue; + + for (const part of message.parts) { + if (part.type === "text") { + const event = append({ + type: "text", + turn, + messageId: message.id, + text: part.text, + hasWorkpiece: hasRunbookWorkpiece(part.text), + }); + if (event.type === "text" && event.hasWorkpiece && !firstWorkpiece) { + firstWorkpiece = { + messageId: message.id, + sequence: event.sequence, + }; + } + continue; + } + if (part.type !== "dynamic-tool") continue; + + if (part.toolName === "activate_skill") { + append({ + type: "activate", + turn, + messageId: message.id, + toolCallId: part.toolCallId, + name: stringInputField(part, "name") ?? "<missing>", + outcome: toolOutcome(part), + }); + continue; + } + if (part.toolName === "read_skill_resource") { + const path = stringInputField(part, "path"); + append({ + type: "read", + turn, + messageId: message.id, + toolCallId: part.toolCallId, + path: path === undefined ? "<missing>" : traceResourcePath(path), + outcome: toolOutcome(part), + }); + continue; + } + + append({ + type: "tool", + turn, + messageId: message.id, + toolCallId: part.toolCallId, + name: part.toolName, + executor: + part.state === "output-available" && isAwaitingClient(part.output) + ? "client" + : "server", + outcome: toolOutcome(part), + }); + } + } + + return { + conversationId: snapshot.conversationId, + events, + ...(firstWorkpiece === undefined ? {} : { firstWorkpiece }), + }; +}; + +const traceEventMarkdown = (event: ProofTraceEvent): string => { + const prefix = `${event.sequence}. turn ${event.turn}: `; + switch (event.type) { + case "user": + return `${prefix}\`user\` — message \`${event.messageId}\``; + case "activate": + return `${prefix}\`activate(${event.name}, ${event.outcome})\` — call \`${event.toolCallId}\``; + case "read": + return `${prefix}\`read(${event.path}, ${event.outcome})\` — call \`${event.toolCallId}\``; + case "tool": + return `${prefix}\`tool(${event.name}, ${event.executor}, ${event.outcome})\` — call \`${event.toolCallId}\``; + case "text": + return `${prefix}\`text(hasWorkpiece=${String(event.hasWorkpiece)})\` — message \`${event.messageId}\``; + } +}; + +export const formatProofTrace = (trace: ProofTrace): string => + [ + "# Canonical proof trace", + "", + `Conversation: \`${trace.conversationId}\``, + "", + ...trace.events.map(traceEventMarkdown), + "", + ].join("\n"); + +const atomicWrite = async (path: string, content: string): Promise<void> => { + const temporaryPath = join( + dirname(path), + `.${basename(path)}.${randomUUID()}.tmp`, + ); + await writeFile(temporaryPath, content, "utf8"); + await rename(temporaryPath, path); +}; + +export interface ProofArtifactManifest { + readonly algorithm: "sha256"; + readonly files: readonly { + readonly path: string; + readonly sha256: string; + }[]; +} + +export const refreshProofManifest = async ( + directory: string, +): Promise<ProofArtifactManifest> => { + const names = (await readdir(directory, { withFileTypes: true })) + .filter( + (entry) => + entry.isFile() && + entry.name !== "manifest.json" && + !entry.name.endsWith(".tmp"), + ) + .map((entry) => entry.name) + .sort((left, right) => left.localeCompare(right)); + const files = await Promise.all( + names.map(async (name) => ({ + path: name, + sha256: createHash("sha256") + .update(await readFile(join(directory, name))) + .digest("hex"), + })), + ); + const manifest: ProofArtifactManifest = { algorithm: "sha256", files }; + await atomicWrite( + join(directory, "manifest.json"), + `${JSON.stringify(manifest, null, 2)}\n`, + ); + return manifest; +}; + +export const writeProofArtifacts = async ( + directory: string, + snapshot: FlueConversationSnapshot, +): Promise<void> => { + await mkdir(directory, { recursive: true }); + const trace = deriveProofTrace(snapshot); + const workpiece = recoverRunbookWorkpiece(snapshot); + await Promise.all([ + atomicWrite( + join(directory, "snapshot.json"), + `${JSON.stringify(snapshot, null, 2)}\n`, + ), + atomicWrite( + join(directory, "transcript.md"), + `${formatFlueTranscript(snapshot).trimEnd()}\n`, + ), + atomicWrite( + join(directory, "trace.json"), + `${JSON.stringify(trace, null, 2)}\n`, + ), + atomicWrite(join(directory, "trace.md"), formatProofTrace(trace)), + ...(workpiece === undefined + ? [] + : [ + atomicWrite( + join(directory, "workpiece.md"), + `${workpiece.content}\n`, + ), + atomicWrite( + join(directory, "workpiece-source.json"), + `${JSON.stringify(workpiece, null, 2)}\n`, + ), + ]), + ]); + await refreshProofManifest(directory); +}; diff --git a/apps/brunch-agent/src/evaluations/persona/refresh-proof-manifest.ts b/apps/brunch-agent/src/evaluations/persona/refresh-proof-manifest.ts new file mode 100644 index 00000000000..a566e785ba5 --- /dev/null +++ b/apps/brunch-agent/src/evaluations/persona/refresh-proof-manifest.ts @@ -0,0 +1,20 @@ +import { resolve } from "node:path"; + +import { refreshProofManifest } from "./proof-artifacts.ts"; + +const argumentsAfterScript = process.argv.slice(2); +const [directory, ...unexpected] = + argumentsAfterScript[0] === "--" + ? argumentsAfterScript.slice(1) + : argumentsAfterScript; +if (directory === undefined || unexpected.length > 0) { + throw new Error( + "usage: yarn workspace @apps/brunch-agent proof:manifest -- <attempt-directory>", + ); +} + +const resolvedDirectory = resolve(directory); +const manifest = await refreshProofManifest(resolvedDirectory); +process.stdout.write( + `PROOF_MANIFEST ${JSON.stringify({ directory: resolvedDirectory, files: manifest.files.length })}\n`, +); diff --git a/apps/brunch-agent/src/evaluations/runbook/artifacts.ts b/apps/brunch-agent/src/evaluations/runbook/artifacts.ts new file mode 100644 index 00000000000..05a50b46847 --- /dev/null +++ b/apps/brunch-agent/src/evaluations/runbook/artifacts.ts @@ -0,0 +1,235 @@ +/** Recover Mission 3 workpieces from a Flue `history()` snapshot. */ + +import { basename } from "node:path"; + +import { sha256 } from "./campaign-integrity.ts"; + +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(); +}; + +export const recoverRunbookIr = ( + snapshot: FlueConversationSnapshot, +): string | undefined => recoverRunbookWorkpiece(snapshot)?.content; + +export interface RecoveredRunbookWorkpiece { + readonly content: string; + readonly sha256: string; + readonly sourceMessageId: string; + readonly sourceMessageSha256: string; +} + +export const recoverRunbookWorkpiece = ( + snapshot: FlueConversationSnapshot, +): RecoveredRunbookWorkpiece | undefined => { + let recovered: RecoveredRunbookWorkpiece | undefined; + for (const message of snapshot.messages) { + if (message.purpose !== "assistant") continue; + const text = message.parts + .filter( + (part): part is Extract<FlueConversationPart, { type: "text" }> => + part.type === "text", + ) + .map((part) => part.text) + .join("\n"); + const content = latestRunbookIrBlock(text); + if (content === undefined) continue; + recovered = { + content, + sha256: sha256(content), + sourceMessageId: message.id, + sourceMessageSha256: sha256(JSON.stringify(message)), + }; + } + return recovered; +}; + +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]; + }), + ); + +export interface OrdinaryElicitationViolation { + readonly code: + | "capture-tool-use" + | "construction-resource-read" + | "construction-tool-use" + | "late-required-resource" + | "missing-required-resource" + | "missing-workpiece" + | "multiple-questions" + | "unexpected-resource-read" + | "unexpected-tool-use"; + readonly detail: string; +} + +const ORDINARY_TOOL_NAMES = new Set(["activate_skill", "read_skill_resource"]); +const CONSTRUCTION_TOOL_NAMES = new Set([ + "getLatestNetDefinition", + "addType", + "addParameter", + "addPlace", + "addTransition", + "addArc", +]); +const CAPTURE_TOOL_NAMES = new Set(["brunch_ask", "brunch_sweep"]); +const ORDINARY_RESOURCE_NAMES = new Set(["profile.md", "workpiece.md"]); +const CONSTRUCTION_RESOURCE_NAMES = new Set([ + "pn-construction.md", + "checks.md", +]); + +const interactiveTextFrom = (text: string): string => + text.replace(/```runbook-ir\s*\n[\s\S]*?```/gu, ""); + +export const ordinaryElicitationViolationsFrom = ( + snapshot: FlueConversationSnapshot, + options: { readonly hasWorkpiece: boolean }, +): readonly OrdinaryElicitationViolation[] => { + const violations: OrdinaryElicitationViolation[] = []; + const successfulResourcePositions = new Map<string, number>(); + let firstQuestionPosition: number | undefined; + let firstWorkpiecePosition: number | undefined; + let position = 0; + + for (const message of snapshot.messages) { + if (message.purpose === "assistant") { + const questionCount = message.parts.reduce((count, part) => { + if (part.type !== "text") return count; + const interactiveText = interactiveTextFrom(part.text); + return count + (interactiveText.match(/\?/gu)?.length ?? 0); + }, 0); + if (questionCount > 1) { + violations.push({ + code: "multiple-questions", + detail: `${message.id}: ${questionCount} question marks`, + }); + } + } + for (const part of message.parts) { + position += 1; + if (message.purpose !== "assistant") continue; + if (part.type === "text") { + if ( + firstQuestionPosition === undefined && + interactiveTextFrom(part.text).includes("?") + ) { + firstQuestionPosition = position; + } + if ( + firstWorkpiecePosition === undefined && + part.text.includes(`\`\`\`${RUNBOOK_IR_FENCE}`) + ) { + firstWorkpiecePosition = position; + } + continue; + } + if ( + part.type !== "dynamic-tool" || + part.toolName !== "read_skill_resource" || + part.state !== "output-available" || + typeof part.input !== "object" || + part.input === null || + !("path" in part.input) || + typeof part.input.path !== "string" + ) { + continue; + } + const resourceName = basename(part.input.path); + if (!successfulResourcePositions.has(resourceName)) { + successfulResourcePositions.set(resourceName, position); + } + } + } + + for (const toolName of interviewerToolNamesFrom(snapshot)) { + if (ORDINARY_TOOL_NAMES.has(toolName)) continue; + violations.push({ + code: CONSTRUCTION_TOOL_NAMES.has(toolName) + ? "construction-tool-use" + : CAPTURE_TOOL_NAMES.has(toolName) + ? "capture-tool-use" + : "unexpected-tool-use", + detail: toolName, + }); + } + for (const path of skillResourcePathsFrom(snapshot)) { + const name = basename(path); + if (ORDINARY_RESOURCE_NAMES.has(name)) continue; + violations.push({ + code: CONSTRUCTION_RESOURCE_NAMES.has(name) + ? "construction-resource-read" + : "unexpected-resource-read", + detail: path, + }); + } + + const requireResourceBefore = ( + resourceName: string, + boundary: number | undefined, + boundaryDescription: string, + ): void => { + const resourcePosition = successfulResourcePositions.get(resourceName); + if (resourcePosition === undefined) { + violations.push({ + code: "missing-required-resource", + detail: resourceName, + }); + } else if (boundary !== undefined && resourcePosition > boundary) { + violations.push({ + code: "late-required-resource", + detail: `${resourceName}: after ${boundaryDescription}`, + }); + } + }; + + requireResourceBefore("profile.md", firstQuestionPosition, "first question"); + if (options.hasWorkpiece) { + requireResourceBefore( + "workpiece.md", + firstWorkpiecePosition, + "first workpiece", + ); + } else { + violations.push({ + code: "missing-workpiece", + detail: "No recoverable runbook-ir workpiece was emitted.", + }); + } + return violations; +}; diff --git a/apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts b/apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts new file mode 100644 index 00000000000..58329694c85 --- /dev/null +++ b/apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts @@ -0,0 +1,127 @@ +import { createHash } from "node:crypto"; +import { readFile, readdir, realpath } from "node:fs/promises"; +import { + basename, + dirname, + isAbsolute, + join, + relative, + resolve, + sep, +} from "node:path"; +import { fileURLToPath } from "node:url"; + +export const sha256 = (content: string | Buffer): string => + createHash("sha256").update(content).digest("hex"); + +const isMissingPathError = (error: unknown): boolean => + error instanceof Error && "code" in error && error.code === "ENOENT"; + +/** Resolve aliases and symlinks even when the final output path does not exist yet. */ +export const canonicalPath = async (path: string): Promise<string> => { + const resolveFromExistingAncestor = async ( + candidate: string, + missingSegments: readonly string[], + ): Promise<string> => { + try { + return resolve(await realpath(candidate), ...missingSegments); + } catch (error) { + if (!isMissingPathError(error)) throw error; + const parent = dirname(candidate); + if (parent === candidate) throw error; + return resolveFromExistingAncestor(parent, [ + basename(candidate), + ...missingSegments, + ]); + } + }; + + return resolveFromExistingAncestor(resolve(path), []); +}; + +export const pathIsWithin = (candidate: string, directory: string): boolean => { + const remainder = relative(directory, candidate); + return ( + remainder === "" || + (!remainder.startsWith(`..${sep}`) && + remainder !== ".." && + !isAbsolute(remainder)) + ); +}; + +export const rejectImmutableBaselineOutput = async ( + outputPath: string, + immutableBaselinePath: string, +): Promise<string> => { + const [canonicalOutput, canonicalBaseline] = await Promise.all([ + canonicalPath(outputPath), + canonicalPath(immutableBaselinePath), + ]); + if (pathIsWithin(canonicalOutput, canonicalBaseline)) { + throw new Error( + "Output path is inside the immutable vestera-prospective-baseline-v1 campaign.", + ); + } + return canonicalOutput; +}; + +const filesystemPathFrom = (specifier: string): string => + specifier.startsWith("file:") ? fileURLToPath(specifier) : specifier; + +export const assertApprovedHermeticModelModules = async ( + repositoryRootPath: string, + modules: { + readonly expert: string; + readonly interviewer: string; + }, +): Promise<void> => { + const approved = { + expert: join( + repositoryRootPath, + "apps/brunch-agent/test/runbook-elicitation-faux-expert.ts", + ), + interviewer: join( + repositoryRootPath, + "apps/brunch-agent/test/runbook-elicitation-faux-provider.ts", + ), + }; + const [expert, interviewer, approvedExpert, approvedInterviewer] = + await Promise.all([ + canonicalPath(filesystemPathFrom(modules.expert)), + canonicalPath(filesystemPathFrom(modules.interviewer)), + canonicalPath(approved.expert), + canonicalPath(approved.interviewer), + ]); + if (expert !== approvedExpert || interviewer !== approvedInterviewer) { + throw new Error( + "Hermetic model overrides must use the approved checked-in faux fixtures.", + ); + } +}; + +export interface BuiltArtifactManifestEntry { + readonly path: string; + readonly sha256: string; +} + +export const builtServerArtifactManifest = async ( + repositoryRootPath: string, +): Promise<readonly BuiltArtifactManifestEntry[]> => { + const distDirectory = join(repositoryRootPath, "apps/brunch-agent/dist"); + const entries = (await readdir(distDirectory, { withFileTypes: true })) + .filter((entry) => entry.isFile() && entry.name.endsWith(".mjs")) + .map((entry) => entry.name) + .sort((left, right) => left.localeCompare(right)); + if (entries.length === 0) { + throw new Error("The built server dist contains no .mjs artifacts."); + } + return Promise.all( + entries.map(async (name) => { + const absolutePath = join(distDirectory, name); + return { + path: relative(repositoryRootPath, absolutePath).split(sep).join("/"), + sha256: sha256(await readFile(absolutePath)), + }; + }), + ); +}; diff --git a/apps/brunch-agent/src/runbook-headless-run.ts b/apps/brunch-agent/src/evaluations/runbook/construction-run.ts similarity index 94% rename from apps/brunch-agent/src/runbook-headless-run.ts rename to apps/brunch-agent/src/evaluations/runbook/construction-run.ts index 233a4571539..eeca24b4f76 100644 --- a/apps/brunch-agent/src/runbook-headless-run.ts +++ b/apps/brunch-agent/src/evaluations/runbook/construction-run.ts @@ -4,7 +4,7 @@ * Petrinaut client and never emits free-form net JSON. * * Build first, then run: - * yarn turbo run build --filter @apps/brunch-agent + * yarn exec turbo run build --filter @apps/brunch-agent * yarn workspace @apps/brunch-agent runbook:headless */ @@ -16,11 +16,21 @@ 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 { VALIDATED_CONSTRUCTION_MODE } from "@hashintel/brunch-agent-plugin-sdcpn/flue"; + +import { + CLIENT_TOOL_RESULT_SIGNAL, + isAwaitingClient, +} from "../../conversation/client-tools.ts"; import { agentOwnershipHeaders, flueConversationIdFrom, -} from "./conversation-identity.ts"; +} from "../../conversation/identity.ts"; +import { CHAT_AGENT_ROUTE } from "../../http/routes.ts"; +import { + interviewerToolNamesFrom, + skillResourcePathsFrom, +} from "./artifacts.ts"; import { createHeadlessPetrinautClient, isPetrinautConstructionToolName, @@ -28,12 +38,6 @@ import { 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"; @@ -46,13 +50,13 @@ const MAX_CLIENT_ROUNDS = Number( 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", + "../../../../../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/", + "../../../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-runbook-headless/", import.meta.url, ), ); @@ -321,9 +325,9 @@ try { totalTokens: totalFrom(turnUsage.map((turn) => turn.totalTokens)), cost: totalFrom(turnUsage.map((turn) => turn.cost)), }, - transcript: (await import("./flue-transcript.ts")).formatFlueTranscript( - snapshot, - ), + transcript: ( + await import("../../conversation/transcript.ts") + ).formatFlueTranscript(snapshot), }; const artifactBase = `${outputDirectory}/${conversationId}`; diff --git a/apps/brunch-agent/src/headless-petrinaut-client.ts b/apps/brunch-agent/src/evaluations/runbook/headless-petrinaut-client.ts similarity index 98% rename from apps/brunch-agent/src/headless-petrinaut-client.ts rename to apps/brunch-agent/src/evaluations/runbook/headless-petrinaut-client.ts index 6bcb0ab6d55..54cce1b64ee 100644 --- a/apps/brunch-agent/src/headless-petrinaut-client.ts +++ b/apps/brunch-agent/src/evaluations/runbook/headless-petrinaut-client.ts @@ -1,3 +1,7 @@ +import { + PETRINAUT_CONSTRUCTION_TOOL_NAMES, + type PetrinautConstructionToolName, +} from "@hashintel/brunch-agent-plugin-sdcpn/flue"; import { createJsonDocHandle, createPetrinaut, @@ -8,11 +12,6 @@ import { 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 { diff --git a/apps/brunch-agent/src/load-built-application.ts b/apps/brunch-agent/src/evaluations/runbook/load-built-application.ts similarity index 90% rename from apps/brunch-agent/src/load-built-application.ts rename to apps/brunch-agent/src/evaluations/runbook/load-built-application.ts index d1681374b34..8d780549737 100644 --- a/apps/brunch-agent/src/load-built-application.ts +++ b/apps/brunch-agent/src/evaluations/runbook/load-built-application.ts @@ -13,7 +13,8 @@ type BuiltApplicationModule = { */ export const loadBuiltBrunchApplication = async (): Promise<BuiltBrunchApplication> => { - const applicationUrl = new URL("../dist/app.mjs", import.meta.url).href; + const applicationUrl = new URL("../../../dist/app.mjs", import.meta.url) + .href; const builtModule = (await import( applicationUrl )) as BuiltApplicationModule; diff --git a/apps/brunch-agent/src/assets.ts b/apps/brunch-agent/src/http/assets.ts similarity index 100% rename from apps/brunch-agent/src/assets.ts rename to apps/brunch-agent/src/http/assets.ts diff --git a/apps/brunch-agent/src/local-dev-origins.ts b/apps/brunch-agent/src/http/local-origins.ts similarity index 100% rename from apps/brunch-agent/src/local-dev-origins.ts rename to apps/brunch-agent/src/http/local-origins.ts diff --git a/apps/brunch-agent/src/agent-ownership.ts b/apps/brunch-agent/src/http/ownership.ts similarity index 88% rename from apps/brunch-agent/src/agent-ownership.ts rename to apps/brunch-agent/src/http/ownership.ts index 7ede5572276..277d83f2227 100644 --- a/apps/brunch-agent/src/agent-ownership.ts +++ b/apps/brunch-agent/src/http/ownership.ts @@ -2,8 +2,8 @@ import { BRUNCH_PRINCIPAL_HEADER } from "@hashintel/brunch-agent-transport-aisdk/headers"; -import { ownsFlueInstance } from "./conversation-identity.ts"; -import { BRUNCH_CONVERSATION_HEADER } from "./conversation-payload.ts"; +import { ownsFlueInstance } from "../conversation/identity.ts"; +import { BRUNCH_CONVERSATION_HEADER } from "../conversation/payload.ts"; import type { MiddlewareHandler } from "hono"; diff --git a/apps/brunch-agent/src/petrinaut-chat.ts b/apps/brunch-agent/src/http/petrinaut-chat.ts similarity index 65% rename from apps/brunch-agent/src/petrinaut-chat.ts rename to apps/brunch-agent/src/http/petrinaut-chat.ts index 6ea6b1865d4..e76ba0fbbdd 100644 --- a/apps/brunch-agent/src/petrinaut-chat.ts +++ b/apps/brunch-agent/src/http/petrinaut-chat.ts @@ -11,15 +11,18 @@ import { type TransportInspectionEvent, } from "@hashintel/brunch-agent-transport-aisdk"; -import { ChatAgent } from "./agents/chat-agent.ts"; -import { clientToolNames, CLIENT_TOOL_RESULT_SIGNAL } from "./client-tool.ts"; +import { ChatAgent } from "../agents/chat-agent/agent.ts"; +import { + clientToolNames, + CLIENT_TOOL_RESULT_SIGNAL, +} from "../conversation/client-tools.ts"; import { agentOwnershipHeaders, flueConversationIdFrom, -} from "./conversation-identity.ts"; -import { snapshotToUiMessages } from "./flue-transcript.ts"; -import { createFlueUiStream } from "./flue-ui-stream.ts"; -import { defaultPanelOrigins } from "./local-dev-origins.ts"; +} from "../conversation/identity.ts"; +import { snapshotToUiMessages } from "../conversation/transcript.ts"; +import { createFlueUiStream } from "../conversation/ui-stream.ts"; +import { defaultPanelOrigins } from "./local-origins.ts"; import { CHAT_AGENT_ROUTE } from "./routes.ts"; import type { UIMessageChunk } from "ai"; @@ -31,15 +34,13 @@ const inspect = } : undefined; -const appTransport: typeof fetch = async (input, init) => { - const { default: app } = await import("./app.ts"); - return app.fetch(input instanceof Request ? input : new Request(input, init)); -}; - const conversationUrl = (instanceId: string): string => `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`; -const historyClient = (identity: ConversationIdentity) => +const historyClient = ( + identity: ConversationIdentity, + appTransport: typeof fetch, +) => createFlueClient({ url: conversationUrl(flueConversationIdFrom(identity)), fetch: appTransport, @@ -94,27 +95,29 @@ const runClientToolResume = ( write, ); -const loadHistory = async ( - identity: ConversationIdentity, -): Promise<{ readonly messages: readonly unknown[] }> => { - let snapshot: FlueConversationSnapshot; - try { - snapshot = await historyClient(identity).history(); - } catch { - return { messages: [] }; - } - return { messages: snapshotToUiMessages(snapshot) }; -}; +export const createPetrinautChatHandler = (appTransport: typeof fetch) => { + const loadHistory = async ( + identity: ConversationIdentity, + ): Promise<{ readonly messages: readonly unknown[] }> => { + let snapshot: FlueConversationSnapshot; + try { + snapshot = await historyClient(identity, appTransport).history(); + } catch { + return { messages: [] }; + } + return { messages: snapshotToUiMessages(snapshot) }; + }; -export const petrinautChatHandler = createAiSdkChatHandler({ - allowedOrigins: ( - process.env.BRUNCH_PETRINAUT_ORIGINS ?? defaultPanelOrigins.join(",") - ) - .split(",") - .map((origin) => origin.trim()) - .filter((origin) => origin.length > 0), - inspect, - runTurn: runUserTurn, - resumeTurn: runClientToolResume, - loadHistory, -}); + return createAiSdkChatHandler({ + allowedOrigins: ( + process.env.BRUNCH_PETRINAUT_ORIGINS ?? defaultPanelOrigins.join(",") + ) + .split(",") + .map((origin) => origin.trim()) + .filter((origin) => origin.length > 0), + inspect, + runTurn: runUserTurn, + resumeTurn: runClientToolResume, + loadHistory, + }); +}; diff --git a/apps/brunch-agent/src/routes.ts b/apps/brunch-agent/src/http/routes.ts similarity index 100% rename from apps/brunch-agent/src/routes.ts rename to apps/brunch-agent/src/http/routes.ts diff --git a/apps/brunch-agent/src/runbook-artifacts.ts b/apps/brunch-agent/src/runbook-artifacts.ts deleted file mode 100644 index e29fe7b14d4..00000000000 --- a/apps/brunch-agent/src/runbook-artifacts.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** 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<FlueConversationPart, { type: "text" }> => - 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 deleted file mode 100644 index 34aed31a349..00000000000 --- a/apps/brunch-agent/src/runbook-elicitation-run.ts +++ /dev/null @@ -1,431 +0,0 @@ -/** - * 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<string, string> = {}; -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<Anthropic.Usage>; - }>; - }; -} - -const defaultExportFrom = async <Value>(specifier: string): Promise<Value> => { - 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<Provider>(interviewerProviderModule)); -} - -const AnthropicClient = (await import("@anthropic-ai/sdk")).default; -const expertClient: ExpertClient = expertClientModule - ? await defaultExportFrom<ExpertClient>(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<string> => { - 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<void> => { - 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/skills/.gitkeep b/apps/brunch-agent/src/skills/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md b/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md deleted file mode 100644 index 7cfe680578c..00000000000 --- a/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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 deleted file mode 100644 index 9ef86854436..00000000000 --- a/apps/brunch-agent/src/skills/sdcpn-modelling/checks.md +++ /dev/null @@ -1,52 +0,0 @@ -# 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 deleted file mode 100644 index 3325eae4e4c..00000000000 --- a/apps/brunch-agent/src/skills/sdcpn-modelling/elicitation.md +++ /dev/null @@ -1,204 +0,0 @@ -# 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 deleted file mode 100644 index 4a5ec6be938..00000000000 --- a/apps/brunch-agent/src/skills/sdcpn-modelling/ir-template.md +++ /dev/null @@ -1,75 +0,0 @@ -# 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: - -### <name> - -#### 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 deleted file mode 100644 index a983ebb0291..00000000000 --- a/apps/brunch-agent/src/skills/sdcpn-modelling/pn-construction.md +++ /dev/null @@ -1,93 +0,0 @@ -# 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/subagents/.gitkeep b/apps/brunch-agent/src/subagents/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/brunch-agent/src/ui/chat.tsx b/apps/brunch-agent/src/ui/chat.tsx index ca4687efbf6..e3581f59903 100644 --- a/apps/brunch-agent/src/ui/chat.tsx +++ b/apps/brunch-agent/src/ui/chat.tsx @@ -6,13 +6,61 @@ import { } from "@flue/sdk"; import { useEffect, useMemo, useState, type FormEvent } from "react"; -import { flueConversationIdWeb } from "../conversation-identity-web.ts"; +import { flueConversationIdWeb } from "../conversation/identity-web.ts"; import { BRUNCH_CONVERSATION_HEADER, BRUNCH_PRINCIPAL_HEADER, LOCAL_UI_PRINCIPAL, -} from "../conversation-payload.ts"; -import { CHAT_AGENT_ROUTE } from "../routes.ts"; +} from "../conversation/payload.ts"; +import { CHAT_AGENT_ROUTE } from "../http/routes.ts"; + +type ChatConfiguration = + | { + readonly mode: "writable"; + readonly principalKey: typeof LOCAL_UI_PRINCIPAL; + readonly conversationId: string; + } + | { + readonly mode: "observe"; + readonly principalKey: typeof LOCAL_UI_PRINCIPAL; + readonly conversationId: string; + } + | { + readonly mode: "observer-error"; + readonly message: string; + }; + +const chatConfiguration = (): ChatConfiguration => { + const parameters = new URLSearchParams(window.location.search); + if (parameters.get("mode") !== "observe") { + return { + mode: "writable", + principalKey: LOCAL_UI_PRINCIPAL, + conversationId: crypto.randomUUID(), + }; + } + + const principalKey = parameters.get("principal"); + const conversationId = parameters.get("id")?.trim(); + if (principalKey !== LOCAL_UI_PRINCIPAL) { + return { + mode: "observer-error", + message: `Observer principal must be "${LOCAL_UI_PRINCIPAL}".`, + }; + } + if (!conversationId) { + return { + mode: "observer-error", + message: "Observer conversation id is required.", + }; + } + + return { + mode: "observe", + principalKey: LOCAL_UI_PRINCIPAL, + conversationId, + }; +}; function VisibleMessage({ message }: { message: FlueConversationMessage }) { if ( @@ -42,7 +90,13 @@ function VisibleMessage({ message }: { message: FlueConversationMessage }) { ); } -function ChatConversation({ client }: { client: FlueClient }) { +function ChatConversation({ + client, + readOnly, +}: { + client: FlueClient; + readOnly: boolean; +}) { const [input, setInput] = useState(""); const agent = useFlueAgent({ client }); @@ -63,10 +117,16 @@ function ChatConversation({ client }: { client: FlueClient }) { <main className="shell"> <header className="masthead"> <div> - <p className="eyebrow">Brunch / Flue chat</p> - <h1>Plain Flue conversation</h1> + <p className="eyebrow"> + {readOnly ? "Brunch / Flue observer" : "Brunch / Flue chat"} + </p> + <h1> + {readOnly ? "Canonical conversation" : "Plain Flue conversation"} + </h1> </div> - <span className={`status status--${agent.status}`}>{agent.status}</span> + <span className={`status status--${agent.status}`}> + {readOnly ? `read-only · ${agent.status}` : agent.status} + </span> </header> <section className="transcript" aria-live="polite" aria-busy={busy}> @@ -76,63 +136,102 @@ function ChatConversation({ client }: { client: FlueClient }) { {agent.error ? <p className="error">{agent.error.message}</p> : null} </section> - <form className="composer" onSubmit={submit}> - <label htmlFor="reply">Your message</label> - <div className="composer__row"> - <textarea - id="reply" - value={input} - onChange={(event) => setInput(event.target.value)} - placeholder="Ask something." - rows={3} - /> - <button type="submit" disabled={busy || input.trim().length === 0}> - Send - </button> - </div> - </form> + {readOnly ? null : ( + <form className="composer" onSubmit={submit}> + <label htmlFor="reply">Your message</label> + <div className="composer__row"> + <textarea + id="reply" + value={input} + onChange={(event) => setInput(event.target.value)} + placeholder="Ask something." + rows={3} + /> + <button type="submit" disabled={busy || input.trim().length === 0}> + Send + </button> + </div> + </form> + )} </main> ); } export function Chat() { - const conversationId = useMemo(() => crypto.randomUUID(), []); + const configuration = useMemo(chatConfiguration, []); const [client, setClient] = useState<FlueClient>(); useEffect(() => { + if (configuration.mode === "observer-error") return; + let cancelled = false; - void flueConversationIdWeb(LOCAL_UI_PRINCIPAL, conversationId).then( - (instanceId) => { - if (cancelled) return; - setClient( - createFlueClient({ - url: `/agents/${CHAT_AGENT_ROUTE}/${instanceId}`, - headers: { - [BRUNCH_PRINCIPAL_HEADER]: LOCAL_UI_PRINCIPAL, - [BRUNCH_CONVERSATION_HEADER]: conversationId, - }, - }), - ); - }, - ); + void flueConversationIdWeb( + configuration.principalKey, + configuration.conversationId, + ).then((instanceId) => { + if (cancelled) return; + setClient( + createFlueClient({ + url: `/agents/${CHAT_AGENT_ROUTE}/${instanceId}`, + headers: { + [BRUNCH_PRINCIPAL_HEADER]: configuration.principalKey, + [BRUNCH_CONVERSATION_HEADER]: configuration.conversationId, + }, + }), + ); + }); return () => { cancelled = true; }; - }, [conversationId]); + }, [configuration]); + + if (configuration.mode === "observer-error") { + return ( + <main className="shell"> + <header className="masthead"> + <div> + <p className="eyebrow">Brunch / Flue observer</p> + <h1>Observer unavailable</h1> + </div> + <span className="status status--error">read-only · error</span> + </header> + <section className="transcript"> + <p className="error">{configuration.message}</p> + </section> + </main> + ); + } if (client === undefined) { return ( <main className="shell"> <header className="masthead"> <div> - <p className="eyebrow">Brunch / Flue chat</p> - <h1>Plain Flue conversation</h1> + <p className="eyebrow"> + {configuration.mode === "observe" + ? "Brunch / Flue observer" + : "Brunch / Flue chat"} + </p> + <h1> + {configuration.mode === "observe" + ? "Canonical conversation" + : "Plain Flue conversation"} + </h1> </div> - <span className="status status--connecting">connecting</span> + <span className="status status--connecting"> + {configuration.mode === "observe" + ? "read-only · connecting" + : "connecting"} + </span> </header> </main> ); } - return <ChatConversation client={client} />; + return ( + <ChatConversation + client={client} + readOnly={configuration.mode === "observe"} + /> + ); } diff --git a/apps/brunch-agent/test/agent-ownership.test.ts b/apps/brunch-agent/test/agent-ownership.test.ts index 48a5e43f2b9..9a2f19921ba 100644 --- a/apps/brunch-agent/test/agent-ownership.test.ts +++ b/apps/brunch-agent/test/agent-ownership.test.ts @@ -7,13 +7,13 @@ import { Hono } from "hono"; import { expect, test } from "vitest"; -import { agentOwnershipGuard } from "../src/agent-ownership.ts"; import { agentOwnershipHeaders, flueConversationIdFrom, -} from "../src/conversation-identity.ts"; -import { BRUNCH_CONVERSATION_HEADER } from "../src/conversation-payload.ts"; -import { CHAT_AGENT_ROUTE } from "../src/routes.ts"; +} from "../src/conversation/identity.ts"; +import { BRUNCH_CONVERSATION_HEADER } from "../src/conversation/payload.ts"; +import { agentOwnershipGuard } from "../src/http/ownership.ts"; +import { CHAT_AGENT_ROUTE } from "../src/http/routes.ts"; const mount = `/agents/${CHAT_AGENT_ROUTE}`; const app = new Hono(); diff --git a/apps/brunch-agent/test/architecture/boundaries.integration.ts b/apps/brunch-agent/test/architecture/boundaries.integration.ts index 76d2921e820..85142865fb8 100644 --- a/apps/brunch-agent/test/architecture/boundaries.integration.ts +++ b/apps/brunch-agent/test/architecture/boundaries.integration.ts @@ -19,7 +19,6 @@ import { AGENT_DIRECTIVE_STATEMENT, agentModules, allDependencies, - CONTEXT_ROOT, importedPackages, MODEL_KEY_NAME, packageOf, @@ -34,45 +33,15 @@ import { const PACKAGES = workspacePackages(); const CORE = "@hashintel/brunch-agent"; -/** Any substrate package. The harness may never name one; a binding must. */ +/** Flue is the selected agent runtime; lower-level Pi packages remain binding/test concerns. */ const SUBSTRATE_SCOPES = ["@flue/", "@earendil-works/"]; +const FLUE_RUNTIME = "@flue/runtime"; const isSubstrate = (name: string): boolean => SUBSTRATE_SCOPES.some((scope) => name.startsWith(scope)); const byRole = (role: string): WorkspacePackage[] => PACKAGES.filter((pkg) => pkg.dir.startsWith(`${role}-`)); -test("every workspace package is one the spec topology names", () => { - // Derived from the spec's own §12.2 topology block instead of a second - // hand-written list here. The spec names *intended* structure — some - // entries are not scaffolded yet — so the direction checked is disk ⊆ - // spec: a package the spec does not name is loud, while an - // intended-but-unbuilt one is not a failure. (The old hardcoded equality - // would have failed the next legitimate package instead of governing it.) - const spec = readFileSync( - join(CONTEXT_ROOT, "docs/specs/elicitation-kernel.md"), - "utf8", - ); - const topology = /### 12\.2[^\n]*\n[\s\S]*?```text\n([\s\S]*?)```/.exec( - spec, - )?.[1]; - expect(topology).toBeDefined(); - const named = new Set( - [...topology!.matchAll(/^((?:packages|apps)\/[\w-]+)(?=\s|$)/gm)].map( - (match) => match[1]!, - ), - ); - expect(named.size).toBeGreaterThan(0); - for (const pkg of PACKAGES) { - const standalonePath = - pkg.kind === "app" ? "apps/dev" : `packages/${pkg.dir}`; - expect({ pkg: pkg.relPath, inSpec: named.has(standalonePath) }).toEqual({ - pkg: pkg.relPath, - inSpec: true, - }); - } -}); - test("every package is actually scanned", () => { // Without this, a package the file walker misses passes every file-level // invariant vacuously — the substrate-import ban, the plugin-resolves-core @@ -116,18 +85,22 @@ describe("role prefixes name what a package is architecturally (spec §12.2)", ( }); }); -describe("dependency direction (spec §4, §12.2)", () => { - test("the harness imports no substrate", () => { +describe("dependency direction", () => { + test("core's only agent-runtime dependency is Flue", () => { const core = PACKAGES.find((pkg) => pkg.name === CORE); expect(core).toBeDefined(); - expect(allDependencies(core!).filter(isSubstrate)).toEqual([]); + expect(allDependencies(core!).filter(isSubstrate)).toEqual([FLUE_RUNTIME]); for (const file of sourceFiles(core!)) { - const substrateImports = importedPackages(file).filter((s) => - isSubstrate(packageOf(s)), + const substrateImports = importedPackages(file).filter((specifier) => + isSubstrate(packageOf(specifier)), ); expect({ file: file.relPath, substrateImports }).toEqual({ file: file.relPath, - substrateImports: [], + substrateImports: + file.relPath.endsWith("/src/flue.ts") || + file.relPath.endsWith("/src/skills/skill-markdown.ts") + ? [FLUE_RUNTIME] + : [], }); } }); @@ -141,7 +114,7 @@ describe("dependency direction (spec §4, §12.2)", () => { } }); - test("plugins resolve core only — never the binding, never Flue", () => { + test("plugins depend inward on core and may contribute through Flue directly", () => { const plugins = byRole("plugin"); expect(plugins.length).toBeGreaterThan(0); for (const plugin of plugins) { @@ -149,17 +122,21 @@ describe("dependency direction (spec §4, §12.2)", () => { dependency.startsWith("@hashintel/brunch-agent"), ); expect(workspaceDeps).toEqual([CORE]); - expect(allDependencies(plugin).filter(isSubstrate)).toEqual([]); + expect( + allDependencies(plugin).filter( + (dependency) => + isSubstrate(dependency) && dependency !== FLUE_RUNTIME, + ), + ).toEqual([]); for (const file of sourceFiles(plugin)) { for (const specifier of importedPackages(file)) { const pkg = packageOf(specifier); - expect(isSubstrate(pkg)).toBe(false); + expect(isSubstrate(pkg) && pkg !== FLUE_RUNTIME).toBe(false); expect(pkg.startsWith("@hashintel/brunch-agent") ? pkg : CORE).toBe( CORE, ); expect(specifier).not.toBe(`${CORE}/storage`); - expect(specifier).not.toBe(`${CORE}/prompts`); } } } @@ -183,36 +160,11 @@ describe("dependency direction (spec §4, §12.2)", () => { } }); - test("repertoire defaults are guarded core prompt data (ADR-0008)", () => { - expect(PACKAGES.map((pkg) => pkg.dir)).not.toContain("repertoire"); - - const promptImporters = PACKAGES.flatMap((pkg) => - sourceFiles(pkg) - .filter((file) => importedPackages(file).includes(`${CORE}/prompts`)) - .map((file) => ({ pkg: pkg.dir, file: file.relPath })), - ); - expect(promptImporters.length).toBeGreaterThan(0); - for (const importer of promptImporters) { - expect(importer.pkg).toMatch(/^binding-/u); - } - - // Plugin-only CI lints the plugin and does not run this suite. The - // oxlint path ban is the gate that fires then; this assertion keeps - // that gate from disappearing while the suite still runs. - for (const plugin of byRole("plugin")) { - expect( - readFileSync(join(plugin.path, ".oxlintrc.json"), "utf8"), - ).toContain(`"name": "${CORE}/prompts"`); - } - }); - test("transports consume their wire encoder only — never core, a binding, or Flue", () => { const transports = byRole("transport"); expect(transports.length).toBeGreaterThan(0); for (const transport of transports) { - expect(runtimeDependencies(transport).sort()).toEqual( - ["ai", "valibot"].sort(), - ); + expect(runtimeDependencies(transport).sort()).toEqual(["ai", "valibot"]); for (const file of sourceFiles(transport).filter((file) => file.path.startsWith(join(transport.path, "src")), )) { @@ -423,33 +375,15 @@ describe("recorded Flue constraints hold by construction (spec §10)", () => { }); }); -describe("core auxiliary subpaths stay in their assigned lanes (spec §12.2)", () => { - test("core exposes browser contracts, prompts, storage support and testing as explicit subpaths", () => { +describe("core auxiliary subpaths stay in their assigned lanes", () => { + test("core exposes Flue composition, browser contracts, and storage support as explicit subpaths", () => { const core = PACKAGES.find((pkg) => pkg.name === CORE)!; expect(Object.keys(core.manifest.exports ?? {})).toEqual([ ".", "./client-tools", - "./prompts", + "./flue", "./storage", - "./testing", ]); - expect(core.manifest.exports?.["./prompts"]).toEqual({ - types: "./src/prompts.ts", - import: "./dist/prompts.js", - }); - - const rootEntry = readFileSync(join(core.path, "src/index.ts"), "utf8"); - expect(rootEntry).not.toMatch(/\bfrom\s+["']\.\/prompts["']/u); - }); - - test("no package source imports core/testing", () => { - // Fixtures, arbitraries and the replay driver belong to tests; production - // bundles stay clean. - for (const pkg of PACKAGES) { - for (const file of sourceFiles(pkg)) { - expect(importedPackages(file)).not.toContain(`${CORE}/testing`); - } - } }); test("only bindings import core/storage", () => { @@ -486,20 +420,22 @@ describe("the HASH smoke is runnable without a model key or a network (spec §12 * path enters here by review only. */ const SUBSTRATE_INTEGRATION_ENTRY_POINTS: Readonly<Record<string, string>> = { + "apps/brunch-agent/test/brunch-turn.test.ts": + "Types Flue's client, admission, and conversation snapshot and constructs FlueExecutionError so the persona bridge can be unit-tested against a stubbed client — no provider key, no socket, no model call, no runtime boot.", "apps/brunch-agent/test/flue-transcript.test.ts": "Types Flue's public conversation snapshot so the transcript 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/flue-ui-stream.test.ts": "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/proof-artifacts.test.ts": + "Types Flue's public conversation snapshot so canonical trace derivation, workpiece binding, and atomic evidence retention can be unit-tested against an in-memory fixture — no provider key, no socket, no model call, no runtime boot.", "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.", }; test("no test file carries a live model credential", () => { diff --git a/apps/brunch-agent/test/assets.test.ts b/apps/brunch-agent/test/assets.test.ts index 33b92ccedfe..a42d35d8d83 100644 --- a/apps/brunch-agent/test/assets.test.ts +++ b/apps/brunch-agent/test/assets.test.ts @@ -25,7 +25,7 @@ import { pathToFileURL } from "node:url"; import { Hono } from "hono"; import { afterAll, describe, expect, test } from "vitest"; -import { assetHandler } from "../src/assets"; +import { assetHandler } from "../src/http/assets"; const uiRoot = mkdtempSync(join(tmpdir(), "brunch-assets-")); const BINARY_BYTES = Uint8Array.from({ length: 256 }, (_, i) => i); diff --git a/apps/brunch-agent/test/brunch-turn.test.ts b/apps/brunch-agent/test/brunch-turn.test.ts new file mode 100644 index 00000000000..0fb9283139e --- /dev/null +++ b/apps/brunch-agent/test/brunch-turn.test.ts @@ -0,0 +1,631 @@ +import { + FlueExecutionError, + type AgentReadResult, + type AgentSendResult, + type FlueClient, + type FlueConversationPart, + type FlueConversationSnapshot, +} from "@flue/sdk"; +import { describe, expect, test, vi } from "vitest"; + +import { + AWAITING_CLIENT, + CLIENT_TOOL_RESULT_SIGNAL, +} from "../src/conversation/client-tools"; +import { + createBrunchTurnTool, + registerBrunchTurn, + type BrunchTurnExtensionApi, + type BrunchTurnTool, +} from "../src/evaluations/persona/brunch-turn"; +import { + createMockClientToolHost, + createRealHeadlessClientToolHost, +} from "../src/evaluations/persona/client-tool-hosts"; + +type BrunchFlueClient = Pick<FlueClient, "history" | "read" | "send">; + +const admission = (submissionId: string, uid: string): AgentSendResult => ({ + streamUrl: `http://brunch.local/stream/${submissionId}`, + offset: "0", + submissionId, + uid, +}); + +const reply = ( + submissionId: string, + uid: string, + text: string, +): AgentReadResult => ({ + submissionId, + uid, + text, + data: {}, +}); + +const snapshot = ( + submissionId?: string, + parts: FlueConversationPart[] = [], +): FlueConversationSnapshot => ({ + v: 1, + conversationId: "test-conversation", + offset: "1", + messages: + submissionId === undefined + ? [] + : [ + { + id: `message-${submissionId}`, + role: "assistant", + purpose: "assistant", + display: "visible", + submissionId, + parts, + }, + ], + settlements: + submissionId === undefined ? [] : [{ submissionId, outcome: "completed" }], +}); + +const controlledClient = ( + send: BrunchFlueClient["send"], + read: BrunchFlueClient["read"], + history: BrunchFlueClient["history"] = vi + .fn<BrunchFlueClient["history"]>() + .mockResolvedValue(snapshot()), +): BrunchFlueClient => ({ send, read, history }); + +const renderTheme = { + bold: (text: string) => text, + italic: (text: string) => text, + strikethrough: (text: string) => text, + underline: (text: string) => text, + fg: (_color: string, text: string) => text, +}; + +describe("brunch_turn", () => { + test("refuses to register without a usable Herdr child identity", () => { + expect(() => + registerBrunchTurn( + { + registerTool: vi.fn<BrunchTurnExtensionApi["registerTool"]>(), + }, + { conversationId: " " }, + ), + ).toThrow(/PI_SUBAGENT_NAME/u); + }); + + test("registers exactly the transport tool", () => { + const registerTool = vi.fn<BrunchTurnExtensionApi["registerTool"]>(); + const client = controlledClient(vi.fn(), vi.fn()); + + registerBrunchTurn( + { registerTool }, + { conversationId: "persona-registration", client }, + ); + + expect(registerTool).toHaveBeenCalledOnce(); + expect(registerTool.mock.calls[0]?.[0]).toMatchObject({ + name: "brunch_turn", + executionMode: "sequential", + }); + }); + + test("retains each settled canonical snapshot before returning", async () => { + const admitted = admission("submission-evidence", "incarnation-evidence"); + const settledSnapshot = snapshot("submission-evidence", [ + { type: "text", text: "A retained reply", state: "done" }, + ]); + const retainSnapshot = vi + .fn<(value: FlueConversationSnapshot) => Promise<void>>() + .mockResolvedValue(); + const tool = createBrunchTurnTool({ + conversationId: "persona-evidence", + client: controlledClient( + vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted), + vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue( + reply( + "submission-evidence", + "incarnation-evidence", + "A retained reply", + ), + ), + vi.fn<BrunchFlueClient["history"]>().mockResolvedValue(settledSnapshot), + ), + retainSnapshot, + }); + + await tool.execute("tool-evidence", { message: "Retain this turn" }); + + expect(retainSnapshot).toHaveBeenCalledExactlyOnceWith(settledSnapshot); + }); + + test("sends once, reads the exact admission, and conditions later turns on its uid", async () => { + const firstAdmission = admission("submission-1", "incarnation-1"); + const secondAdmission = admission("submission-2", "incarnation-1"); + const send = vi + .fn<BrunchFlueClient["send"]>() + .mockResolvedValueOnce(firstAdmission) + .mockResolvedValueOnce(secondAdmission); + const read = vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValueOnce( + reply("submission-1", "incarnation-1", "First elicitor reply"), + ) + .mockResolvedValueOnce( + reply("submission-2", "incarnation-1", "Second elicitor reply"), + ); + const tool = createBrunchTurnTool({ + conversationId: "persona-sequential", + client: controlledClient(send, read), + }); + + const firstResult = await tool.execute("tool-call-1", { + message: "First persona message", + }); + const secondResult = await tool.execute("tool-call-2", { + message: "Second persona message", + }); + + expect(send).toHaveBeenNthCalledWith(1, { + message: { kind: "user", body: "First persona message" }, + uid: null, + signal: undefined, + }); + expect(send).toHaveBeenNthCalledWith(2, { + message: { kind: "user", body: "Second persona message" }, + uid: "incarnation-1", + signal: undefined, + }); + expect(read).toHaveBeenNthCalledWith(1, firstAdmission, { + signal: undefined, + }); + expect(read).toHaveBeenNthCalledWith(2, secondAdmission, { + signal: undefined, + }); + expect(firstResult).toEqual({ + content: [{ type: "text", text: "First elicitor reply" }], + details: { + conversationId: "persona-sequential", + submissionId: "submission-1", + submissionIds: ["submission-1"], + status: "elicitor-replied", + elicitorText: "First elicitor reply", + toolActivity: [], + }, + }); + expect(secondResult.details).toMatchObject({ + submissionId: "submission-2", + elicitorText: "Second elicitor reply", + }); + expect(firstResult.details.submissionId).not.toBe( + secondResult.details.submissionId, + ); + }); + + test("rejects empty and concurrent calls without sending them", async () => { + let releaseAdmission: ((value: AgentSendResult) => void) | undefined; + const pendingAdmission = new Promise<AgentSendResult>((resolve) => { + releaseAdmission = resolve; + }); + const send = vi + .fn<BrunchFlueClient["send"]>() + .mockReturnValue(pendingAdmission); + const read = vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue( + reply("submission-active", "incarnation-active", "Continue"), + ); + const tool = createBrunchTurnTool({ + conversationId: "persona-concurrent", + client: controlledClient(send, read), + }); + + await expect( + tool.execute("tool-call-empty", { message: " \n " }), + ).rejects.toThrow(/must not be empty/u); + + const activeCall = tool.execute("tool-call-active", { + message: "Active message", + }); + await expect( + tool.execute("tool-call-overlap", { message: "Overlapping message" }), + ).rejects.toThrow(/active submission/u); + + releaseAdmission?.(admission("submission-active", "incarnation-active")); + await activeCall; + + expect(send).toHaveBeenCalledOnce(); + }); + + test("preserves a failed settlement and never sends again after admission", async () => { + const admitted = admission("submission-failed", "incarnation-failed"); + const executionError = new FlueExecutionError({ + target: "agent_submission", + targetId: admitted.submissionId, + failure: "failed", + error: { message: "provider failed" }, + }); + const send = vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted); + const read = vi + .fn<BrunchFlueClient["read"]>() + .mockRejectedValue(executionError); + const tool = createBrunchTurnTool({ + conversationId: "persona-failed", + client: controlledClient(send, read), + }); + + await expect( + tool.execute("tool-call-failed", { message: "Admitted once" }), + ).rejects.toBe(executionError); + expect(executionError).toMatchObject({ + targetId: "submission-failed", + failure: "failed", + }); + + await expect( + tool.execute("tool-call-after-failure", { + message: "Must not be admitted", + }), + ).rejects.toThrow(/inspect canonical Flue history/u); + expect(send).toHaveBeenCalledOnce(); + expect(read).toHaveBeenCalledOnce(); + }); + + test("treats empty assistant text as a terminal protocol failure", async () => { + const admitted = admission("submission-empty", "incarnation-empty"); + const send = vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted); + const read = vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue(reply("submission-empty", "incarnation-empty", "")); + const tool = createBrunchTurnTool({ + conversationId: "persona-empty", + client: controlledClient(send, read), + }); + + await expect( + tool.execute("tool-call-empty-reply", { message: "Please continue" }), + ).rejects.toThrow(/submission-empty.*without assistant text/u); + await expect( + tool.execute("tool-call-after-empty", { message: "Do not send this" }), + ).rejects.toThrow(/inspect canonical Flue history/u); + + expect(send).toHaveBeenCalledOnce(); + }); + + test("records server tool execution without changing the persona-visible reply", async () => { + const admitted = admission("submission-server-tool", "incarnation-tools"); + const history = vi.fn<BrunchFlueClient["history"]>().mockResolvedValue( + snapshot("submission-server-tool", [ + { + type: "dynamic-tool", + toolCallId: "tool-server-1", + toolName: "activate_skill", + state: "output-available", + input: { name: "sdcpn-modelling" }, + output: { activated: true }, + }, + ]), + ); + const tool = createBrunchTurnTool({ + conversationId: "persona-server-tool", + client: controlledClient( + vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted), + vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue( + reply( + "submission-server-tool", + "incarnation-tools", + "What happens first?", + ), + ), + history, + ), + }); + + const result = await tool.execute("tool-call-server", { + message: "I want to describe the process.", + }); + + expect(result.content).toEqual([ + { type: "text", text: "What happens first?" }, + ]); + expect(result.details.toolActivity).toEqual([ + { + sequence: 1, + submissionId: "submission-server-tool", + toolCallId: "tool-server-1", + toolName: "activate_skill", + executor: "server", + outcome: "output", + input: { name: "sdcpn-modelling" }, + output: { activated: true }, + }, + ]); + }); + + test("services client-deferred calls with ordered mocks and resumes the exact submission", async () => { + const firstAdmission = admission( + "submission-client-tool", + "incarnation-tools", + ); + const resumeAdmission = admission( + "submission-client-resume", + "incarnation-tools", + ); + const send = vi + .fn<BrunchFlueClient["send"]>() + .mockResolvedValueOnce(firstAdmission) + .mockResolvedValueOnce(resumeAdmission); + const read = vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValueOnce( + reply( + "submission-client-tool", + "incarnation-tools", + "I will check the guide.", + ), + ) + .mockResolvedValueOnce( + reply( + "submission-client-resume", + "incarnation-tools", + "Open Simulation settings first.", + ), + ); + const history = vi + .fn<BrunchFlueClient["history"]>() + .mockResolvedValueOnce( + snapshot("submission-client-tool", [ + { + type: "dynamic-tool", + toolCallId: "tool-doc-1", + toolName: "readPetrinautDoc", + state: "output-available", + input: { doc: "simulation" }, + output: { awaiting: AWAITING_CLIENT }, + }, + ]), + ) + .mockResolvedValueOnce(snapshot("submission-client-resume")); + const host = createMockClientToolHost([ + { + toolName: "readPetrinautDoc", + input: { doc: "simulation" }, + output: "Simulation guide fixture", + }, + ]); + const tool = createBrunchTurnTool({ + conversationId: "persona-client-tool", + client: controlledClient(send, read, history), + resolveClientToolHost: () => host, + }); + + const result = await tool.execute("tool-call-client", { + message: "How do I run a simulation?", + }); + + expect(send).toHaveBeenNthCalledWith(2, { + message: { + kind: "signal", + type: CLIENT_TOOL_RESULT_SIGNAL, + tagName: CLIENT_TOOL_RESULT_SIGNAL, + body: JSON.stringify([ + { + toolCallId: "tool-doc-1", + toolName: "readPetrinautDoc", + output: "Simulation guide fixture", + }, + ]), + attributes: { toolCallIds: "tool-doc-1" }, + }, + uid: "incarnation-tools", + signal: undefined, + }); + expect(read).toHaveBeenNthCalledWith(1, firstAdmission, { + signal: undefined, + }); + expect(read).toHaveBeenNthCalledWith(2, resumeAdmission, { + signal: undefined, + }); + expect(result).toEqual({ + content: [{ type: "text", text: "Open Simulation settings first." }], + details: { + conversationId: "persona-client-tool", + submissionId: "submission-client-resume", + submissionIds: ["submission-client-tool", "submission-client-resume"], + status: "elicitor-replied", + elicitorText: "Open Simulation settings first.", + toolActivity: [ + { + sequence: 1, + submissionId: "submission-client-tool", + toolCallId: "tool-doc-1", + toolName: "readPetrinautDoc", + executor: "mock", + outcome: "output", + input: { doc: "simulation" }, + output: "Simulation guide fixture", + }, + ], + }, + }); + + const rendered = tool + .renderResult(result, { isPartial: false }, renderTheme, { + isError: false, + }) + .render(80) + .join("\n"); + expect(rendered).toContain("Tool activity"); + expect(rendered).toContain("readPetrinautDoc"); + expect(rendered).toContain("mock; output"); + }); + + test("fails closed on a mock mismatch and blocks later user sends", async () => { + const admitted = admission("submission-mock-mismatch", "incarnation-tools"); + const send = vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted); + const tool = createBrunchTurnTool({ + conversationId: "persona-mock-mismatch", + client: controlledClient( + send, + vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue( + reply("submission-mock-mismatch", "incarnation-tools", ""), + ), + vi.fn<BrunchFlueClient["history"]>().mockResolvedValue( + snapshot("submission-mock-mismatch", [ + { + type: "dynamic-tool", + toolCallId: "tool-doc-mismatch", + toolName: "readPetrinautDoc", + state: "output-available", + input: { doc: "simulation" }, + output: { awaiting: AWAITING_CLIENT }, + }, + ]), + ), + ), + resolveClientToolHost: () => + createMockClientToolHost([ + { + toolName: "readPetrinautDoc", + input: { doc: "scenarios" }, + output: "Wrong fixture", + }, + ]), + }); + + await expect( + tool.execute("tool-call-mismatch", { message: "Check simulation" }), + ).rejects.toThrow(/mock client-tool host failed.*mismatch/iu); + await expect( + tool.execute("tool-call-after-mismatch", { message: "Do not send" }), + ).rejects.toThrow(/inspect canonical Flue history/u); + expect(send).toHaveBeenCalledOnce(); + }); + + test("executes real Petrinaut docs and construction callbacks headlessly", async () => { + const host = createRealHeadlessClientToolHost("Persona tool proof"); + try { + const guide = await host.execute({ + submissionId: "submission-doc", + toolCallId: "tool-doc-real", + toolName: "readPetrinautDoc", + input: { doc: "simulation" }, + }); + const mutation = await host.execute({ + submissionId: "submission-place", + toolCallId: "tool-place-real", + toolName: "addPlace", + input: { + id: "line_idle", + name: "LineIdle", + colorId: null, + dynamicsEnabled: false, + differentialEquationId: null, + x: 0, + y: 0, + }, + }); + + expect(guide).toEqual(expect.stringContaining("# Simulation")); + expect(guide).not.toEqual(expect.stringContaining("<img")); + expect(mutation).toEqual({ applied: true }); + } finally { + await host.dispose?.(); + } + }); + + test("reports admission progress with the submission id", async () => { + const admitted = admission("submission-progress", "incarnation-progress"); + const onUpdate = + vi.fn<NonNullable<Parameters<BrunchTurnTool["execute"]>[3]>>(); + const tool: BrunchTurnTool = createBrunchTurnTool({ + conversationId: "persona-progress", + client: controlledClient( + vi.fn<BrunchFlueClient["send"]>().mockResolvedValue(admitted), + vi + .fn<BrunchFlueClient["read"]>() + .mockResolvedValue( + reply("submission-progress", "incarnation-progress", "Observed"), + ), + ), + }); + + await tool.execute( + "tool-call-progress", + { message: "Show progress" }, + undefined, + onUpdate, + ); + + expect(onUpdate).toHaveBeenCalledWith({ + content: [ + { + type: "text", + text: "Waiting for elicitor submission submission-progress", + }, + ], + details: { + conversationId: "persona-progress", + submissionId: "submission-progress", + status: "waiting-for-elicitor", + }, + }); + }); + + test("renders the two sides of a turn as width-bounded Markdown", () => { + const tool = createBrunchTurnTool({ + conversationId: "persona-render", + client: controlledClient(vi.fn(), vi.fn()), + }); + const callComponent = tool.renderCall( + { message: "A persona message that exceeds the narrow pane width." }, + renderTheme, + ); + const resultComponent = tool.renderResult( + { + content: [{ type: "text", text: "The elicitor's reply." }], + details: { + conversationId: "persona-render", + submissionId: "submission-render", + submissionIds: ["submission-render"], + status: "elicitor-replied", + elicitorText: "The elicitor's reply.", + toolActivity: [], + }, + }, + { isPartial: false }, + renderTheme, + { isError: false }, + ); + + const callLines = callComponent.render(12); + const resultLines = resultComponent.render(12); + + expect(callLines.map((line) => line.trimEnd())).toEqual([ + "User", + "", + "A persona", + "message that", + "exceeds the", + "narrow pane", + "width.", + ]); + expect(resultLines.map((line) => line.trimEnd())).toEqual([ + "Brunch", + "", + "The", + "elicitor's", + "reply.", + ]); + expect( + [...callLines, ...resultLines].every((line) => line.length <= 12), + ).toBe(true); + }); +}); diff --git a/apps/brunch-agent/test/build-artifact.test.ts b/apps/brunch-agent/test/build-artifact.test.ts index 14a8775ba43..a11f909b31d 100644 --- a/apps/brunch-agent/test/build-artifact.test.ts +++ b/apps/brunch-agent/test/build-artifact.test.ts @@ -41,7 +41,10 @@ beforeAll(() => { /** The pinned identity of every agent module in the app, read from source. */ function declaredAgentIdentities(): string[] { const agentsDirectory = join(DEV_APP, "src/agents"); - return readdirSync(agentsDirectory) + return readdirSync(agentsDirectory, { + recursive: true, + encoding: "utf8", + }) .filter((entry) => entry.endsWith(".ts")) .flatMap((entry) => Array.from( @@ -106,9 +109,17 @@ describe("the emitted server bundle", () => { }); test("packages the authored skill without the retired filesystem loader", () => { - expect(bundle).toContain("createSkillReference"); - expect(bundle).toContain("skill:sdcpn-modelling:"); + expect(bundle).toContain("defineSkill"); expect(bundle).toContain("sdcpn-modelling"); + expect(bundle).toContain("The registers are addresses, not a procedure"); + expect(bundle).toContain("Operational-Process and SDCPN Elicitation"); + expect(bundle).toContain( + "Every operational claim has one authoritative home", + ); + expect(bundle).toContain("Capability-aware lifecycle"); + expect(bundle).toContain("Activate the `elicitation` skill"); + expect(bundle).not.toContain("## The role (core)"); + expect(bundle).not.toContain("Completion is computed by the harness"); expect(bundle).not.toContain("splitSkillMarkdown"); expect(bundle).not.toContain("skillFileUrl"); expect(bundle).not.toContain("./sdcpn-modelling/SKILL.md"); diff --git a/apps/brunch-agent/test/conversation-identity.test.ts b/apps/brunch-agent/test/conversation-identity.test.ts index 839ab95dba1..acf6d6c361b 100644 --- a/apps/brunch-agent/test/conversation-identity.test.ts +++ b/apps/brunch-agent/test/conversation-identity.test.ts @@ -1,11 +1,11 @@ import { expect, test } from "vitest"; -import { flueConversationIdWeb } from "../src/conversation-identity-web.ts"; +import { flueConversationIdWeb } from "../src/conversation/identity-web.ts"; import { flueConversationId, flueConversationIdFrom, ownsFlueInstance, -} from "../src/conversation-identity.ts"; +} from "../src/conversation/identity.ts"; test("the same principal and conversation id always hash to the same Flue instance", () => { expect(flueConversationId("principal-a", "conversation-1")).toBe( diff --git a/apps/brunch-agent/test/fixtures/candidate-process-model-workpiece.md b/apps/brunch-agent/test/fixtures/candidate-process-model-workpiece.md new file mode 100644 index 00000000000..57d27646f63 --- /dev/null +++ b/apps/brunch-agent/test/fixtures/candidate-process-model-workpiece.md @@ -0,0 +1,87 @@ +# Process-Model Workpiece + +## Purpose and posture + +### What the model must answer, compare, or support + +Show whether a dispatch crew remains unavailable until final sign-off and becomes available afterward. + +### Who will use it and how + +The operations team will inspect the resource-holding structure before using it in a scheduling comparison. + +### Boundary, horizon, and accuracy expectation + +One batch from entry to dispatch, with only the crew reservation and release path in scope. + +### What the result must not claim + +This account does not establish timing, failure rates, or behavior under simulation. + +## Operational account + +### Participants, locations, flowing things, and resources + +- **Expert evidence:** One dispatch crew is available before final inspection. +- **Expert evidence:** Final inspection reserves the crew so no other batch can use it. + +### Activities, inputs, outputs, and resource use + +- Final inspection reserves the dispatch crew. +- Sign-off releases the dispatch crew in its available state. + +### Case and process spine: flow, branching, joining, failure, retry, and recovery + +#### Primary case: inspected batch + +##### Trigger or admission + +A batch is ready for final inspection. + +##### Ordered account and references + +Final inspection reserves the dispatch crew. Sign-off ends inspection and releases the crew. The batch is then ready for dispatch. + +##### Branches, joins, waits, failures, recovery, and outcomes + +Failure and recovery are **Not yet asked**. + +##### Objective dependencies + +The objective depends on the crew being unavailable between reservation and sign-off and available after sign-off. + +### Time, quantities, arrivals, and stochastic behavior + +Inspection and sign-off durations are **Unknown**. + +## Construction notes + +### Candidate target structures + +Represent crew availability explicitly and preserve acquisition and release around the inspection interval. + +### Construction inferences, approximations, and defaults + +None before construction. + +### Questions reopened by construction + +None for the tool-schema path; a structurally faithful net would still need the inspection interval represented. + +### Target-representation losses + +None identified before construction. + +## Delivery status + +### What this workpiece currently supports + +Inspection of the crew reservation/release requirement. + +### Consequential gaps + +Timing, failure, and recovery remain unresolved. + +### Net status + +Construction was not attempted. Behavior is untested. diff --git a/apps/brunch-agent/test/flue-transcript.test.ts b/apps/brunch-agent/test/flue-transcript.test.ts index 85f93f31fed..0b6827cfbe1 100644 --- a/apps/brunch-agent/test/flue-transcript.test.ts +++ b/apps/brunch-agent/test/flue-transcript.test.ts @@ -3,7 +3,7 @@ import { expect, test } from "vitest"; import { formatFlueTranscript, snapshotToUiMessages, -} from "../src/flue-transcript.ts"; +} from "../src/conversation/transcript.ts"; import type { FlueConversationSnapshot } from "@flue/sdk"; diff --git a/apps/brunch-agent/test/flue-ui-stream.test.ts b/apps/brunch-agent/test/flue-ui-stream.test.ts index cac01ef0756..c151078e40a 100644 --- a/apps/brunch-agent/test/flue-ui-stream.test.ts +++ b/apps/brunch-agent/test/flue-ui-stream.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "vitest"; -import { createFlueUiStream } from "../src/flue-ui-stream.ts"; +import { createFlueUiStream } from "../src/conversation/ui-stream.ts"; import type { ConversationStreamChunk } from "@flue/sdk"; import type { UIMessageChunk } from "ai"; diff --git a/apps/brunch-agent/test/headless-petrinaut-client.test.ts b/apps/brunch-agent/test/headless-petrinaut-client.test.ts index 04ac82d2a72..a8aeaddae68 100644 --- a/apps/brunch-agent/test/headless-petrinaut-client.test.ts +++ b/apps/brunch-agent/test/headless-petrinaut-client.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "vitest"; -import { createHeadlessPetrinautClient } from "../src/headless-petrinaut-client"; +import { createHeadlessPetrinautClient } from "../src/evaluations/runbook/headless-petrinaut-client"; describe("the headless Petrinaut client", () => { test("constructs a parser-accepted document through the bounded callbacks", async () => { diff --git a/apps/brunch-agent/test/local-dev-origins.test.ts b/apps/brunch-agent/test/local-dev-origins.test.ts index e49b0b26a24..f85c76e97bb 100644 --- a/apps/brunch-agent/test/local-dev-origins.test.ts +++ b/apps/brunch-agent/test/local-dev-origins.test.ts @@ -8,7 +8,7 @@ import { localChatListen, localPanelListen, petrinautLocalServer, -} from "../src/local-dev-origins.ts"; +} from "../src/http/local-origins.ts"; const readAppFile = (relativePath: string): string => readFileSync(new URL(`../${relativePath}`, import.meta.url), "utf8"); @@ -65,5 +65,7 @@ test("petrinaut:dev listens on the panel origin chat CORS already assumes", () = expect(readAppFile("petrinaut-local.vite.config.ts")).toContain( "petrinautLocalServer", ); - expect(readAppFile("src/petrinaut-chat.ts")).toContain("defaultPanelOrigins"); + expect(readAppFile("src/http/petrinaut-chat.ts")).toContain( + "defaultPanelOrigins", + ); }); diff --git a/apps/brunch-agent/test/persona-probe-objective.test.ts b/apps/brunch-agent/test/persona-probe-objective.test.ts new file mode 100644 index 00000000000..010e481d967 --- /dev/null +++ b/apps/brunch-agent/test/persona-probe-objective.test.ts @@ -0,0 +1,35 @@ +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; + +import { describe, expect, test } from "vitest"; + +const probeObjective = readFileSync( + new URL( + "../../../libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/persona-probe-objective.md", + import.meta.url, + ), + "utf8", +); + +describe("Mission 4 persona probe objective", () => { + test("matches the complete owner-selected mechanical objective", () => { + expect(createHash("sha256").update(probeObjective).digest("hex")).toBe( + "27396ce3e6e5ed36aa21adbb00d93129af179535c3fb34accca459733dddaa13", + ); + }); + + test("uses a mechanical stop owned by the visible turn count", () => { + expect(probeObjective).toContain( + "Make exactly three visible user submissions, counting the opening as the first", + ); + expect(probeObjective).toContain( + "The turn count alone owns the normal stop.", + ); + }); + + test("does not ask the isolated persona to apply evaluator categories", () => { + expect(probeObjective).not.toMatch( + /\b(?:Orientation|Substantive|Battery|pass|fail)\b/iu, + ); + }); +}); diff --git a/apps/brunch-agent/test/petrinaut-chat.integration.ts b/apps/brunch-agent/test/petrinaut-chat.integration.ts index 20f70775a09..111f119aefa 100644 --- a/apps/brunch-agent/test/petrinaut-chat.integration.ts +++ b/apps/brunch-agent/test/petrinaut-chat.integration.ts @@ -13,17 +13,19 @@ import { import { setProvider } from "@flue/runtime"; import { createFlueClient, FlueApiError } from "@flue/sdk"; -import { applyCaptureSweep } from "../src/capture-sweep.ts"; -import { CLIENT_TOOL_RESULT_SIGNAL } from "../src/client-tool.ts"; +import { READ_PETRINAUT_DOC_TOOL_NAME } from "@hashintel/brunch-agent-plugin-sdcpn/flue"; +import { ELICITATION_SKILL_NAME } from "@hashintel/brunch-agent/flue"; + +import { PING_TOOL_NAME } from "../src/agents/chat-agent/tools/ping.ts"; +import { applyCaptureSweep } from "../src/capture/apply-sweep.ts"; +import { CLIENT_TOOL_RESULT_SIGNAL } from "../src/conversation/client-tools.ts"; import { agentOwnershipHeaders, 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"; +} from "../src/conversation/identity.ts"; +import { formatFlueTranscript } from "../src/conversation/transcript.ts"; +import { loadBuiltBrunchApplication } from "../src/evaluations/runbook/load-built-application.ts"; +import { CHAT_AGENT_ROUTE } from "../src/http/routes.ts"; import type { PetrinautChatResult, @@ -142,14 +144,28 @@ try { ], { stopReason: "toolUse" }, ), + fauxAssistantMessage( + [ + fauxThinking("The job skill routes universal judgment to core."), + fauxToolCall( + ACTIVATE_SKILL_TOOL_NAME, + { name: ELICITATION_SKILL_NAME }, + { id: "tool-skill-2" }, + ), + ], + { stopReason: "toolUse" }, + ), (context) => fauxAssistantMessage( [ - fauxThinking("Read elicitation teaching from the skill package."), + fauxThinking("Read the SDCPN-specific elicitation profile."), fauxToolCall( READ_SKILL_RESOURCE_TOOL_NAME, { - path: packagedSkillResourcePathFrom(context, "elicitation.md"), + path: packagedSkillResourcePathFrom( + context, + "references/profile.md", + ), }, { id: "tool-resource-1" }, ), diff --git a/apps/brunch-agent/test/petrinaut-chat.test.ts b/apps/brunch-agent/test/petrinaut-chat.test.ts index b252a487cc4..712f5cac82d 100644 --- a/apps/brunch-agent/test/petrinaut-chat.test.ts +++ b/apps/brunch-agent/test/petrinaut-chat.test.ts @@ -113,12 +113,13 @@ test("the committed /api/chat door streams a plain Flue agent through server and toolName: "read_skill_resource", }); expect(JSON.stringify(result.readSkillResourceCall?.input ?? {})).toContain( - "elicitation.md", + "profile.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("brunch_ask"); expect(result.interviewerToolNames).not.toContain("sweep"); expect(result.interviewerToolNames).not.toContain("brunch_sweep"); expect(result.interviewerToolNames).not.toEqual( diff --git a/apps/brunch-agent/test/proof-artifacts.test.ts b/apps/brunch-agent/test/proof-artifacts.test.ts new file mode 100644 index 00000000000..2b12eecf76f --- /dev/null +++ b/apps/brunch-agent/test/proof-artifacts.test.ts @@ -0,0 +1,297 @@ +import { createHash } from "node:crypto"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { afterEach, expect, test } from "vitest"; + +import { + deriveProofTrace, + writeProofArtifacts, +} from "../src/evaluations/persona/proof-artifacts"; + +import type { FlueConversationSnapshot } from "@flue/sdk"; + +const temporaryDirectories: string[] = []; + +afterEach(async () => { + await Promise.all( + temporaryDirectories.map((directory) => + rm(directory, { recursive: true, force: true }), + ), + ); +}); + +const snapshot: FlueConversationSnapshot = { + v: 1, + conversationId: "proof-conversation", + offset: "9", + messages: [ + { + id: "user-1", + role: "user", + purpose: "user", + display: "visible", + parts: [{ type: "text", text: "Help me model this.", state: "done" }], + }, + { + id: "assistant-1", + role: "assistant", + purpose: "assistant", + display: "visible", + submissionId: "submission-1", + parts: [ + { + type: "dynamic-tool", + toolCallId: "activate-job", + toolName: "activate_skill", + state: "output-available", + input: { name: "sdcpn-modelling" }, + output: { ok: true }, + }, + { + type: "dynamic-tool", + toolCallId: "activate-capability", + toolName: "activate_skill", + state: "output-available", + input: { name: "elicitation" }, + output: { ok: true }, + }, + { + type: "dynamic-tool", + toolCallId: "read-profile", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/skill%3Asdcpn-modelling%3Aabc/references/profile.md", + }, + output: "profile", + }, + { + type: "text", + text: "What happens when the alarm fires?", + state: "done", + }, + { + type: "dynamic-tool", + toolCallId: "read-template", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/skill%3Asdcpn-modelling%3Aabc/templates/workpiece.md", + }, + output: "template", + }, + { + type: "text", + text: "```runbook-ir\n# Current workpiece\n```", + state: "done", + }, + { + type: "dynamic-tool", + toolCallId: "client-doc", + toolName: "readPetrinautDoc", + state: "output-available", + input: { doc: "simulation" }, + output: { awaiting: "client" }, + }, + { + type: "dynamic-tool", + toolCallId: "server-ping", + toolName: "ping", + state: "output-error", + input: {}, + errorText: "not available", + }, + ], + }, + ], + settlements: [{ submissionId: "submission-1", outcome: "completed" }], +}; + +test("derives canonical proof events in message and part order", () => { + expect(deriveProofTrace(snapshot)).toEqual({ + conversationId: "proof-conversation", + events: [ + { + sequence: 1, + type: "user", + turn: 1, + messageId: "user-1", + text: "Help me model this.", + }, + { + sequence: 2, + type: "activate", + turn: 1, + messageId: "assistant-1", + toolCallId: "activate-job", + name: "sdcpn-modelling", + outcome: "ok", + }, + { + sequence: 3, + type: "activate", + turn: 1, + messageId: "assistant-1", + toolCallId: "activate-capability", + name: "elicitation", + outcome: "ok", + }, + { + sequence: 4, + type: "read", + turn: 1, + messageId: "assistant-1", + toolCallId: "read-profile", + path: "sdcpn-modelling/references/profile.md", + outcome: "ok", + }, + { + sequence: 5, + type: "text", + turn: 1, + messageId: "assistant-1", + text: "What happens when the alarm fires?", + hasWorkpiece: false, + }, + { + sequence: 6, + type: "read", + turn: 1, + messageId: "assistant-1", + toolCallId: "read-template", + path: "sdcpn-modelling/templates/workpiece.md", + outcome: "ok", + }, + { + sequence: 7, + type: "text", + turn: 1, + messageId: "assistant-1", + text: "```runbook-ir\n# Current workpiece\n```", + hasWorkpiece: true, + }, + { + sequence: 8, + type: "tool", + turn: 1, + messageId: "assistant-1", + toolCallId: "client-doc", + name: "readPetrinautDoc", + executor: "client", + outcome: "ok", + }, + { + sequence: 9, + type: "tool", + turn: 1, + messageId: "assistant-1", + toolCallId: "server-ping", + name: "ping", + executor: "server", + outcome: "error", + }, + ], + firstWorkpiece: { + messageId: "assistant-1", + sequence: 7, + }, + }); +}); + +test("retains malformed resource paths in an error trace", () => { + const malformedPath = "/.flue/packaged-skills/skill%/references/profile.md"; + const malformedSnapshot = { + ...snapshot, + messages: [ + ...snapshot.messages, + { + id: "assistant-2", + role: "assistant", + purpose: "assistant", + display: "visible", + submissionId: "submission-2", + parts: [ + { + type: "dynamic-tool", + toolCallId: "read-malformed", + toolName: "read_skill_resource", + state: "output-error", + input: { path: malformedPath }, + errorText: "invalid path", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect(deriveProofTrace(malformedSnapshot).events).toContainEqual( + expect.objectContaining({ + type: "read", + path: malformedPath, + outcome: "error", + }), + ); +}); + +test("atomically writes the canonical snapshot and its derived projections", async () => { + const directory = await mkdtemp(join(tmpdir(), "brunch-proof-")); + temporaryDirectories.push(directory); + + await writeFile( + join(directory, "run.json"), + `${JSON.stringify({ attemptId: "attempt-1", slot: "probe" })}\n`, + ); + await writeProofArtifacts(directory, snapshot); + + const [ + snapshotJson, + transcript, + traceJson, + traceMarkdown, + workpiece, + workpieceSourceJson, + manifestJson, + ] = await Promise.all([ + readFile(join(directory, "snapshot.json"), "utf8"), + readFile(join(directory, "transcript.md"), "utf8"), + readFile(join(directory, "trace.json"), "utf8"), + readFile(join(directory, "trace.md"), "utf8"), + readFile(join(directory, "workpiece.md"), "utf8"), + readFile(join(directory, "workpiece-source.json"), "utf8"), + readFile(join(directory, "manifest.json"), "utf8"), + ]); + + expect(JSON.parse(snapshotJson)).toEqual(snapshot); + expect(transcript).toContain("Help me model this."); + expect(transcript).toContain("tool activate_skill"); + expect(JSON.parse(traceJson)).toEqual(deriveProofTrace(snapshot)); + expect(traceMarkdown).toContain("2. turn 1: `activate(sdcpn-modelling, ok)`"); + expect(traceMarkdown).toContain( + "7. turn 1: `text(hasWorkpiece=true)` — message `assistant-1`", + ); + expect(workpiece).toBe("# Current workpiece\n"); + expect(JSON.parse(workpieceSourceJson)).toMatchObject({ + sourceMessageId: "assistant-1", + }); + + const manifest = JSON.parse(manifestJson) as { + files: { path: string; sha256: string }[]; + }; + expect(manifest.files.map(({ path }) => path)).toEqual([ + "run.json", + "snapshot.json", + "trace.json", + "trace.md", + "transcript.md", + "workpiece-source.json", + "workpiece.md", + ]); + const snapshotEntry = manifest.files.find( + ({ path }) => path === "snapshot.json", + ); + expect(snapshotEntry?.sha256).toBe( + createHash("sha256").update(snapshotJson).digest("hex"), + ); +}); diff --git a/apps/brunch-agent/test/retired-run-archive.test.ts b/apps/brunch-agent/test/retired-run-archive.test.ts new file mode 100644 index 00000000000..602848d6e7e --- /dev/null +++ b/apps/brunch-agent/test/retired-run-archive.test.ts @@ -0,0 +1,78 @@ +import { execFile } from "node:child_process"; +import { createHash } from "node:crypto"; +import { mkdtemp, readFile, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { promisify } from "node:util"; + +import { expect, test } from "vitest"; + +const execFileAsync = promisify(execFile); +const repositoryRoot = join(import.meta.dirname, "../../.."); +const brunchRoot = join(repositoryRoot, "libs/@hashintel/brunch-agent"); +const archivePath = join( + brunchRoot, + "docs/archive/evaluations/flue-skill-composition-side-quest-runs.tar.gz", +); +const archiveSha256 = + "99d5302fb42807b9e9d77d4c432f4b52b77aeea4f7312cd6fb8f104452e3fc2a"; +const campaigns = [ + "flue-skill-composition-side-quest-v1", + "flue-skill-composition-side-quest-v2", + "flue-skill-composition-side-quest-v3", +] as const; + +test("the retired side-quest runs recover without a historical Git ref", async () => { + const recoveryDirectory = await mkdtemp( + join(tmpdir(), "brunch-retired-runs-"), + ); + try { + expect( + createHash("sha256") + .update(await readFile(archivePath)) + .digest("hex"), + ).toBe(archiveSha256); + await execFileAsync("tar", ["-xzf", archivePath, "-C", recoveryDirectory], { + cwd: repositoryRoot, + }); + + const entries = ( + await Promise.all( + campaigns.map(async (campaign) => { + const ledger = await readFile( + join( + brunchRoot, + `docs/evidence/evaluations/${campaign}/retired-runs.sha256`, + ), + "utf8", + ); + return ledger + .trim() + .split("\n") + .map((line) => { + const separator = line.indexOf(" "); + if (separator === -1) { + throw new Error(`Malformed retired-run ledger entry: ${line}`); + } + return { + expectedHash: line.slice(0, separator), + path: line.slice(separator + 2), + }; + }); + }), + ) + ).flat(); + + expect(entries).toHaveLength(47); + await Promise.all( + entries.map(async ({ expectedHash, path }) => { + const recovered = await readFile(join(recoveryDirectory, path)); + expect(createHash("sha256").update(recovered).digest("hex")).toBe( + expectedHash, + ); + }), + ); + } finally { + await rm(recoveryDirectory, { recursive: true, force: true }); + } +}); diff --git a/apps/brunch-agent/test/runbook-artifacts.test.ts b/apps/brunch-agent/test/runbook-artifacts.test.ts index 410ff7357b9..ec065240c0c 100644 --- a/apps/brunch-agent/test/runbook-artifacts.test.ts +++ b/apps/brunch-agent/test/runbook-artifacts.test.ts @@ -2,10 +2,12 @@ import { describe, expect, test } from "vitest"; import { latestRunbookIrBlock, + ordinaryElicitationViolationsFrom, recoverRunbookIr, + recoverRunbookWorkpiece, RUNBOOK_IR_FENCE, skillResourcePathsFrom, -} from "../src/runbook-artifacts.ts"; +} from "../src/evaluations/runbook/artifacts.ts"; import type { FlueConversationSnapshot } from "@flue/sdk"; @@ -46,6 +48,262 @@ describe("runbook artifact recovery", () => { ].join("\n"), ); expect(recoverRunbookIr(snapshot)).toContain("# Runbook IR"); + const workpiece = recoverRunbookWorkpiece(snapshot); + expect(workpiece?.content).toContain("# Runbook IR"); + expect(workpiece?.sourceMessageId).toBe("a1"); + expect(workpiece?.sha256).toMatch(/^[0-9a-f]{64}$/u); + expect(workpiece?.sourceMessageSha256).toMatch(/^[0-9a-f]{64}$/u); + }); + + test.each([ + ["construction tool", "addPlace", "construction-tool-use"], + ["capture tool", "brunch_sweep", "capture-tool-use"], + ["other tool", "ping", "unexpected-tool-use"], + ])("classifies %s as an ordinary-path violation", (_, toolName, code) => { + const snapshot = { + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [ + { + type: "dynamic-tool", + toolCallId: "t1", + toolName, + state: "output-available", + input: {}, + output: "ok", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).toContainEqual(expect.objectContaining({ code, detail: toolName })); + }); + + test("construction resources and a missing workpiece invalidate an ordinary member", () => { + 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/example/references/pn-construction.md", + }, + output: "ok", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: false }), + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: "construction-resource-read" }), + expect.objectContaining({ code: "missing-workpiece" }), + ]), + ); + }); + + test("rejects more than one explicit question in an assistant turn", () => { + const snapshot = snapshotWithAssistantText( + "What starts the process? Who performs the first step?", + ); + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: false }), + ).toContainEqual({ + code: "multiple-questions", + detail: "a1: 2 question marks", + }); + }); + + test("does not count questions recorded inside the workpiece as interactive questions", () => { + const snapshot = snapshotWithAssistantText( + "```runbook-ir\n# Workpiece\n## Open questions\n- Who signs off?\n- When is the crew released?\n```", + ); + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).not.toContainEqual( + expect.objectContaining({ code: "multiple-questions" }), + ); + }); + + test("does not treat a workpiece question as the first interactive question", () => { + const snapshot = { + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [ + { + type: "text", + text: "```runbook-ir\n# Workpiece\n## Open questions\n- Who signs off?\n```", + state: "done", + }, + { + type: "dynamic-tool", + toolCallId: "guidance-profile", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/example/references/profile.md", + }, + output: "ok", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).not.toContainEqual({ + code: "late-required-resource", + detail: "profile.md: after first question", + }); + }); + + test("requires successful profile and workpiece reads for an ordinary workpiece", () => { + const snapshot = snapshotWithAssistantText( + "What process should we model?\n```runbook-ir\n# Workpiece\n```", + ); + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).toEqual( + expect.arrayContaining([ + { code: "missing-required-resource", detail: "profile.md" }, + { code: "missing-required-resource", detail: "workpiece.md" }, + ]), + ); + }); + + test("requires guidance before the first question and the template before workpiece creation", () => { + const snapshot = { + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [ + { + type: "text", + text: "What process should we model?", + state: "done", + }, + { + type: "dynamic-tool" as const, + toolCallId: "guidance-profile", + toolName: "read_skill_resource", + state: "output-available" as const, + input: { + path: "/.flue/packaged-skills/example/references/profile.md", + }, + output: "ok", + }, + { + type: "text", + text: "```runbook-ir\n# Workpiece\n```", + state: "done", + }, + { + type: "dynamic-tool", + toolCallId: "template", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/example/templates/workpiece.md", + }, + output: "ok", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).toEqual( + expect.arrayContaining([ + { + code: "late-required-resource", + detail: "profile.md: after first question", + }, + { + code: "late-required-resource", + detail: "workpiece.md: after first workpiece", + }, + ]), + ); + }); + + test("accepts ordered required disclosure on an ordinary workpiece path", () => { + const snapshot = { + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [ + { + type: "dynamic-tool" as const, + toolCallId: "guidance-profile", + toolName: "read_skill_resource", + state: "output-available" as const, + input: { + path: "/.flue/packaged-skills/example/references/profile.md", + }, + output: "ok", + }, + { + type: "text", + text: "What process should we model?", + state: "done", + }, + { + type: "dynamic-tool", + toolCallId: "template", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/example/templates/workpiece.md", + }, + output: "ok", + }, + { + type: "text", + text: "```runbook-ir\n# Workpiece\n```", + state: "done", + }, + ], + }, + ], + } as FlueConversationSnapshot; + + expect( + ordinaryElicitationViolationsFrom(snapshot, { hasWorkpiece: true }), + ).toEqual([]); }); test("collects only successfully read skill resource paths", () => { diff --git a/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts b/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts index fdf1c1de6a2..ea74ca432e6 100644 --- a/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts +++ b/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts @@ -1,13 +1,45 @@ +import { mkdirSync, renameSync, writeFileSync } from "node:fs"; +import { basename, join } from "node:path"; + const replies = [ "Last Tuesday Line 1 stopped milling because the holding tank before filling was full.", ]; let replyIndex = 0; +let sabotaged = false; + +const applyRequestedRetentionSabotage = (): void => { + if (sabotaged) return; + const databasePath = process.env["BRUNCH_DEV_DB_PATH"]; + const outputDirectory = process.env["BRUNCH_RUNBOOK_OUTPUT_DIR"]; + if (databasePath === undefined || outputDirectory === undefined) return; + if (process.env["BRUNCH_RUNBOOK_FAUX_ARTIFACT_COLLISION"] === "1") { + const runId = basename(databasePath, ".db"); + writeFileSync( + join(outputDirectory, `${runId}.json`), + "collision sentinel\n", + { + flag: "wx", + }, + ); + sabotaged = true; + } + if (process.env["BRUNCH_RUNBOOK_FAUX_CLEANUP_FAIL"] === "1") { + renameSync(databasePath, `${databasePath}.retained`); + mkdirSync(databasePath); + writeFileSync(join(databasePath, "cleanup-blocker"), "retained\n"); + sabotaged = true; + } +}; export default { messages: { - create: () => - Promise.resolve({ + create: () => { + if (process.env["BRUNCH_RUNBOOK_FAUX_EXPERT_FAIL"] === "1") { + throw new Error("Deliberate faux expert failure"); + } + applyRequestedRetentionSabotage(); + return Promise.resolve({ content: process.env["BRUNCH_RUNBOOK_EMPTY_EXPERT"] === "1" ? [] @@ -26,6 +58,7 @@ export default { 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 index 27b6614ead9..90267356fb5 100644 --- a/apps/brunch-agent/test/runbook-elicitation-faux-provider.ts +++ b/apps/brunch-agent/test/runbook-elicitation-faux-provider.ts @@ -7,6 +7,8 @@ import { const modelId = process.env["BRUNCH_CHAT_MODEL"] ?? "claude-haiku-4-5"; const skillName = "sdcpn-modelling"; +const elicitationSkillName = "elicitation"; +const violation = process.env["BRUNCH_RUNBOOK_FAUX_VIOLATION"]; const packagedSkillResourcePathFrom = ( context: unknown, @@ -41,13 +43,49 @@ const faux = fauxProvider({ models: [{ id: modelId, reasoning: true }], }); +const maybeIr = (detail: string): string => + violation === "missing-workpiece" ? detail : ir(detail); +const adversarialToolName = + violation === "construction-tool" + ? "addPlace" + : violation === "capture-tool" + ? "brunch_sweep" + : violation === "unexpected-tool" + ? "ping" + : undefined; + faux.setResponses([ + (context: unknown) => { + const modelRequest = JSON.stringify(context); + for (const requiredPromptText of [ + "You are the Brunch elicitation assistant.", + "Operational Process Modelling for SDCPN", + "substantive elicitation, review, workpiece revision, or construction", + ]) { + if (!modelRequest.includes(requiredPromptText)) { + throw new Error(`model request omitted: ${requiredPromptText}`); + } + } + if (modelRequest.includes("## The role (core)")) { + throw new Error("model request retained the legacy core prompt"); + } + return fauxAssistantMessage( + [ + fauxToolCall( + "activate_skill", + { name: skillName }, + { id: "activate-skill" }, + ), + ], + { stopReason: "toolUse" }, + ); + }, fauxAssistantMessage( [ fauxToolCall( "activate_skill", - { name: skillName }, - { id: "activate-skill" }, + { name: elicitationSkillName }, + { id: "activate-elicitation-skill" }, ), ], { stopReason: "toolUse" }, @@ -58,39 +96,57 @@ faux.setResponses([ fauxToolCall( "read_skill_resource", { - path: packagedSkillResourcePathFrom(context, "elicitation.md"), + path: packagedSkillResourcePathFrom( + context, + violation === "construction-resource" + ? "references/pn-construction.md" + : "references/profile.md", + ), }, - { id: "read-elicitation" }, + { id: "read-profile" }, ), ], { stopReason: "toolUse" }, ), + fauxAssistantMessage([ + fauxText( + "Walk me through the last scheduling decision that surprised you.", + ), + ]), (context: unknown) => fauxAssistantMessage( [ fauxToolCall( "read_skill_resource", { - path: packagedSkillResourcePathFrom(context, "ir-template.md"), + path: packagedSkillResourcePathFrom( + context, + "templates/workpiece.md", + ), }, - { id: "read-ir-template" }, + { id: "read-workpiece-template" }, ), ], { stopReason: "toolUse" }, ), fauxAssistantMessage([ fauxText( - `Walk me through the last scheduling decision that surprised you.\n\n${ir("Not yet asked.")}`, + `What caused Line 1 to wait in that case?\n\n${maybeIr("Line 1 waited between milling and filling.")}`, ), ]), + ...(adversarialToolName === undefined + ? [] + : [ + fauxAssistantMessage( + [fauxToolCall(adversarialToolName, {}, { id: "adversarial-tool" })], + { stopReason: "toolUse" }, + ), + ]), fauxAssistantMessage([ fauxText( - `What caused Line 1 to wait in that case?\n\n${ir("Line 1 waited between milling and filling.")}`, + maybeIr("Line 1 waited when its mill-to-fill holding tank backed up."), ), ]), - 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 deleted file mode 100644 index 44aca868307..00000000000 --- a/apps/brunch-agent/test/runbook-elicitation.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -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<string, string>; - }; - 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 index dbef41dd0c9..4d5c0833ce6 100644 --- a/apps/brunch-agent/test/runbook-headless.integration.ts +++ b/apps/brunch-agent/test/runbook-headless.integration.ts @@ -12,34 +12,34 @@ import { import { setProvider } from "@flue/runtime"; import { createFlueClient } from "@flue/sdk"; +import { VALIDATED_CONSTRUCTION_MODE } from "@hashintel/brunch-agent-plugin-sdcpn/flue"; + import { CLIENT_TOOL_RESULT_SIGNAL, isAwaitingClient, -} from "../src/client-tool.ts"; +} from "../src/conversation/client-tools.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"; +} from "../src/conversation/identity.ts"; +import { deriveProofTrace } from "../src/evaluations/persona/proof-artifacts.ts"; import { interviewerToolNamesFrom, skillResourcePathsFrom, -} from "../src/runbook-artifacts.ts"; -import { VALIDATED_CONSTRUCTION_MODE } from "../src/tools/petrinaut-construction.ts"; +} from "../src/evaluations/runbook/artifacts.ts"; +import { + createHeadlessPetrinautClient, + isPetrinautConstructionToolName, +} from "../src/evaluations/runbook/headless-petrinaut-client.ts"; +import { loadBuiltBrunchApplication } from "../src/evaluations/runbook/load-built-application.ts"; +import { CHAT_AGENT_ROUTE } from "../src/http/routes.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", + "references/pn-construction.md", + "references/checks.md", ] as const; process.env.BRUNCH_CHAT_MODEL = CHAT_MODEL_ID; @@ -48,10 +48,7 @@ process.env.BRUNCH_DEV_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, - ), + new URL("./fixtures/candidate-process-model-workpiece.md", import.meta.url), ); const filledIr = await readFile(irPath, "utf8"); @@ -238,7 +235,23 @@ faux.setResponses([ ), fauxAssistantMessage([ fauxText( - "Construction complete. Assumption: one line state is representative. Unknowns and the IR's commercial and breakdown losses remain unresolved.", + [ + "The mounted tools accepted and returned a minimal self-loop, but it does not yet represent the workpiece's reservation interval. Behavior was not executed or tested.", + "", + "```runbook-ir", + filledIr, + "", + "## Construction update", + "", + "**Agent construction finding:** tool-schema acceptance was reached for a minimal place/transition/arc path.", + "", + "**Target-representation loss:** the constructed self-loop does not preserve crew unavailability between final inspection and sign-off, so structural correspondence with the workpiece was not established.", + "", + "## Delivery update", + "", + "Net status: tool-schema accepted; structural correspondence not established; behavior untested.", + "```", + ].join("\n"), ), ]), ]); @@ -329,6 +342,10 @@ try { ), ); const resourcePaths = skillResourcePathsFrom(snapshot); + const activatedSkillNames = deriveProofTrace(snapshot).events.flatMap( + (event) => + event.type === "activate" && event.outcome === "ok" ? [event.name] : [], + ); const assistantText = snapshot.messages .flatMap((message) => message.parts) .flatMap((part) => (part.type === "text" ? [part.text] : [])) @@ -338,11 +355,12 @@ try { process.stdout.write( `RUNBOOK_HEADLESS_HERMETIC ${JSON.stringify({ - sourceIrUsed: filledIr.includes("VW-02 dark tint restriction"), + sourceIrUsed: filledIr.includes("dispatch crew remains unavailable"), parseOk: parsed.ok, placeCount: definition.places.length, transitionCount: definition.transitions.length, toolNames: interviewerToolNamesFrom(snapshot), + activatedSkillNames, resourceFilesRead: RUNBOOK_RESOURCE_FILES.filter((resourceFile) => resourcePaths.some((resourcePath) => resourcePath.endsWith(resourceFile), @@ -350,6 +368,11 @@ try { ), validationRejections, emittedFreeFormPnJson: assistantText.includes("```pn-json"), + emittedUpdatedWorkpiece: assistantText.includes("```runbook-ir"), + evidenceLevelHonest: + assistantText.includes("tool-schema accepted") && + assistantText.includes("structural correspondence not established") && + assistantText.includes("behavior untested"), userMessages: snapshot.messages.filter( (message) => message.purpose === "user", ).length, diff --git a/apps/brunch-agent/test/runbook-headless.test.ts b/apps/brunch-agent/test/runbook-headless.test.ts index 861f0865d74..c9d20996370 100644 --- a/apps/brunch-agent/test/runbook-headless.test.ts +++ b/apps/brunch-agent/test/runbook-headless.test.ts @@ -8,7 +8,7 @@ import { runNodeScript } from "./run-node-script"; const testDirectory = import.meta.dirname; -test("the built ChatAgent constructs a validated net from the saved IR", async () => { +test("the built ChatAgent reports only the construct-only evidence it reaches", async () => { const dbDirectory = await mkdtemp(join(tmpdir(), "brunch-runbook-")); try { const { exitCode, stdout, stderr } = await runNodeScript( @@ -29,9 +29,12 @@ test("the built ChatAgent constructs a validated net from the saved IR", async ( placeCount: number; transitionCount: number; toolNames: string[]; + activatedSkillNames: string[]; resourceFilesRead: string[]; validationRejections: string[]; emittedFreeFormPnJson: boolean; + emittedUpdatedWorkpiece: boolean; + evidenceLevelHonest: boolean; userMessages: number; wroteCaptureStore: boolean; }; @@ -54,17 +57,19 @@ test("the built ChatAgent constructs a validated net from the saved IR", async ( expect(result.toolNames).not.toContain("sweep"); expect(result.toolNames).not.toContain("brunch_sweep"); expect(result.toolNames).not.toContain("brunch_ask"); + expect(result.activatedSkillNames).toEqual(["sdcpn-modelling"]); + expect(result.activatedSkillNames).not.toContain("elicitation"); expect(result.resourceFilesRead).toEqual([ - "elicitation.md", - "ir-template.md", - "pn-construction.md", - "checks.md", + "references/pn-construction.md", + "references/checks.md", ]); expect(result.validationRejections).toHaveLength(1); expect(result.validationRejections[0]).toContain( "expected number to be >0", ); expect(result.emittedFreeFormPnJson).toBe(false); + expect(result.emittedUpdatedWorkpiece).toBe(true); + expect(result.evidenceLevelHonest).toBe(true); expect(result.userMessages).toBe(1); expect(result.wroteCaptureStore).toBe(false); } finally { diff --git a/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts b/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts deleted file mode 100644 index aa63b1e60ac..00000000000 --- a/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 224a52c4eaf..00000000000 --- a/apps/brunch-agent/test/sdcpn-modelling-skill.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -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 deleted file mode 100644 index 35c65f43629..00000000000 --- a/apps/brunch-agent/test/turn-timing.test.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { expect, test } from "vitest"; - -import { - createTurnTimingRecorder, - type TurnTimingPurpose, -} from "../../../libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/turn-timing.ts"; - -import type { FlueObservation, ModelRequest } from "@flue/runtime"; - -const observation = ( - event: Partial<FlueObservation> & Pick<FlueObservation, "type">, -): FlueObservation => event as FlueObservation; - -const request = (latestUserMessage = "Continue."): ModelRequest => ({ - providerId: "faux", - providerName: "faux", - requestedModel: "faux-model", - api: "faux", - input: { - messages: [{ role: "user", content: latestUserMessage }], - }, -}); - -const completedTurn = ( - turnId: string, - operationId: string | undefined, - purpose: "agent" | "compaction", -): FlueObservation => - observation({ - type: "turn", - turnId, - operationId, - purpose, - durationMs: 17, - request: { - providerId: "faux", - providerName: "faux", - requestedModel: "faux-model", - api: "faux", - }, - response: {}, - isError: false, - }); - -test("attributes post-sweep compaction to the completed harness purpose", () => { - const recorder = createTurnTimingRecorder(); - recorder.startInterviewerTurn(1); - recorder.observe( - observation({ - type: "operation_start", - operationId: "outer", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "interview", - operationId: "outer", - purpose: "agent", - request: request(), - }), - ); - recorder.observe(completedTurn("interview", "outer", "agent")); - recorder.observe( - observation({ - type: "operation_start", - operationId: "sweep-extraction", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "sweep", - operationId: "sweep-extraction", - purpose: "agent", - request: request("Extract proposals."), - }), - ); - recorder.observe(completedTurn("sweep", "sweep-extraction", "agent")); - recorder.observe( - observation({ - type: "operation", - operationId: "sweep-extraction", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "tool", - toolName: "brunch_sweep", - toolCallId: "applied-sweep", - isError: false, - result: { status: "applied" }, - durationMs: 1, - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "sweep-compaction", - operationId: "sweep-compaction-operation", - purpose: "compaction", - request: request(), - }), - ); - recorder.observe( - completedTurn( - "sweep-compaction", - "sweep-compaction-operation", - "compaction", - ), - ); - - expect( - Object.fromEntries( - recorder - .all() - .map((timing) => [timing.flueTurnId, timing.purpose] as const), - ), - ).toEqual<Record<string, TurnTimingPurpose>>({ - interview: "interview", - sweep: "sweep", - "sweep-compaction": "sweep", - }); -}); - -test("attributes an inline retry after a refused sweep as repair", () => { - const recorder = createTurnTimingRecorder(); - recorder.startInterviewerTurn(1); - recorder.observe( - observation({ - type: "operation_start", - operationId: "outer", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "interview", - operationId: "outer", - purpose: "agent", - request: request(), - }), - ); - recorder.observe( - observation({ - type: "operation_start", - operationId: "initial-extraction", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "sweep-extraction", - operationId: "initial-extraction", - purpose: "agent", - request: request("Extract proposals."), - }), - ); - recorder.observe( - completedTurn("sweep-extraction", "initial-extraction", "agent"), - ); - recorder.observe( - observation({ - type: "operation", - operationId: "initial-extraction", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "tool", - toolName: "brunch_sweep", - toolCallId: "refused-sweep", - isError: false, - result: { status: "refused" }, - durationMs: 1, - }), - ); - recorder.observe( - observation({ - type: "operation_start", - operationId: "repair-extraction", - operationKind: "prompt", - }), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "repair-compaction", - operationId: "repair-compaction-operation", - purpose: "compaction", - request: request(), - }), - ); - recorder.observe( - completedTurn( - "repair-compaction", - "repair-compaction-operation", - "compaction", - ), - ); - recorder.observe( - observation({ - type: "turn_request", - turnId: "repair-extraction", - operationId: "repair-extraction", - purpose: "agent", - request: request("Extract repaired proposals."), - }), - ); - recorder.observe( - completedTurn("repair-extraction", "repair-extraction", "agent"), - ); - recorder.observe(completedTurn("interview", "outer", "agent")); - - expect( - Object.fromEntries( - recorder - .all() - .map((timing) => [timing.flueTurnId, timing.purpose] as const), - ), - ).toEqual<Record<string, TurnTimingPurpose>>({ - "sweep-extraction": "sweep", - "repair-compaction": "repair", - "repair-extraction": "repair", - interview: "interview", - }); -}); diff --git a/apps/brunch-agent/tsconfig.json b/apps/brunch-agent/tsconfig.json index 1f44fa66022..adb88fa90e5 100644 --- a/apps/brunch-agent/tsconfig.json +++ b/apps/brunch-agent/tsconfig.json @@ -17,5 +17,5 @@ "skipLibCheck": true, "isolatedModules": true }, - "include": ["src", "test", "*.config.ts"] + "include": [".pi", "src", "test", "*.config.ts"] } diff --git a/apps/brunch-agent/turbo.json b/apps/brunch-agent/turbo.json index 4db37dc9c52..4d01ac0c4a7 100644 --- a/apps/brunch-agent/turbo.json +++ b/apps/brunch-agent/turbo.json @@ -28,6 +28,7 @@ "codegen", "^build", "@hashintel/brunch-agent#build", + "@hashintel/brunch-agent-plugin-dafny#build", "@hashintel/brunch-agent-plugin-gherkin#build", "@hashintel/brunch-agent-plugin-sdcpn#build" ], diff --git a/apps/brunch-agent/vite.config.ts b/apps/brunch-agent/vite.config.ts index e5d3a94ef1b..f0faeeaf4b9 100644 --- a/apps/brunch-agent/vite.config.ts +++ b/apps/brunch-agent/vite.config.ts @@ -1,7 +1,7 @@ import { flue } from "@flue/vite"; import { defineConfig } from "vite"; -import { localChatListen } from "./src/local-dev-origins.ts"; +import { localChatListen } from "./src/http/local-origins.ts"; // No @vitejs/plugin-react: the flue plugin's dev controller owns the whole // request space and hands every request to app.ts, with no fall-through to diff --git a/libs/@hashintel/brunch-agent/AGENTS.md b/libs/@hashintel/brunch-agent/AGENTS.md index e09a70a9a67..ef91c69a29a 100644 --- a/libs/@hashintel/brunch-agent/AGENTS.md +++ b/libs/@hashintel/brunch-agent/AGENTS.md @@ -24,77 +24,74 @@ from this file. ## Mission contract -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 draft and bounded side quest described below. +When work starts on a branch, state these six things in [`MISSION.md`](MISSION.md) and copy them into the branch/PR description. These sections are required semantic addresses, not a ceiling on detail or a fixed template budget. - **Imperative** — what must become true, and why now. - **Throughline** — the real entrypoint or boundary being changed. -- **Proof** — the observable evidence that would establish progress, and the claim it does not - make. A path, a connected skeleton, and a discharged contract are different completions. +- **Proof** — the observable evidence that would establish progress, and the claim it does not make. A path, a connected skeleton, and a discharged contract are different completions. - **Constraints** — the few already-earned truths that must stay true. -- **Fog-line** — uncertainty that current evidence cannot yet decide between consequential - alternatives, and must not be designed past. Clarifying intent is not clearing terrain. Capture - unresolved flags here: why they matter, what they constrain, and what would re-enter them. - Running the path may lengthen this list; that is calibration, not regression. +- **Fog-line** — uncertainty that current evidence cannot yet decide between consequential alternatives, and must not be designed past. Clarifying intent is not clearing terrain. Capture unresolved flags here: why they matter, what they constrain, and what would re-enter them. Running the path may lengthen this list; that is calibration, not regression. - **Stop or reorient** — evidence that invalidates or changes the route. -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. - -### One live mission, next-concerns draft - -[`MISSION.md`](MISSION.md) is the only execution authority. Agents and humans implement against it. - -[`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 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 -on it, same as an ADR. +The six sections are the live mission contract. Keep a short **Status** header (live / accepted) above the contract and a closing **Deferred** section pointing into the future planning record. + +Preserve known precision whenever it changes builder behavior, scope, proof, risk, or handoff. Nest cold-start reads, boundary crossings, risks and assumptions, oracle-bound acceptance leaves, guarded invariants, layered verification, cross-cutting obligations, expected touched paths, and readiness-ratchet sections under the six semantic addresses when earned. Compact trees and flow diagrams are welcome when they make topology or sequencing more legible; omit unearned symmetric filler. + +Every final proof leaf in a live mission or side quest must name a credible oracle: an exact test or command, fixture, artifact inspection, human witness, or adjudication that can distinguish the claimed result from mere presence. A provisional mission draft may instead mark `ORACLE GAP` and state what must resolve it, but that gap must close before the draft is cut with that leaf as a claim. + +### Throughline proof, readiness gate, and stratum closure + +- **Throughline proof** is the smallest deployed end-to-end path showing that a capability crosses the real product boundary. +- **Readiness gate** is the decision after that path works: enumerate the lateral obligations now exposed, decide which are required to trust the current visible capability, and identify which first become load-bearing for the next visible product advance. +- **Stratum closure** completes breadth, fidelity, invalid-state, durability, identity, failure, and oracle obligations across one named contract layer and accepted scenario or peer set. + +A vertical tracer does not automatically require horizontal completion. Close an obligation now when the current visible claim would otherwise be false or unsafe. Carry it only when the next visible product mission is its first real consumer, and name that owner, re-entry gate, and oracle; “later” is not a disposition. + +Use the recursive operating model: + +```text +survey the real territory, not only its maps +→ establish a working line of communication, transport, and evidence +→ stage a dependable camp/base by closing the contract stratum the line has made load-bearing +→ launch the next survey and throughline from that stronger departure point +``` + +Terrain claims require inspection or probes at the real production or deployed boundary. A working line crosses entry to visible exit with the least mechanism that carries product data, control, evidence, and failure. A dependable base closes only the earned coverage, identity, durability, recovery, observability, and oracle obligations required by accepted consumers. Never silently treat a provisional line as a hardened departure base, and do not fortify every adjacent contract merely because one route exposed it. + +This is an expeditionary posture, not a defensive one. Survey only until the next consequential and reversible move is warranted. Once downside is bounded or explicitly accepted, advance; uncertainty is terrain to reduce through action, not a reason to hold position. Stage only the base the next operation needs, not the safest or most complete base imaginable. + +### One live mission and bounded planning surfaces + +One Linear issue = one Graphite branch = one GitHub PR, and only one live mission may exist on that branch. [`MISSION.md`](MISSION.md) is the sole execution authority; agents and humans implement only against it. + +Only three additional planning or control surfaces are permitted: + +1. [`MISSION.next.md`](MISSION.next.md), the compact canonical future spine, shared frame, cross-mission constraints, and unallocated-backlog index. +2. Linked provisional drafts under [`docs/mission-drafts/`](docs/mission-drafts/), which preserve detailed cold-start context for named future clusters under the [draft authority and lifecycle rules](docs/mission-drafts/README.md). +3. `SIDE_QUEST.md`, when present, as the one temporary user-authorized experiment or remediation inside the live mission. + +`MISSION.next.md` and its linked provisional drafts form the combined future planning record. They are not execution authority, do not create concurrent missions, and must not be implemented before conversion into `MISSION.md`. Give every planning item one authoritative planning home; the compact spine may carry a concise summary and link, but must not duplicate the detailed contract. Keep each hypothesis, observation, accepted decision, rejected alternative and reason, re-entry condition, question, named mechanism, constraint, fog item, stop condition, scenario class, and evidence source at the fidelity needed for a cold-start builder. Do not rely on a transcript as the surviving record. + +A side quest is legitimate only when live-mission evidence exposes a bounded set of concrete residual failures whose investigation helps close that mission or informs named future clusters. It must state its relationship to the live mission, imperative, throughlines, oracle-bound 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 affected future-planning homes and in any mission evidence it produced, then remove the active file before archiving the mission. A documentation-only remediation that produces no separate implementation or evaluation evidence records its oracle-bound close audit in the canonical future-planning record rather than inventing another evidence document. + +### Conversion and lifecycle + +Promotion is re-evaluation and conversion, never a rename or wholesale promotion. The ordered conversion and archival procedure lives in [`docs/mission-drafts/README.md`](docs/mission-drafts/README.md#lifecycle). + +The always-loaded invariants are: keep exactly one live mission; return every item omitted from a cut to the combined future planning record at full fidelity; remove the consumed draft so it cannot remain duplicate quasi-authority; and compare every affected planning file before and after with no unexplained loss or duplication. A current mission's **Deferred** items belong in that record and must not be silently dropped or superseded. + +### Decision integrity across handoffs + +These rules exist because Mission 4 lost its design between the owner conversation and production: each handoff summarized the previous summary, an evaluator narrowed an accepted wording, and prompts were then rewritten to satisfy the evaluator. The record is [`docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md`](docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md). + +1. **Current-decision promotion.** When the owner accepts a decision that changes the live mission's implementation or proof, amend `MISSION.md` before any further delegation. `MISSION.next.md`, drafts, evidence, ledgers, and transcripts never substitute for current authority. +2. **Authority amendment before implementation.** The owner reviews and accepts a material recut; that authority change is committed on its own before dependent product or evaluation work begins. Never combine recut, implementation, instrument freeze, or close in one transformation or one commit. +3. **Authority-preserving handoff.** A handoff that translates accepted semantic content, architecture, interaction policy, proof interpretation, or a frozen instrument names the protected source, each production destination, the permitted semantic deltas, and the unresolved choices. An unlisted semantic delta is a stop condition, not a judgment call. Mechanical corrections inside an owner-approved envelope with stated bounds and stop conditions may be batched without a per-change gate. +4. **Oracle non-authority.** An oracle may falsify an implementation or a claim; it may not redefine policy, architecture, or interaction semantics. An operationalization stricter than the accepted wording is an owner decision, and prompts are never rewritten to mirror a checker. +5. **Scoped experimental verdicts.** Every experiment adjudication states which decisions its evidence may update and which remain owner-held. Failure of one implementation mechanism does not select another architecture. +6. **Rationale before disposal.** Before a workbench or draft holding the only explanation of a surviving decision is deleted, the surviving rationale is preserved under `docs/evidence/` with adopted and superseded portions marked, and the complete artifact is pinned by commit. Do not copy whole stale workbenches forward. +7. **Status is present tense.** `MISSION.md` Status carries only the current state and pointers; campaign chronology lives in evidence. +8. **Close by external acceptance.** Where closure, witness acceptance, handoff selection, or a paid ceiling is owner-reserved, an agent prepares the packet and stops. It records acceptance only after the owner has performed that gate. ## Correctives @@ -132,10 +129,8 @@ on it, same as an ADR. - **Linear and GitHub writing:** follow [`docs/agents/issue-writing.md`](docs/agents/issue-writing.md) whenever creating or editing an issue, pull request, or comment. -- **Topology gates** (enforced by tests): plugins never import - `@hashintel/brunch-agent/prompts`; transport packages never depend on a binding; bindings - depend inward on core; plugins depend only on core. Evaluation answer keys stay on the - evaluation side, never inside interviewee or elicitor inputs. +- **Plugin scope:** each plugin pairs one reusable domain typology with one target formalism; it may name concepts from that typology but never facts or nouns from a concrete domain, organization, situation, or scenario. +- **Topology gates** (enforced by tests): core and plugins expose Flue-native production resources through dedicated `./flue` subpaths; plugins depend inward on core and never on bindings; transport packages never depend on a binding; suspended code lives under a package's `src/_suspended/` and is never mounted; bindings translate generalized capture machinery into the selected substrate. Evaluation answer keys stay on the evaluation side, never inside interviewee or elicitor inputs. - **Posture:** prototype · stakes high — persisted capture data and merge gates must fail loudly, never corrupt silently · horizon: current milestone. - **Flue:** when adding state, a loop, a route, or a test harness, consult diff --git a/libs/@hashintel/brunch-agent/CONTEXT.md b/libs/@hashintel/brunch-agent/CONTEXT.md index d25e2356bc6..a963b301204 100644 --- a/libs/@hashintel/brunch-agent/CONTEXT.md +++ b/libs/@hashintel/brunch-agent/CONTEXT.md @@ -1,200 +1,173 @@ # Brunch — domain language -Vocabulary for the brunch elicitation system: an architecture generalizing agentic interviewing -against pluggable elicitation targets. - -## Shells - -**Substrate** — the agent framework the system is built on (Flue), including deploy target, -storage-port implementation, artifact delivery, and model/provider. -_Avoid_: harness (for Flue), platform, host. - -**UI** — whatever affords user interaction: rendering, input, reply transport. Not bound to -GUI/TUI; a chat channel qualifies. -_Avoid_: host, frontend, client. - -**Harness** — the generic capability layer: mechanism and orchestration (the conversation loop, -the `ask` API, capture envelope, issue queue, sweep bookkeeping). Injected into plugins as a -narrow context; never owned by them. -_Avoid_: kernel, core (as a prose shell name; the package path `packages/core` is exempt). - -**Plugin** — the innermost shell: target-defining policy, one per **target formalism** and never -per domain. Authored as cells under harness-owned **keys**; receives capabilities by injection; -mechanism stays in the harness. -_Avoid_: extension, pack (a pack is a unit within a plugin). - -**Binding** — the substrate-facing adapter implementing the harness's named substrate-capability -list (tool registration, instruction assembly, persistent state, affordance emission, -suspend-for-reply, private model call) in one substrate's dialect. One per substrate; the harness -imports no substrate, a binding imports both. -_Avoid_: adapter, integration, wrapper. - -## Sessions and durability - -**Target formalism** — the artifact family a plugin projects into (Gherkin, SDCPN, assurance -arguments, BPMN); the unit a plugin is written for. -_Avoid_: target-domain; bare "target" where family vs instance is ambiguous. - -**Domain** — the operational system the expert knows and the model describes (a packaging line, a -truck fleet). Unknown before the conversation; discovered during it; never a plugin unit, key, -row, or noun in a plugin definition. -_Avoid_: target-domain, use case. - -**Target-document** — the durable unit sessions attach to: one target formalism, its capture -store, and its session history. Its authoritative state is the capture store plus session logs, -never the rendered artifact (renders are derived and disposable). Endures independently of any -session; completion is a derived status, not a write gate. -_Avoid_: spec, workpiece, case, target-output. - -**Session** — one substrate conversation: the full log of entries (user, agent, tool calls, -injected state). Per-session state is exactly the evidence log, the swept high-water mark, and the -pending-affordance slot. Sessions go quiet rather than close; any session is resumable. -_Avoid_: sitting, conversation (as a distinct concept). - -**Capture store** — the durable, session-independent truth of a target-document: captures, -issues, events. Written only by atomic sweep application; statuses and projections derive from it -at read time. - -**Re-entry briefing** — the state message injected when a session resumes after the world moved: -computed facts only (unswept tail, world-moved delta, open issues, pending affordance). Authored -on behalf of the user in the transcript, distinguished from true user entries in the data model, -and never citable as capture evidence. -_Avoid_: sync message, forced re-sweep. - -## Interaction - -**Affordance** — a structured interactive element (question form, choice strip, questionnaire) -emitted into the stream as a rendered enhancement. Not a state machine; its payload is session -evidence like any other entry. -_Avoid_: exchange, exchange pair, terminal. - -**Capture** — extraction of structured evidence (envelope plus plugin-typed payload) from session -entries. Produced by sweeps, never written directly during conversation. -_Avoid_: extraction, harvest. - -**Sweep** — an idempotent pass over a settled range of session entries that produces captures; -re-sweeping never double-captures. (`apply-sweep` in the capture store names only the atomic -storage half.) - -**Settlement** — the agent-judged event marking a range of conversation ready to sweep. Always -range-level. -_Avoid_: exchange completion. - -**Interpretation render** — the harness-owned affordance showing current captured state. The -harness frames envelope semantics; the plugin's renderer supplies the content view, with a -plain-JSON default. - -## Envelope and packs - -**Intermediate representation (IR)** — the elicited conceptual model a target-document -accumulates, the middle of three registers: typed **assertions** (active captures) fold, by a -pure plugin-declared fold, into the **model** (node instances with slot states), which -**projections** consume without rereading the transcript. A derivation, recomputable from active -captures, never a persistence surface. Defining a plugin's IR means writing its kind and -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 -time. - -**Evidence span** — a capture's provenance: a **quoted excerpt** (primary, model-facing citation) -plus a **pointer** (session id + entry range). Anchors only on true user and user-affordance -entries. - -**Epistemic status** — `explicit | inferred | tentative | defaulted | external-lookup`: how a -capture's content relates to what the user said. Distinct from confidence; excluded from capture -identity; one per capture. - -**Grade** — how narrow a slot value's interpretation space is. Per-slot; distinct from confidence -(`firm | hedged | speculative`). - -**Basis** — the provenance carrier for non-user-grounded captures (`declared-default` or -`documented-transformation`), required exactly when epistemic status is `defaulted` / -`external-lookup`; structurally exclusive with evidence spans. - -**Absence state** — a first-class capture value where an answer would be: `unknown-to-user | -not-yet-decided | not-applicable | explicitly-absent | declined | deferred`. Never null. - -**Supersession** — explicit correction, single-hop over active heads only: the creation-time -`supersedes` link (sweep-time) and the resolution record (issue-time). Superseded captures stay -visible. - -**Resolution record** — the capture-store event that alone closes a `conflicting` issue (and, with -no successor, expresses retraction). Must cite the true user's utterance. - -**Issue** — typed, stored backpressure: `missing | ambiguous | conflicting | invalid | -unsupported | unmapped | low-confidence`. Produced by plugin ops (payload level) or the harness -(envelope level). Closes only explicitly. - -**Advisory** — a computed, ephemeral, non-blocking fact surfaced to the agent. Never stored; never -gates anything. - -**Pack** — a unit within a plugin: **ProjectionPack** (`project` + `validate`, optional -`reconcile`, annotated shapes, typed loss reports). The guidance-and-runbook cells replaced the -retired ElicitationPack. - -**Demand row** — one row of a plugin's must-know table: a slot on a kind, its required precision, -whether "not applicable" is accepted, and why the model needs it. Kind-level only. - -**Pattern** — a discretionary, kind-indexed heuristic under a plugin's `patterns` key: a machine -trigger (declared kind, optionally one unsatisfied demanded slot), `when` text, and an `ask` -question. Never names a domain. - -**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. +Vocabulary for Brunch, an elicitation system in which a universal core and formalism-specific plugins compose one model-facing agent that interviews a person, maintains a recoverable account of what they know, and constructs a target representation from it. -**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 -what to notice; an **anchor** is a judgment to check against. Each guidance key has exactly one. +## Language -**Posture** — the interaction stance `kickoff` produces from the expert's appetite, time, intended -use, and tolerance for proposed assumptions. Varies the trajectory; continuously re-read; never -stored. Not a state machine. +### Package authority -**PluginContext** — the narrow injected context through which a plugin receives harness -capabilities. Its entire world at runtime; the four operations stay pure. - -**Storage port** — the harness-defined contract for the capture store (atomic sweep application, -envelope invariants as store-level refusals), implemented by the binding. Plugins are -storage-blind. In code its type is `CaptureStore` (`packages/core/src/capture-store.ts`). - -## Simulation and evaluation - -**Situation pack** — the interviewee-side bundle defining a user-to-be-simulated: situation, -scenario, persona. Private to whoever plays the user; never authored from or shaped to mirror the -IR. -_Avoid_: fact pack, persona pack. - -**Answer key** — the modeller-side list of facts the reference net needs. Sits on the -elicitor-team side of the wall; never part of the situation pack. +**Core**: +The universal authority: context-, domain-, editor-, and formalism-independent elicitation semantics, the always-on prompt, the `elicitation` capability, and the evidence contracts. Owns nothing that names a formalism or a concrete situation. +_Avoid_: harness, kernel -**Walking skeleton** — a build proving a transport or integration end-to-end on the real substrate -with stubbed internals. +**Plugin**: +A contribution bundle pairing one **domain typology** with one **target formalism**: the prompts, skills, tools, and mounting it has earned, and nothing for symmetry. It names concepts from its typology, never facts or nouns from a concrete **domain**. +_Avoid_: extension, pack, formalism-only plugin, symmetric inventory -**Logic-prototype** — a prototype locking down mechanism semantics in isolation, without the full -host substrate. +**App**: +The directive-marked registration and host-composition point that selects core and plugin contributions, carries transport and client-tool results, and owns deployment diagnostics. It owns no modelling semantics. +_Avoid_: host, shell, server (as a shell name) + +**Binding**: +The substrate-facing adapter that implements core's evidence mechanics in one substrate's dialect. Core imports no substrate; a binding imports both. +_Avoid_: adapter, integration, wrapper + +**Transport**: +The wire projection between a **UI** and the agent: ingress validation and reply encoding, never a binding or substrate import. + +**Substrate**: +The agent runtime the system is built on (Flue), including its skill packaging, tool registration, persistence, and model provider. +_Avoid_: harness (for Flue), platform, host + +**UI**: +Whatever affords user interaction: rendering, input, and reply capture. A chat panel qualifies. +_Avoid_: frontend, client, host + +### Model-facing primitives + +**Prompt**: +Always-present content carrying identity and the invariants that must bind for the whole mounted lifetime of a contribution. Core returns the universal prompt; a plugin may add a compact **append**. +_Avoid_: system prompt fragment, instructions (as a unit name) + +**Append**: +A plugin's optional always-on prompt contribution: scoped specialization and pre-activation guardrails, earned only by invariants that apply across all of its skills. Never a second persona and never skill procedure. + +**Skill**: +Reusable procedure and judgment for a recognizable job or capability, disclosed progressively: one catalog line, then instructions on activation, then resources on demand. A skill may direct activation of another skill. +_Avoid_: runbook, loader, workflow + +**Capability skill**: +A skill whose method is meaningful independently of any job, such as `elicitation`. Core's contributions are capability skills. + +**Job skill**: +A skill that accomplishes one recognizable user outcome, such as `sdcpn-modelling`, owning its workpiece, target review and revision, construction, checks, and tool orchestration, and activating capability skills when it needs them. A plugin contributes the smallest set of job skills its real jobs earn. +_Avoid_: task skill, lifecycle skill, one-skill-per-plugin + +**Resource**: +A supporting file packaged inside a skill and read only when its branch requires it. A **reference** carries detailed teaching; a **template** carries a recording shape. +_Avoid_: include, transclusion + +**Tool contract**: +The semantics and constraints of one executable operation: inputs, locally expressible preconditions, outputs and failures, and exactly what evidence a result establishes. Ownership follows semantic capability, not where execution happens. +_Avoid_: function, action + +**Disclosure state**: +How far a contribution has reached the model: always present, catalogued, activated, resource-read, or callable. Independent of package authority and of primitive type. + +### Elicitation + +**Elicitation**: +Acquiring and improving an epistemically responsible account from a person through adaptive conversation: recognizing cues, choosing the next probe, handling correction and contextual variation, preserving authorship and uncertainty, and judging when evidence suffices. Excludes target review, target mutation, construction, and tool execution. +_Avoid_: interviewing (as the whole), intake, questionnaire + +**Domain typology**: +The reusable subject-matter concepts and recurring situations a plugin uses to recognize and investigate what may matter, such as operational processes or software behavior. Paired with a **target formalism**; never contains facts from a concrete **domain**. +_Avoid_: domain, target-domain, use case, scenario + +**Target formalism**: +The artifact family a plugin constructs into, such as SDCPN or Gherkin. The representational half of a plugin's pairing, not the plugin itself. +_Avoid_: target-domain, bare "target" where family and instance are ambiguous + +**Domain**: +The concrete system or situation the person knows and the model describes. Unknown before the conversation and discovered during it; its facts populate the **workpiece**, never a plugin. +_Avoid_: domain typology, use case + +**Register**: +One of five semantic addresses classifying what elicitation guidance does: Directives, Recognition, Operations, Coverage, Verification. Registers are not phases, question order, skills, schemas, or file topology. + +**Workpiece**: +The recoverable, domain-primary, cold-readable account the agent maintains during elicitation and revision and consumes during construction. Each operational claim has one authoritative home, with its evidence and epistemic treatment beside it. +_Avoid_: runbook IR, IR, intermediate representation, target-document, spec, requirements graph + +**Epistemic annotation**: +A distinction attached to a workpiece claim where it carries information: expert evidence, working account, agent inference, assumed, unknown, not yet asked, declined, deferred, conflict, correction, contextual coexistence, omitted, loss. Optional labels, not mandatory fields or a closed type system. +_Avoid_: slot, grade, typed claim + +**Correction**: +A later account that replaces an earlier one, leaving one active claim with enough history to explain the change. Distinguished from **contextual coexistence** before any reconciliation. + +**Contextual coexistence**: +Differing accounts that each hold under a selecting condition such as person, time, mode, direction, or policy regime. Both remain active beside their conditions; never averaged. +_Avoid_: conflict (when the selector is known) + +**Unknown**: +Asked, and the person does not know. Distinguished from **not yet asked**, which is relevant and identified but not yet addressed. Absence alone establishes neither. + +**Construction**: +Selecting and recording a target representation from the current workpiece through mounted tools. Construction may infer a representation from recorded meaning; it may not invent operational facts. Losses it opens are construction findings, not new evidence. +_Avoid_: generation, realization, projection (reserved for future automatic traceable projection) + +**Construct-only execution**: +The runtime branch in which the workpiece is the complete input and no interview occurs; a consequential gap is reported as a re-entry question, never asked or invented. + +**Evidence level**: +One of three non-collapsible claims about a constructed artifact: tool-schema acceptance, agent-reviewed structural correspondence, and behavioral execution or stronger analysis. Report every level reached; none implies the next. + +### Evidence and capture + +**Session**: +One substrate conversation: the full log of user, agent, tool, and injected entries. Sessions go quiet rather than close. +_Avoid_: sitting, conversation (as a distinct concept) + +**Capture**: +Mechanically extracted source evidence from a settled range of session entries: an immutable, quote-anchored, domain-opaque envelope. Produced only by a sweep and never written during conversation. +_Avoid_: extraction, harvest, typed claim + +**Capture envelope**: +The domain-free wrapper around an opaque payload: minted id, evidence spans, epistemic status, confidence, value or absence, and one supersedes link. Status derives at read time. + +**Evidence span**: +A capture's provenance: a quoted excerpt plus a pointer to the session entry range. Anchors only on true user entries. + +**Capture store**: +The durable, session-independent record of captures, issues, and events, written only by atomic sweep application. +_Avoid_: knowledge store, database (as a concept) + +**Sweep**: +A harness-owned, idempotent pass over a settled entry range that produces captures; re-sweeping never double-captures. The interviewer neither receives nor schedules it. + +### Suspended concepts + +**Affordance**: +A structured interactive element such as a question form or choice strip emitted into the stream, whose reply is session evidence. Suspended; re-enters only with a complete vertical path from model invocation to rendered reply. +_Avoid_: exchange, terminal + +**Structured question**: +A core-owned operation for single-select, multi-select, or questionnaire forms, rendered by a **UI** without acquiring semantic ownership. Suspended re-entry candidate. + +**Settlement**: +The agent-judged event marking a range ready to sweep. Suspended with the interviewer-scheduled sweep. +_Avoid_: exchange completion + +**Observer**: +A hypothetical background consolidation mechanism over captured evidence. Absent by default; re-enters only under observed foreground revision strain. +_Avoid_: fold, background agent + +### Evaluation + +**Situation pack**: +The interviewee-side bundle defining a simulated person: situation, scenario, persona. Private to whoever plays the user and never shaped to mirror the workpiece. +_Avoid_: fact pack, persona pack + +**Answer key**: +The modeller-side list of facts a reference model needs. Stays on the evaluation side, never inside interviewer or interviewee inputs. + +**Control**: +The immutable comparison population: Mission 3's frozen prospective campaign and its exact source revision. Never written to, relocated, or aggregated with later runs. +_Avoid_: baseline (when it would be modified or extended) + +**Frozen instrument**: +The exact committed prompts, skills, resources, built bundle, case, ruler, and protocol whose hashes fix a campaign. Changing one byte is a new instrument. + +**Campaign**: +A versioned protocol run of the frozen instrument through the production agent, retaining raw traces, manifests, and invalid members separately from graded workpieces. diff --git a/libs/@hashintel/brunch-agent/MISSION.md b/libs/@hashintel/brunch-agent/MISSION.md index 7f9e316eb9d..468aad51347 100644 --- a/libs/@hashintel/brunch-agent/MISSION.md +++ b/libs/@hashintel/brunch-agent/MISSION.md @@ -1,199 +1,7 @@ -# Mission 3 — structurally typed runbook to headless PN +# No live Brunch mission on this branch ## Status -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. +**Mission 4 closed by owner adjudication on 2026-09-03.** Its accepted implementation, bounded evidence, observed S4 failure, missing full-run candidate, and deferred concerns are preserved in [`4-core-plugin-elicitation-proof-of-life.md`](docs/mission-archive/4-core-plugin-elicitation-proof-of-life.md) and [`mission-4-closure-and-deferral-2026-09-03.md`](docs/evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md). -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 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. - -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. - -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. - -## Throughline - -One headless pass through the Mission 1 production door: - -`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` - -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. - -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. - -## Proof - -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 - -- 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 user-facing docs only where exercised behavior changes. - -## Fog-line - -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 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 second server instead of `createFlueClient` against the live door; -- the adapter grows a dependency on core, binding, or plugins; -- 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 - -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. +This file is a closure pointer, not execution authority. No implementation may begin here until an owner-authorized issue/branch mission is re-evaluated and installed. Future planning lives in [`MISSION.next.md`](MISSION.next.md). Voice reconciliation starts from [`mission-4-voice-integration-handoff.md`](docs/evidence/implementations/mission-4-voice-integration-handoff.md) without reopening Mission 4. Restacked commit navigation and the content-hash evidence rule are recorded in [`mission-4-final-restack-provenance-2026-09-03.md`](docs/evidence/decisions/mission-4-final-restack-provenance-2026-09-03.md). diff --git a/libs/@hashintel/brunch-agent/MISSION.next.md b/libs/@hashintel/brunch-agent/MISSION.next.md index 9a15cad8dba..fc8c2c63533 100644 --- a/libs/@hashintel/brunch-agent/MISSION.next.md +++ b/libs/@hashintel/brunch-agent/MISSION.next.md @@ -1,600 +1,501 @@ -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. +# Brunch future mission spine -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. +> Canonical future-planning spine, shared frame, and backlog index only. This file is not execution authority and authorizes no implementation. [`MISSION.md`](MISSION.md) is currently a closure pointer; a future owner-authorized cut must replace it with the sole live authority. Detailed provisional clusters are context repositories, not missions; re-evaluate and convert one into `MISSION.md` before acting. -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. +This spine and its six linked drafts form one future-planning record. Keep each consequential meaning in one authoritative planning home: shared contracts and unallocated concerns live here; mission-specific detail lives in its draft. A spine pointer is not a second contract. Material omitted from a future cut returns to this record at full fidelity, and the consumed draft is removed. -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`. +## Current authority and accepted spine -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. +Mission 4 closed on this branch by owner adjudication on 2026-09-03. The accepted implementation is the independent core `elicitation` capability, SDCPN job-skill activation, and core/plugin/app responsibility split. The bounded evidence is narrower than the pre-registered campaign claim: Vestera and Data Centre activated correctly before substance, and S3 correctly refrained; S4 preserved the unresolved rule but did not activate elicitation, so the frozen campaign stopped before Industrial Gas. No `3/3` claim or full-run conversation/workpiece candidate exists. The owner judged immediate review-to-elicitation switching a nice-to-have at this boundary and deferred it. See the [closure decision](docs/evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md), [campaign adjudication](docs/evidence/evaluations/mission-4-proof-of-life-v2/final-adjudication.md), and [Voice integration handoff](docs/evidence/implementations/mission-4-voice-integration-handoff.md). The parallel deployment branch's earlier Mission 4 transition remains historical and is not imported as independent acceptance. -```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: +A future Mission 4 close-out addendum requires its own issue, branch, PR, and mission authority. It may stack on this closed branch and own broader reliability/hardening if warranted, browser parity, fixture/seed promotion contracts, topology-neutral case allocation, contract/readiness sweeps, archive subtraction, and Mission 8 preparation. It also owns the observed S4 report-versus-immediate-ask decision unless a later numbered mission first makes it load-bearing: re-enter only when a real review must continue immediately or repeated gap-only reports create visible friction; preserve S3 restraint while testing S4 activation and asking under a fresh instrument. Its exact issue/name and minimum scope remain owner decisions; do not create another Mission 4 draft. -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. +Two successor missions are now independently cuttable from Mission 4 under separate issue, branch, PR, worktree, and mission authority. Mission 5 retires the Voice transport uncertainty: finalized speech enters canonical Flue directly and canonical Brunch output reaches TTS without the AI SDK chat composer or a secondary generative simplifier. Mission 6 retires the workpiece/projection uncertainty: one deliberately prepared, honestly labelled fixture joins canonical conversation, session history, Markdown workpiece, and Petrinaut document; one browser-backed read/write change saves and resumes across tabs. Neither requires a Mission 4 full-run candidate, and neither is the other's prerequisite. -“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: +The earlier capture-backed provenance, automatic-projection, revision, and optimisation drafts remain later readiness/product advances. They are renumbered around the historical Mission 8 deployment track rather than forcing the two uncertainty-retiring tracers to inherit its unproved remote boundary. ```text -conversation evidence → workpiece meaning -workpiece meaning → projection decision → SDCPN element -workpiece revision → bounded net change +M4 closed — core/plugin elicitation pattern accepted; S4 transition and full-run candidate deferred +M4+ optional successor — broader hardening or source promotion only under separate authority +M5 direct Voice/Flue — one finalized spoken turn, canonical streamed reply, cancellation, and reopen +M6 resumable fixture tracer — conversation → Markdown workpiece → Petrinaut read/write → cross-tab resume +M7 capture-backed review — close selected-pair provenance breadth and visible why/refusal +M8 deployment handoff — historical branch stopped after local application proof, before infrastructure deployment +M9 automatic projection — broaden the proved fixture seam to repeatable traceable projection of one meaningful region +M10 revision — ship bounded authorized reviewer revision and a scoped patch +M11 optimisation — ship an accepted optimisation handoff after its consumer contract exists ``` -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. +Every numbered product mission after the proof-of-life exception must pass the **product-manager litmus**: a product manager who did not watch the work must be able to notice that the product materially moved forward. Each mission therefore states, in its draft's visible-product-advance section, a release-note sentence, a demo script a product manager can run without an engineer, and the thing that was impossible before. Snapshots, manifests, event ledgers, and negative controls are oracles that belong in the evidence sections; they are not the visible advance. A mission is complete at its readiness gate, when the demo script works for the named scenario, not at the first green throughline tracer, which is an internal milestone inside the mission. Mission 5 names the Voice/Flue surface; Mission 6 names the stable fixture and browser Petrinaut document; Missions 7, 9, and 10 name the Petrinaut Brunch panel. Because Mission 8 stopped before remote deployment, those panel missions must name the deployment posture available at cut time, and a locally run panel is acceptable for the demo; a product-manager-noticeable claim must never depend on infrastructure that does not exist, while remote durability obligations stay in their readiness gates. Architecture, schema repair, fixtures, evaluation, rehearsal, and spikes may support the advance but cannot be the sole outcome. Parallel work means separate issue, branch, PR, worktree, and mission authority; it never means multiple live missions here. -## Confidence map +## Successor mission précis -### High confidence — observed or owner-settled +### M5 — Speak directly to canonical Brunch -- 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. +Tracker projection: [FE-1574](https://linear.app/hash/issue/FE-1574/let-voice-speak-through-canonical-brunch-conversations). -### Medium confidence — plausible first mechanisms, not yet proven +A finalized spoken answer enters Flue exactly once and canonical Brunch text streams directly to visible Voice output and TTS, without the AI SDK chat composer or a secondary generative simplifier. This can be cut immediately and independently on the Voice reconciliation worktree. **Visible/usable proof:** [one spoken turn, cancellation, canonical snapshot, and reopen](docs/mission-drafts/5-direct-voice-flue-transport.md#throughline-proof-floor). -- 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. +### M6 — Prove the Markdown/workpiece/Petrinaut loop -### Low confidence — must alter the next move +Tracker projection: [FE-1575](https://linear.app/hash/issue/FE-1575/resume-one-brunch-workpiece-and-petrinaut-document-across-tabs). -- 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. +One honestly prepared fixture links a canonical conversation, session history, Markdown workpiece, and Petrinaut document; Brunch updates the workpiece, performs one meaningful browser-backed document change, saves, and resumes from a second tab. This can be cut immediately and independently of Voice. **Product-manager litmus:** Brunch edits the net you are looking at from the conversation, and the work survives closing the tab. Demo: open the demo fixture, say one new thing about the process, watch the net change, save, reopen in a second tab and continue. Previously impossible: Brunch only produced off-canvas net JSON for manual load. Complete at the [readiness gate](docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md#readiness-gate-after-the-new-throughline), not at the first green mutation; see the [visible product advance](docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md#visible-product-advance). -## 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 -``` +### M7 — Make the demo net genuinely explainable -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. +Tracker projection: [FE-1573](https://linear.app/hash/issue/FE-1573/explain-one-prepared-petrinaut-net-from-exact-conversation-evidence), advancing stakeholder outcome [FE-1478](https://linear.app/hash/issue/FE-1478/provide-provenance-from-a-generated-net-back-to-the-requirements-graph). -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. +After M6 proves viability, close exact capture-backed provenance across the selected prepared pair so a reviewer can ask why any consequential visible element exists and receive workpiece meaning, preparation rationale, and exact conversation evidence, or a visible refusal. **Product-manager litmus:** ask why about any element in the demo net and get the original conversation back. Demo: open the prebuilt demo net, pick any element, type its name, read the answer and its quoted evidence; pick the element known to have no support and watch Brunch decline. Previously impossible: nothing connected a net element to what the expert actually said. Complete when every consequential element in the demo net resolves or visibly declines, not when one element resolves; one element is the throughline tracer inside the mission. On 2026-09-03 the one-element cut was judged too small under the litmus and Mission 7 was expanded to the whole demo net rather than folded into Mission 6 or Mission 9; the reasoning is recorded in the [visible product advance](docs/mission-drafts/7-capture-backed-review.md#visible-product-advance). -# Retained teaching, workpiece, and seam backlog +### M9 — Make projection repeatable and traceable -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. +Tracker projection: [FE-1438](https://linear.app/hash/issue/FE-1438/project-an-evidence-backed-workpiece-into-a-traceable-live-sdcpn). -## Universal teaching backlog and baseline-gated edits +Broaden the viable M6 mutation seam into automatic projection of one meaningful workpiece region, with stable identities and derivations that M7's why route can resolve. Close repeat, changed-input, schema, partial-failure, and semantic-correspondence obligations for the named region rather than a whole-net platform. **Product-manager litmus:** Brunch builds a recognisable part of the net itself from the conversation, and can still explain every piece it built. Demo: from the demo workpiece, ask Brunch to model the named region, watch a non-empty region appear in the panel that matches what was discussed, then ask why about one generated element. Previously impossible: every net in the demo was prebuilt by a person. Provider-schema repair, the transaction probe, and the batching decision are internal to this mission and are not the advance; see the [visible product advance](docs/mission-drafts/9-traceable-projection.md#visible-product-advance). -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. +### M10 — Revise meaning without collateral rebuilding -Candidate moves present in the research but without a reliable current home include: +Tracker projection: [FE-1394](https://linear.app/hash/issue/FE-1394/revise-one-traceable-net-region-through-targeted-reviewer-elicitation). -- 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. +A scenario-authorized reviewer supplies focused new evidence; Brunch preserves prior support, creates an inspectable workpiece revision, and applies a bounded net patch, justified widening, or refusal. **Product-manager litmus:** a second person corrects the model in conversation and only the relevant part of the net changes. Demo: a reviewer challenges one modelled fact in a few turns, sees that region update with the correction attributed to them, sees the rest of the net untouched, and sees Brunch decline a second change it is not entitled to make. Previously impossible: changing the net meant regenerating or hand-editing it. Complete when both the accepted correction and a visible refusal or qualification appear in the same demo; see the [visible product advance](docs/mission-drafts/10-bounded-reviewer-revision.md#visible-product-advance). -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. +### M11 — Hand an accepted model to optimisation -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. +Tracker projection: [FE-1503](https://linear.app/hash/issue/FE-1503/hand-one-accepted-sdcpn-to-an-optimisation-experiment). -## SDCPN investigation backlog +Only after Chris and Yannis define one concrete consumer contract, broaden the proven path to one selected complete SDCPN and deliver the semantic artifact package in the form they accept. **Product-manager litmus:** Chris and Yannis start an optimisation experiment on a model that came out of Brunch, without asking anyone to reconstruct it. Demo: the handoff package opens in the form they accepted and the experiment begins. Previously impossible: no Brunch output had an external consumer. This mission passes the litmus by construction; see the [visible product advance](docs/mission-drafts/11-optimisation-handoff.md#visible-product-advance). -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: +## FE-1476 product frame -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. +The 2026-08-31 planning session set the historical delivery window as the end of the following week. That date explains the original sequencing; it is not a live schedule lock. The six-beat review/revise story is the minimum integrated floor, not the product or demo ceiling: -Typology decisions remain provisional and strain-gated: +1. Show a completed requirements artifact prebuilt from an earlier elicitation; do not stage a live first interview. +2. A reviewer other than the original expert examines the SDCPN projected from it. +3. The reviewer asks why a visible part was modelled that way and receives its provenance. +4. The reviewer conducts 3–5 focused re-elicitation turns against one section. +5. The net changes accordingly without unrelated rebuilding. +6. The revised artifact is handed to Chris and Yannis for an agreed optimisation experiment. -- 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. +“Requirements graph” is not a settled implementation. Use **evidence-backed workpiece** for the inspectable/exportable semantic account between conversation evidence and SDCPN. The durable relationships are: -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. +```text +conversation evidence → workpiece meaning +workpiece meaning → projection decision → SDCPN element +workpiece revision → bounded net change +``` -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 broader scenario portfolio has not been enumerated. At each mission cut, name the accepted scenarios and contract classes honestly; “all scenarios” means that named portfolio, never every imaginable operational process. -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. +Low-confidence questions must change the next move rather than harden into architecture: the stable granularity and identity of workpiece meaning; whether foreground or any later observer revision preserves correction, qualification, and conflict; whether optional mapping hints help projection or bias synthesis; whether the live provider path preserves nested Petrinaut schemas; whether one region can be revised without unrelated churn; whether a reviewer may supersede meaning or only propose a change; whether net create/save/load supplies sufficient identity; whether compaction preserves workpiece/evidence recovery; and the exact Chris/Yannis optimisation contract. -## Workpiece structure hypotheses and observed strain +### Tracer floor and contract readiness -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. +Use three distinct claims: -Candidate structures remain hypotheses: +- **Throughline proof:** the smallest deployed end-to-end path showing that product data, control, evidence, and failure cross the real boundary. +- **Readiness gate:** after that path works, decide which lateral obligations are now enumerable, which must close for the visible claim to be trustworthy, and which first become load-bearing for the next advancement. +- **Stratum closure:** complete breadth, fidelity, invalid-state, durability, identity, failure, and oracle obligations across one named contract layer and accepted scenario/peer set. -- **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. +```text +survey the real territory, not only its maps +→ establish a working line through the real boundary +→ stage a dependable base by closing the now-load-bearing stratum +→ launch the next survey and throughline +``` -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. +A tracer is not automatically a hardened base, and one green element, region, or correction does not close a mission. Close an obligation now when the current visible claim would otherwise be false or unsafe. Carry it only when the next visible consumer first makes it load-bearing, naming owner, re-entry gate, and oracle. Do not sweep every adjacent contract merely because one route exposed it. -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. +This is an expeditionary posture, not a defensive one. Survey only until the next consequential and reversible move is warranted. Once downside is bounded or explicitly accepted, advance; uncertainty is terrain to reduce through action, not a reason to hold position. Stage only the base the next operation needs, not the safest or most complete base imaginable. -Rejected mechanisms retain their re-entry conditions: +### Cross-mission proof obligations -- 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. +- **Workpiece sufficiency:** a cold reader can identify the objective, reconstruct the operational account, distinguish evidence from inference/assumption, and locate consequential unresolved material. +- **Projection fidelity:** projection consumes the workpiece, not the transcript as primary IR; each consequential region identifies producing meaning and rationale. +- **Evidence provenance:** visible element → current workpiece meaning → mechanically retained exact conversation evidence, without laundering normalized prose into quotation. +- **Revision integrity:** new attributed evidence produces an inspectable revision that preserves prior supported meaning unless explicitly corrected, qualified, split, merged, retired, or retained in contextual conflict. +- **Patch locality:** intended region changes, unrelated identities and behavior remain stable, and legitimate impact widening is reported. +- **Petrinaut semantic acceptance:** canonical schemas accept a non-empty net whose behavior and meaning correspond to the workpiece; parser acceptance alone is vacuous. +- **Deployed interaction quality:** the real panel supports the why operation and focused review without construction vocabulary or background work taking over ordinary turns. +- **Visible failure:** provider-schema failure, unsupported or unavailable projection/evidence, stale observer state if ever admitted, unresolved authority, failed mutation or patch validation, and failed canonical validation stop or visibly degrade the operation; none silently advances canonical workpiece or net state. -## Prior capture/workpiece seam hypotheses and unrun probes +## Shared constraints and standing locks -The prior research compared four relationships; FE-1476 does not erase their evidence: +### Evidence, workpiece, capture, and projection -- **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. +Flue history is the canonical conversation log. Mechanical capture envelopes are immutable, exact-evidence, domain-opaque source records; the foreground Markdown workpiece owns semantic synthesis. Projection consumes the current workpiece. Petrinaut owns canonical net schemas, mutations, parsing, and simulation; Brunch imports or mechanically derives those contracts and never hand-copies their field shapes. -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. +Mission 2 proved an idempotent model-free sweep: one envelope per user utterance, quote equal to source text, payload `{}`. The production runbook path still does not invoke capture. Mission 6 may use an explicitly prepared fixture without claiming capture-backed provenance; Mission 7 is the first planned consumer that turns capture into trustworthy product provenance, so capture must become durable before that claim is made. Task-local JSON is forbidden across any claimed process/task replacement boundary. -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. +Keep these epistemic levels separate: -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. +```text +what conversation evidence supports +what target-formalism guidance suggests might be present +what projection actually represents +``` -# Mission 4 — owner-led runbook and workpiece redesign +Optional SDCPN mapping hints remain advisory, may be absent or plural, identify the prose they concern, and record whether projection accepted, rejected, or deferred them. They neither establish completeness nor copy Petrinaut payloads. -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. +The smallest currently earned provenance seam is current workpiece revision/reference, capture evidence references, net-element ids, and projection rationale. Exact storage and identity shape remain fog for the real tracer. Stable ids must be exercised rather than assumed. Unsupported defaults, stale/partial state, identity churn, repeated projection, and visible partial failure stay explicit. -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. +Do not add a comprehensive process ontology, graph database, universal subject/predicate/value schema, deterministic capture-to-workpiece reducer, full regeneration engine, or typed completion algebra before observed consumer strain earns one. -The candidate throughline is: +### Foreground revision and observer re-entry -```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 -``` +Default revision is one bounded foreground phase-boundary synthesis over the prior workpiece revision plus newly durable reviewer evidence. Reviewer authority is scenario-declared and region-bounded; recency never authorizes overwrite. Resolve correction, qualification, contextual coexistence, conflict, proposal, or refusal before canonical state changes. -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. +The inferential observer is **not planned implementation**. It may re-enter only if foreground synthesis repeatedly causes consequential blocking, loses prior meaning, uses stale state, cannot recover after compaction/restart, or requires unavoidable unbounded history. Promotion would require all of the following evidence: -Candidate evidence obligations: +- two or more settled ranges fold in semantic order into coherent versioned items with valid source excerpts; +- separate `scheduledThrough` and `foldedThrough` marks prevent overlap, retries do not duplicate commits, and result-persistence retry reuses the same candidate rather than rerunning interpretation; +- revision lineage, consolidated result, and folded mark commit atomically; later reinterpretation creates a reviewed revision; +- ordinary foreground turns do not wait, and a forced tail flush makes a short review available before revision commit; +- correction, contextual qualification, and conflict preserve prior supported meaning and expose staleness/failure; +- a cold reviewer can trace every material claim to evidence or explicit inference; optional hints improve projection without biasing consolidation. -- 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. +If admitted, trigger only at a valid settled agent boundary, guard `useAgentFinish` suspensions with pending-affordance state, queue ranges in semantic order, and block canonical revision/projection when retries exhaust. The observer edits evidence-backed workpiece meaning; it never mutates the net. It must not require a second event log, full target ontology, mandatory hint slots, or foreground consultation of every fold. A threshold near 10,000 unscheduled tokens and later regroup cadence are precedents, not locked values. -## Constraints already earned +Mission 10's likely observer barrier, if that mechanism is ever admitted, is a forced tail sweep and queue flush after the 3–5 reviewer turns; the approximate token threshold alone may never fire during a short review. -- 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. +The extraction ladder remains: model-free stub envelopes (proved) → separate cheap quote/opaque extraction or inferential consolidation (unproved) → closed typed claims/plugin catalogs (the Condition 5 failure shape). Advance only when the current consumer proves the thinner rung insufficient. Micro-cognitive subagents for decision/decomposition remain undecided and are neither an observer scheduler nor a general multi-agent architecture. -## Fog-line +### Product and host boundary -- 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. +The production door is Petrinaut panel → AI SDK `useChat`/`onToolCall` transport → long-running Flue `ChatAgent` → Anthropic, with client tools executed through the existing host route. Brunch is a selectable second assistant; stock remains functional when Brunch is absent or unselected. Never splice histories, steal the stock `/api/chat` contract, rewrite the panel onto `@flue/react`, or add a direct canvas/server bypass. -## Stop or reorient +Core owns universal, context/domain/editor/formalism-independent elicitation semantics. Plugins pair one reusable domain typology with one target formalism and own that pairing's recognition/operations/coverage/verification guidance, never concrete scenario nouns. The app is the directive-marked registration and host-composition shell. Flue owns `useInstruction`, `useSkill`, `useTool`, static resource packaging, and runtime lifecycle; binding packages adapt generalized capture mechanics to a substrate. -- 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. +Prompting and recognition remain Brunch-owned. The latest `petrinautAiPrompt` is coverage evidence, not text to copy; FE-1516's one-day prose drift remains the counterexample to hand-copying Petrinaut contracts. Assertion mechanics, if ever earned, are harness-owned, while SDCPN mapping hints are target-formalism policy and must not leak concepts such as `resource`, `shift`, or `place` into generic capture/revision machinery. Universal ↔ SDCPN provenance migration remains an editorial practice recorded per edit; Mission 3 exercised it zero times on new real evidence. -# Mission 5 — traceable projection through the real panel +HASH Graph, Temporal, Redis, HASH API, S3, Kratos, and Petrinaut Optimizer are not current Brunch runtime dependencies and must not be added for symmetry. `@flue/react` remains appropriate for Brunch's local debug UI, and `binding-flue` remains a package even if it is the sole binding. The current host switch is still `yarn dev` versus `yarn dev:brunch`; that fact does not settle the product picker. -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. +#### Mission 4 architecture and interaction decisions -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. +A prompt carries mounted-lifetime invariants. A skill carries the procedures and judgment for a recognizable job. A tool contract carries the semantics and constraints of one executable operation. These are responsibility boundaries, not requirements for matching files or packages. -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. +Tools follow semantic capability authority even when a binding, transport, frontend, or other host layer implements part of the vertical execution path. Crossing ownership does not relocate the tool's meaning: core or plugin policy owns the operation it defines, while binding, transport, and frontend own faithful adaptation, carriage, rendering, reply capture, and execution at their respective boundaries. -## Constraints already earned +A plugin is a contribution bundle, not a symmetric inventory. It contributes only the prompts, skills, tools, and mounting seams earned by its recognizable jobs and executable capabilities. Plugin append prompts are optional, and skill cardinality is earned by distinct jobs and observed routing/context strain—not by package count, operation count, or a requirement that every plugin expose one skill per concern. -- 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. +Ampcode is the selected conceptual basis. Retain from Five-Register the domain-primary workpiece, single-authoritative-home locality, Flue-native experiments, and explicit distinctions among authored text, parser/schema acceptance, construction execution, semantic correspondence, and stronger behavioral evidence. Do not create a third synthesis. Directives, Recognition, Operations, Coverage, and Verification may classify what elicitation guidance does; they do not define phases, skills, schemas, runtime machinery, or file topology. -## Fog-line +Structured-question contract and evidence semantics are core-owned. Bindings adapt the core operation to a substrate, transport carries the affordance and correlated reply, and the frontend renders and collects that reply. Each crossing owns its local fidelity and failure behavior without redefining the question, answer, cancellation, redirect, or evidence semantics. -- 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. +The earlier "exactly one focused question" and template-gate content repairs were withdrawn on 2026-09-02 with the campaigns that motivated them; the accepted dosage wording and any re-admission of those repairs are owner decisions recorded in [`MISSION.md`](MISSION.md). -## Stop or reorient +On 2026-09-02 the owner set aside the skill-composition side quest's selection of packaged Candidate B and directed that the agreed topology be implemented as designed: core mounts an independent `elicitation` capability skill and plugin job skills activate it. The v3 observation (independent activation 0/3 versus packaged disclosure 2/3 on one opening case) stands as evidence of an activation risk to be re-tested under the new evaluation approach, not as architecture authority. The current binding decisions live only in [`MISSION.md`](MISSION.md); the evidence remains in [`flue-skill-composition-side-quest-v3/comparison.md`](docs/evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md). -- 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 8 consumed deployment contract -# Mission 6 — bounded reviewer revision to scoped net patch +Mission 8 at commit `157730cc5a214dd9c543e8d95c7193a219c48aef` on `ln/fe-1569-brunch-agent-deployment` stopped at the explicit application-to-infrastructure handoff. The application artifact is locally verified but **no remote deploy or acceptance happened**. No confirmed Brunch ECR repository, ECS service/task family, RDS database/user/IAM grant, hosted collector, restricted ingress, deployment owner, AWS credentialed run, real IAM probe, restricted Anthropic turn, cross-host replacement recovery, remote telemetry inspection, rollback, or owner acceptance exists. -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. +FE-1441 remains the deployment/Postgres/rate-limit tracker, while FE-1423 retains the authentication, telemetry, state-versioning/backup, and restart-durability gates. FE-1439's browser-minted UUID demo posture does not discharge FE-1423: caller UUID, CORS, obscurity, and rate limiting are not authentication. Resolve that policy conflict before any restricted-to-public cut. -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. +Landed application contract, retained for successor consumers: -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. +- immutable Node `22.21.1` non-root image runs `node dist/server.mjs`, carries focused dependencies, client assets, core prompt, and SDCPN resources, and builds on arm64 and amd64; +- `GET /health` is cheap and non-billable; required Postgres configuration and migration/connect failures fail closed before listening; +- active Flue conversation/submission/recovery/settlement state uses `@flue/postgres` with dedicated fields, verified TLS, RDS-IAM async fresh-token support and runtime-password fallback; URI-only and silent SQLite production fallback are rejected; +- OTLP/gRPC is initialized before content-free Flue instrumentation and flushed on shutdown; local disposable collector receipt is proved; +- local Docker/Postgres/collector smoke proved non-root execution, packaged resources, no `/repo` writes, TLS Postgres startup/refusal, and bounded graceful shutdown; +- public ingress denies `/`, `/assets/*`, and `/agents/chat/:id`; restricted product traffic uses `/api/chat`; one-live-owner policy remains desired-count one, stop-before-start until overlap safety is proved; +- separate Brunch capture JSON is inactive and non-durable. Do not migrate it speculatively, but any mission that consumes capture must first give it durable owner refusal, atomicity, format validation, and session/capture consistency. -## Constraints already earned +Flue's Node target is a long-running service with an in-process coordinator and long-lived streams. Do not deploy it as Lambda, a short-lived function, or scale-to-zero. Shared Postgres does not establish active-active safety; keep one replica until ownership and routing through replacement overlap are proved. -- 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. +Still-open infrastructure/release gate: -## Fog-line +- infra must approve/provision image repository, account/region, ECS cluster/service/task/execution roles, RDS endpoint/database/user/schema/CA and IAM grant or secret, Anthropic secret, collector, restricted hostname/access boundary, TLS/load-balancer health/stream timeout, CPU/memory, drain/stop/deployment settings, and named deployment/acceptance owner; +- one immutable digest must pass the two-connection IAM probe (or documented password fallback), real streamed Anthropic/tool turn, in-place restart, cross-host replacement, client abort, bounded provider/database failure, content/secret inspection, graceful replacement, rollback, and remote telemetry checks; +- public release additionally requires trusted identity/authorization, stock-safe Petrinaut routing and mode choice, route exposure policy, principal/IP rate and spend controls, retention/deletion/provider policy, backup/restore objectives, dashboards/alerts, and later capacity or multi-replica ownership evidence. -- 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. +A private smoke may temporarily use task-local SQLite only when restart loss is intentional, no durable user promise is made, and the environment is explicitly disposable. An EFS-backed SQLite singleton remains unproved and must not become accidental production architecture merely to postpone Postgres. -## Stop or reorient +Old Mission 8 reconciliation: -- 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. +| Old subsection | Disposition | Surviving consequence/evidence | +| --- | --- | --- | +| Observed starting point; application-owned surface; runtime candidates; CI wiring | Superseded proposal where implemented; landed application contract where locally observed | The bullets above and deployment handoff replace the pre-implementation audit. Image slimming, Compose parity, and obsolete workflow cleanup have no surviving requirement without strain. | +| Service/communication contract | Landed locally at the application seam | Long-running `/api/chat` → Flue → Anthropic shape, Postgres state, liveness, restricted routes, and content-free OTel survive; remote crossing remains unproved. | +| Infrastructure-owned surface | Still-open infrastructure gate | Provisioning and identifiers belong to infra; a deploy-catalog entry cannot create them. | +| Restricted smoke/public release; identity; front door; rate limits; streaming/availability | Restricted-threshold proposal partly superseded by the stopped handoff; public decisions still open | No public release. Caller UUID, CORS, obscurity, or rate limiting are not authentication. Keep one replica; measure timeout/reconnect and ownership before widening. | +| Persistence, migration, recovery | Flue Postgres application contract landed; replacement/backup and capture durability still open | Process-local proof is not ECS replacement proof. Capture remains inactive and must become durable when consumed. | +| Operational visibility/health | Local application contract landed; hosted inspection still open | Local collector and liveness pass; remote normal/failure/cost correlation, privacy inspection, retention, dashboards, and alerts do not. | +| Confidence, constraints, fog, stop lines | Reduced to the landed/open gates above | Never call an image or HTTP 200 deployed/durable, never weaken TLS or leak secrets, never infer active-active safety, and stop before unrestricted exposure or false recovery claims. | -# Mission 7 — complete FE-1476 rehearsal and optimisation handoff +Authoritative observed details are at `157730cc5a214dd9c543e8d95c7193a219c48aef:libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md` on `ln/fe-1569-brunch-agent-deployment`. This branch imports the application contract and open gates only—not that branch's Mission 4 archive, Mission 8 live-status transition, or an implication of remote success. -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. +## Parallel and asynchronous proof tracks -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. +These tracks may start only under their own issue, branch, PR, and mission authority when they change product code. Their results are evidence inputs and do not silently rewrite another mission. -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. +| Track | Join gate under the accepted spine | Does not block | +| --- | --- | --- | +| Frozen prospective baseline | The closed Mission 4 branch preserves it unchanged and makes no quality-improvement claim; because Mission 4 produced no full-run candidate, a successor may inspect this observed one-invalid/two-valid Mission 3 range only if it explicitly selects and promotes an eligible source | Mission 7's source selection and provenance-suitability decision, or the optional Mission 4 addendum | +| Direct Voice/Flue | One finalized spoken turn has one canonical Flue submission and canonical streamed TTS output without AI SDK chat transport or generative simplification | Mission 6 and later product-data work | +| Resumable fixture viability | One prepared fixture crosses conversation → Markdown workpiece → browser Petrinaut read/write → save/reopen | Mission 5 Voice transport work | +| Inferential observer fold | Decide before Mission 10 whether observed foreground strain earns promotion; otherwise retain phase-boundary synthesis | Missions 4–9 | +| Provider-visible nested schema | Mission 6 tests only the least mutation needed by its meaningful fixture; Mission 9 closes the broader canonical projection classes after a crisp blocker or success | Mission 5 and non-construction Voice work | +| Provenance interaction fixture | Mission 6 establishes minimal fixture identity; freeze the broader derivation fixture before Mission 7 why and Mission 9 automatic projection diverge | Voice work and prepared-fixture viability | +| Host choice/session lifecycle | Mission 5 proves Flue conversation reopen; Mission 6 proves fixture save/reopen; later host/picker breadth waits for its visible consumer | Either independent tracer | +| Optimisation handoff contract | Chris/Yannis accept input/output contract and one fixture before Mission 11 is cut | Missions 4–10 | +| Simulation-backed semantic check | Promote only if cheap and discriminating for the selected revision | First provenance tracer | -## Constraints already earned +The deliberately provisional shared-interface names remain `EvidenceBackedWorkpieceItem`, `DerivationRecord`, and `NetPatch`. Do not freeze richer names or field catalogs before two tracks genuinely share them. If fixture UI and projection cannot agree on the minimal derivation record, pause parallel work at that seam. -- 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. +## Detailed provisional clusters -## Fog-line +Detailed mission-specific boundaries, tracer floors, readiness ratchets, risks, oracles, and stop conditions live only in these six context repositories: -- 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. +- [Draft Mission 5 — direct Voice over canonical Flue transport](docs/mission-drafts/5-direct-voice-flue-transport.md) +- [Draft Mission 6 — resumable workpiece-to-Petrinaut fixture tracer](docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md) +- [Draft Mission 7 — capture-backed review](docs/mission-drafts/7-capture-backed-review.md) +- [Draft Mission 9 — automatic traceable projection](docs/mission-drafts/9-traceable-projection.md) +- [Draft Mission 10 — bounded reviewer revision](docs/mission-drafts/10-bounded-reviewer-revision.md) +- [Draft Mission 11 — optimisation handoff](docs/mission-drafts/11-optimisation-handoff.md) -## Stop or reorient +Do not create Mission 4 or Mission 8 drafts. Missions 5 and 6 are parallel, independent next cuts; each must become the sole live root `MISSION.md` in its own worktree. Mission 11 stays deliberately shallow until Chris and Yannis accept input artifacts, one optimisation question, scenario/parameter representation, execution boundary, expected result, and minimum credibility checks. -- 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. +## Unallocated backlog -# Parallel and asynchronous proof tracks +### Universal elicitation teaching -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. +The supported core is objective-relative interviewing: establish intended questions, audience, boundary, horizon, accuracy need, non-claims, and assumption tolerance; begin with one concrete occasion and walk it before generalizing; preserve expert statement, inference, assumption, unknown, unasked, conflict, correction, omission, and loss; treat divergence as information; spend questions by information value; stop on evidence rather than fluency, headings, fatigue, or turn count. -| 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 | +Unplaced candidate moves remain: closing clearinghouse (“what important thing was not asked?”); anchored hypotheticals based on a narrated incident; the clairvoyant definitional test for quantities; contrastive/expert-versus-novice probes; full-history consistency checks; depth on load-bearing facts without a universal turn count; anti-vagueness and anti-leading guidance; explicit exception/absence sweeps; premortem phrasing; correction-versus-context discriminator; and distinct declined, deferred, user-unknown, undecided, not-applicable, explicitly absent, and not-yet-asked outcomes. -The first shared interface candidates are deliberately small and provisional: +Placement and dosage remain unresolved. Historical 2–4 batching guidance did not prevent 4–10-question openings because those runs asked before reading elicitation guidance, while valid prospective runs did not repeat the overload. One question versus a small shared-frame batch, posture-as-intake risk, clarification versus case-deepening, quantitative/tail scripts, closing cadence, and assent semantics need discriminating probes. Do not contaminate the frozen baseline; use a separately versioned campaign. Remove caveat/failure/restatement/typology duplication before adding another catalog, and replace “high appetite,” “several turns,” or “deepen” with observable behavior. -```text -EvidenceBackedWorkpieceItem -DerivationRecord -NetPatch -``` +### SDCPN investigation and construction teaching -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. +Preserve this operational investigation floor without turning it into an opening schema: -# Inferential observer-fold hypothesis +1. decision/comparison/worry, audience, boundary, horizon, accuracy, non-claims; +2. one trigger-to-end occasion: flows, prerequisites, activities, order, outputs; +3. consumed versus reserved/released versus inspected inputs, capacity, simultaneous demand; +4. practiced versus written branching/contention, overrides, retries, failure, recovery, conflict; +5. objective-relevant typical/tail timing, hidden waits, calendars, arrivals, directional losses, grouping/splitting, thresholds, changing conditions; +6. attributed evidence, assumptions, unknown/unasked/conflict/correction/omission and projection limits without hardening hedges or incidents into rates; +7. trust observations and alternatives to compare; +8. a reconstructable spine from which construction can name the smallest consequential gap. -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. +Typology decisions are strain-gated: retain six question shapes; complete Grouped movement; strengthen Timed work before adding hidden-waiting type; split Mode-change loss into time/material/availability/what-cannot-run-next; probe condition-dependent duration/failure/loss under properties/time before adding a type; make arrivals explicit for throughput before a seventh type; do not add queue/waiting, priority/deadline, or escalation/approval types while existing resource/branch/trigger/lens guidance covers them. Numeric thresholds are required only when expert usage/objective requires one. Unclear scarce-resource release blocks construction only when alternate semantics change the objective; otherwise preserve a conspicuous assumption. -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. +Return to elicitation on missing objective or spine, unclear capped-resource fate, policy without practiced contention, missing throughput arrival, or objective-critical tail behavior. This list is an unexercised probe, not typed completion. Preserve adversarial probes: policy/practice, shared contention, hidden waiting encoded as work, directional changeover, rare incident as rate, unknown distribution forced into a family, grouped work that can split, continuous influence without threshold; plus newcomer, borderline, same-case/different-objective, evidence-order perturbation, and true correction after apparent readiness. Classify misses as acquisition, conservation, or simulator nondisclosure. -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. +The six `Transform to PN` children conceptually belong to construction, but two historical runs showed no vocabulary leak. Move them only in an instrumented teaching variant. Construction opens representational losses and returns only the smallest consequential gap; it does not silently fill one. -## Mechanical shell around inferential semantics +### Workpiece hypotheses and cold-reading obligations -- 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. +Observed strain: Mission 3 workpieces were composed wholesale late; six epistemic marks did not prevent agent values reading as testimony or unasked material reading as user unknown; Situation notes duplicated sections; Projection losses mixed elicitation gaps, construction choices, and actual representational loss; assumptions omitted reason/check; unsettled marks had no authority; declined/not-applicable were unexercised; flat prose obscured contextual/directional quantities. -## Semantic obligation +Competing shapes remain: -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, case/process spine + epistemic ledger:** one concrete flow; one light authoritative home for unresolved meaning, assumptions, conflicts, next questions. +- **B, entity/resource register:** stronger repeated per-entity/product/direction/quantity retrieval at greater semantic-organization cost. +- **C, current headings split by authorship/phase:** lowest-change control separating expert-given, agent-assumed with reason/check, construction-decided, elicitation gap, construction-opened loss. +- **D, append-only journal:** rejected unless a real correction lifecycle outweighs its cold-reading burden. +- **Objective slices/cases/residue:** alternate emphasis around model questions, evidence, blockers, validation, and next questions. +- **Versioned assertion clusters:** coherent evidence-backed prose with lineage and optional hints; granularity/stability remain low confidence, not default ontology. -A useful oracle compares: +A transcript-blind reader must state objective; reconstruct order; separate evidence/inference/assumption/unknown/conflict; name contradictions and next questions; judge construction readiness without inventing a spine; and spot-check epistemic standing and typical/tail context. Preserve objective, boundary/horizon, cases, policy/practice, contextual quantities, contention, assumptions, conflicts, omissions, validation, and named losses. Move PN transformation knowledge to construction, repair phase-mixed gap/loss and formulaic closure, and delete duplicate summaries only after one authority is proved. -```text -previous consolidated meaning + newly disclosed evidence -against -new consolidated meaning -``` +Rejected mechanisms re-enter only under their named strain: closed kinds/slots/demand rows/precision ladders if projection repeatedly cannot find consequential prose meaning; typed completion if evidence checks repeatedly permit unsupported readiness or cannot name the next question; per-statement epistemic enums if targeted prose/revision checks still launder authorship; typed per-capture losses if explicit construction loss cannot audit decisions; `firesWhen`, motif/repertoire/plugin runtime, one-artifact merger, and capture-envelope typed fold only for a real second consumer or observed failure. -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 previous typed-map cluster—required fields and unresolved gaps mechanically deriving ask/construct/deliver—remains a recorded hypothesis, not the default. It re-enters only if the workpiece-to-projection tracer or construction-gap return shows that model-assisted judgment cannot reliably name the smallest next question without typed demands. -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. +### Capture/workpiece seam history and rejected mechanisms -## SDCPN recognition hints +Four prior relationships retain distinct evidence: -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. +- **A, production independence:** lowest Condition 5 exposure and still useful outside delivery, but insufficient for FE-1476 provenance. High fan-in/context dependence supports keeping source ledger and synthesis separate. +- **B, support links only:** nearest current hypothesis. Offline links need evaluation-local ids; durable links require workpiece identity stable through revision. +- **C, capture fold proposes updates:** requires semantic interpretation/order/authority and remains unproved; the observer is only an asynchronous variant question. +- **D, one artifact:** refused because immutable evidence and editable synthesis have different lifecycles and it recreates the latency/complexity failure shape. -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. +The optional offline shadow join runs Mission 2 capture over settled history, assigns temporary workpiece-statement ids, and grades evidence relation, epistemic treatment, lifecycle relation, and projection treatment separately; assumptions never count as evidence support. Measure support coverage, synthesis fan-in, capture utility, context dependence, correction integrity, path sensitivity, and revision link churn. A fold becomes plausible only if order perturbation preserves active meaning without loop latency or requiring elicitor consultation of every fold. FE-1476 supersedes independence only as a sufficient delivery posture; it proves no store fold, merger, live linker, or comprehensive identity system. -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. +### Mission 3 split and construction evidence -## Spike evidence that would justify promotion +The surviving outcome is intentionally split: **runbook/workpiece path accepted; real-model semantic construction false on the exercised route**. Mission 3 locked one off-canvas PN JSON result, Petrinaut validation, manual load as inspection, and no canvas tools. The frozen prospective control has one invalid runtime member and two valid independently graded workpieces; historical runs are calibration only. -- 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. +Validated construction proved packaging, canonical callback validation, and a hermetic non-empty fixture using exactly `getLatestNetDefinition`, `addType`, `addParameter`, `addPlace`, `addTransition`, and `addArc` through immutable Flue `initialData`; those tools stayed absent from ordinary conversations. One paid run failed provider-visible nested shape: all nine `addType.elements` arrays arrived as strings, yielding a parser-valid but semantically vacuous empty net. One-shot construction took 162–271 seconds versus 5–23-second teaching turns. Construction-gap return was not exercised; the agent emitted `partial-with-named-gaps`. Periodic generation, programmatic load, and validated patch remain successors, never retroactive success. -## Stop or reorient +Do not rewrite Mission 3 as if all proof items passed. Mission 6 may test only the least browser mutation required by its prepared-fixture viability line; the broader falsified provider-visible nested-schema route remains Mission 9's first projection risk tracer, not Mission 5/6 closure and not retroactive Mission 3 success. -- 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. +### Gherkin pressure test -## Extraction thickness retained from the prior draft +Selected paper hypothesis: **software behavior × Gherkin, Shape C**—a lightweight near-target behavior workpiece plus combined Gherkin authoring/checking. The workpiece holds purpose, rules, concrete context/event-or-action/outcome examples, terms, status, authorship, and open matters without mandatory Given/When/Then decomposition; target text can become a correction surface. Core owns universal epistemic conduct; plugin owns software behavior/Gherkin additions across Directives, Recognition, Operations, Coverage, Verification; grammar semantics come from authoritative Gherkin capability. -Mission 2 proved the pipe with no extraction model: one envelope per user utterance, quote equal to that text, payload `{}`. That remains the floor. +The retained paper instrument lives under [`evaluations/protocols/gherkin-shape-c-paper-v1/`](evaluations/protocols/gherkin-shape-c-paper-v1/protocol.md); its former temporary workbench is reconstructible at commit `5249a73f09977ad2ef007e08de7b7314f94568e1`. The former `packages/plugin-gherkin/plugin.yaml` (removed with the YAML machinery in `93eb211dd3`; last present at `924be780ce`) was content-territory evidence from inactive generalized YAML machinery, not a Flue/parser/binding route. The paper instrument now also lives, adapted to the accepted topology, in `packages/plugin-gherkin/src/`. A read-only Oracle review independently selected Shape C; that agreement is design advice, not runtime evidence. -Progressive re-entry remains an evidence ladder rather than a destination: +Rejected **A** (mirror SDCPN's separate construction topology) overfits a distant/tool-mediated shape to near textual projection. Rejected **B** (`.feature` as workpiece and target) cannot honestly carry unsupported rules, authorship, current/proposed status, conflict, deferral, and unchecked binding without a hidden comments sidecar. Shape C reverses toward a less target-shaped workpiece if authorship laundering repeats, toward target text plus minimal open matters if transcription repeatedly has zero epistemic delta, or toward a separate checking phase if a real codebase step-binding capability earns it. -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. +Evidence ladder remains authored text → parser-valid text → step-definition-bound text → runtime-executed behavior → behavioral adequacy; claim only checks actually run. No Flue plugin route, parser, step corpus, binding index, or execution path exists. First tracer: smallest real software-behavior/Gherkin path through Flue, resource-read observation, provenance preservation, and explicit separation of authored/parser/binding/execution evidence. Early drafting may improve correction or anchor users to agent wording; only a run decides. -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. +### Dafny pressure test -# Delivery-adjacent host continuity +Candidate pairing is **software correctness obligations × Dafny specification modules/program contracts**. “Formal-verification use cases” is too target-neutral for a plugin; “verified state evolution × kernel contracts” overfits one repository. Supplied kernels are project context, not plugin ontology: `Model`/`Action`/`Inv`/`Valid`/`Apply`, `TryStep`, `Rebase`, `Candidates`, and `Explains` are recognition examples, never required decomposition. -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. +The source prompt is [“From Intent to Proof: Dafny Verification for Web Apps”](http://midspiral.com/blog/from-intent-to-proof-dafny-verification-for-web-apps/). The removed Ampcode workbench recorded the pairing without Dafny plugin resources or production wiring and is reconstructible at commit `5249a73f09977ad2ef007e08de7b7314f94568e1`; this section is the current authoritative planning account. A read-only Oracle review selected this pairing over state-evolution and target-neutral alternatives; that remains design advice. -## Two brains, same panel +Recognition spans initialization/representation invariants; pre/post/frame conditions; preservation, rejection, unchanged-on-failure; history/trace/round-trip/determinism/idempotence; abstraction/refinement/simulation/normalization/intent preservation; termination/progress; and trusted boundaries. It is an open repertoire. A nonnegative invariant can miss stale-redo while history laws catch it; initialization plus preservation proves only the expressed invariant for reachable states. -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`. +Keep three claim ladders explicit: intent ≠ formalization ≠ proof; stated ≠ assumed/axiomatized ≠ discharged ≠ trusted; verified source ≠ compiled artifact ≠ integrated system. Preconditions, admissibility predicates, abstraction functions, and candidate relations can make proof vacuous: ask what states/inputs are excluded, seek a witness and nearby counterexample, and expose every weakening/strengthening/normalization/reinterpretation. Proof search never silently edits authoritative intent to get green. -**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`. +Minimum target is a Dafny specification module—types/abstract types, predicates, signatures, relevant `requires`/`ensures`/`reads`/`modifies`/`decreases`, named lemmas—plus an obligation manifest mapping human claims to declarations, formalization choices, evidence status, and trusted dependencies. Missing bodies and `assume`/`{:axiom}` remain stated/trusted, not proved. The workpiece retains purpose/economics, harm, natural-language intent, witnesses/counterexamples/boundaries, state/operations/history, kernel context, assumptions/environment, authored formalization and deltas, tool status, and unverified integration perimeter; “not suited/worthwhile for Dafny” is a valid outcome. -**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. +A separate authoring and proof-evidence resource is plausible but unearned. Reverse to narrower state-evolution only if independent projects converge on it; add target selection above activation if Dafny-shaped recognition repeatedly anchors users away from a better method. First proof is one existing-codebase claim, exact contract, exact verifier run, and human judgment that intent/formalization/proof stayed legible. Fog: anchoring, smallest useful abstract module/manifest, accessible semantic diff, and keeping triage additive rather than generic consulting. -## Net create/save/load as session lifecycle +### Structured-question vertical capability -**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. +Structured questions remain an optional future capability, not permission to restore Condition 5's foreground `brunch_ask` or retired generalized runtime. Conversation is primary; a question is a rendered affordance committed as session evidence, not an exchange-pair ontology. Preserve free-text, single-choice, multi-choice, questionnaire chaining, and a markdown floor; custom forms remain progressive, opaque at the tool boundary, and harness-validated on read-back. -**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. +The real proof must cross **model → binding → transport → frontend → correlated reply → resumed model turn**. One assistant message may have one live interactive affordance because the data channel is last-write-wins; durable identity/payload ride the ask tool output, and mechanism rejects a second ask in the batch. A terminating tool suspends; pending affordance is durable per-session state and is narrated in the tool result, never interpolated into instructions. The harness mechanically binds the next reply while pending—no model-memory id or echo token—then resumes a fresh dispatch; interpretation later cites reply text. Outbound may be rich, inbound is string-only; unknown parts can disappear, so markdown fallback is mandatory. Preserve answered/redirected/unanswered as transport outcomes distinct from epistemic absence, and filter diagnostic signals by message purpose/display. -**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. +Do not claim this from a model tool call or rendered widget alone. Exercise all layers, cancellation/topic redirect, replay/resume, correlation, no wake wart, and stock-assistant isolation before promotion. -## Compaction +## Later and opportunistic concerns -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. +### Host/session continuity and compaction -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. +Host choice remains unasked at the real boundary: shared origin, picker placement, and whether switching is start-only or mid-net. Fixed Brunch mode may carry an early tracer; admit only the choice the visible story requires. -Compaction is history reconstruction, not prompting. Do not use it to sequence the first traceable projection unless the real rehearsal crosses it. +Missions 5 and 6 may each use a fixed Brunch mode. Mission 11 owns only the broader host/picker/continuity choice the complete six-beat rehearsal actually requires; do not pull broad host productization earlier. -## Voice +Working session hypothesis: net id discriminates one Flue conversation per principal; save/load resumes it, new net creates another. Today localStorage maps conversation ids by `netId`, while capture keys principal + conversation id. If ids regenerate/collide, rekey. Locked: net id is only a discriminator, not a Brunch target-document ontology. Keep rejected alternatives rejected: “net equals target-document” and sweep into a throwaway store for later splicing. -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. +Before claiming long-running provenance, prove panel/transcript/workpiece recovery across real Flue compaction (`compaction-vs-durable-history` / FE-1386). Current recovery scrapes the last `runbook-ir` fence; summary loss would break it. A short rehearsal may avoid compaction only if the handoff declares uncompacted-history dependence. Compaction is history reconstruction, not projection sequencing; control to compact/show summaries waits for evidence. -# Later / opportunistic tracks +### Voice -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. +Direct Voice/Flue transport is now Draft Mission 5 rather than an unallocated integration constraint. The observed remote stack remains `kostandin/fe-1570-voice-optimized-brunch-responses` → `kah-6763-temporary-brunch-ask` → `kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance` (PRs #9496, #9507, and #9512), diverging before Mission 4's app/package restructuring. Reconciliation must preserve current `useBrunchAgent()` + `useSdcpnPlugin()` composition and port only the still-needed Voice behavior onto current seams; never restore the older app-local stub agent. -## Observability / eval / tracing +Mission 5 treats the AI SDK UI-message transport and existing Petrinaut assistant as optional consumers, not Voice authority. Voice should use supported Flue `send`/`read`/`observe`/`history`/`abort` semantics directly or through the thinnest authenticated protocol-preserving proxy. Brunch owns canonical response text; Voice owns STT, TTS, interruption/cancellation, finalized-answer provenance, and playback. Provisional transcription/audio remains ephemeral, finalized answers enter canonical Flue history once, and the first tracer speaks canonical output without a secondary generative simplifier. Adapter/UI deletion requires separate consumer proof. -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. +The complete inherited seam map is in [`mission-4-voice-integration-handoff.md`](docs/evidence/implementations/mission-4-voice-integration-handoff.md); the revised cut contract and proof target are in [`5-direct-voice-flue-transport.md`](docs/mission-drafts/5-direct-voice-flue-transport.md). -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. +### Observability and simulation viewing -## Watch simulated conversations +Brunch has local content-free OTLP/gRPC export and graceful flush; hosted reachability and real normal/failure attributes remain open. Prove `gen_ai.conversation.id` equals Flue instance id and decide `traceparent` propagation. Keep prompt/response/tool content off until privacy/retention/access policy. Mission 5 needs only tracer latency/tool evidence; broad OTel remains a release gate. -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. +Simulated-conversation viewing remains evaluation infrastructure, not a protocol rewrite. The +2026-09-02 [live-observable persona spike](docs/evidence/evaluations/live-observable-persona-spike/README.md) +proved one local three-turn line: a restricted Herdr/Pi persona used +`createFlueClient → send → read(admission)` against the production `ChatAgent`, while the existing +`:4321` `useFlueAgent` view and transcript CLI projected the same canonical user/assistant order. +Submission-scoped `read(admission)` is the actor correlation primitive; `history()` remains the +observer/audit surface, not a latest-reply lookup. The browser must attach after the first +admission: an observer opened before the Flue instance existed stayed idle until reloaded, whereas +attachment to the created instance caught up history, streamed the next reply, and reconstructed +all settled messages after reload. The harness carried Mission 4's now-closed direct-Flue proof-of-life campaign and remains evaluation infrastructure rather than Petrinaut product evidence. -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. +The harness now lives in the Brunch agent application at `apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts`, with its persona policy and implementation decisions in the same-named folder beside it; it is a client of the application's composition, so the application owns, types, lints, and tests it, while it consumes reusable case inputs from this context root's `evaluations/`. A subsequent owner-authorized local remediation added explicit `none`, ordered-exact `mock`, and `real-headless` client-tool host modes. The bridge inspects only the settled submission's dynamic-tool parts, records server execution out of band, services client-deferred calls through the selected host, resumes Brunch with the existing `client-tool-result` signal and incarnation uid, and returns only the final resumed assistant text to the persona. The real-headless mode reuses the existing in-memory Petrinaut callbacks and reads checked-out Petrinaut docs; it does not claim browser execution or mount production tools. The contract suite proves server observation, mock suspension/resume and mismatch closure, real headless callback execution, and rendering, while direct Pi loading proves the relocated extension exposes its flags. No paid live tool turn was run, so live provider behavior and product/browser parity remain unproved. -## Simulation-backed construct check +The missing future capability is still a broader second observer, not a Herdr webview or PTY polling. Rendering `dynamic-tool`, `data-*`, and skill activation could make the browser observer more useful only when a named consumer requires it; Pi's evaluation-side tool trace is not that product surface. Re-enter pre-creation discovery, longer/full persona runs, paid live tool-host validation, pending-admission recovery, or remote access only with that consumer and its oracle. -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. +### Simulation-backed checking -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. +No evidence yet says a non-empty parser-valid net behaves like the workpiece. Candidate oracle: run Petrinaut simulation against qualitative objective expectations. The hermetic non-empty net is a fixture; the paid empty net is not. Promote only if cheap and discriminating for the selected revision; otherwise retain human semantic inspection and disclose the limitation. -## AI SDK 7 `HarnessAgent` +### Other substrate and product hypotheses -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. +AI SDK 7 `HarnessAgent` is undecided: it is the converse of the current door, resuming a harness session by chat id. Flue already owns that session and `transport-aisdk` adapts UI. A Pi/Claude Code harness is another binding substrate or Flue replacement, not the simulation viewer. Re-enter only with a concrete consumer. -# Live-mission leftovers and close inputs +Exploded-view net prototypes belong on Petrinaut website host routes, not `:4321`. If `ChatAgent` leaves the app, put it under `packages/<chat-agent>/`; the app stays shell. HASH embed remains stock unless explicitly opted in. Historical Conditions 1/2/4/5 remain batch evidence; no TUI, retired SDCPN elicitor, generalized `useElicitation()` runtime, loader, workflow engine, or second model-facing agent. -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. +## Historical 2026-09-02 migration disposition -- 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. +This section preserves the planning split as it was accepted on 2026-09-02. It is historical evidence, not the current draft inventory or numbering authority; the current six-draft topology is listed above. At that time, the candidate split had one live authority, one compact spine, four provisional drafts, and no Mission 4 or Mission 8 draft. Its source-to-destination ledger was: -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. +| Previous `MISSION.next.md` section | Surviving planning home | Disposition and consequence | +| --- | --- | --- | +| Opening authority warning and M1–M8 topology | Current authority and accepted spine | Condensed and renumbered to the accepted M4–M9 sequence; deployment branch history transition explicitly excluded. | +| FE-1476 delivery frame, confidence map, cross-milestone obligations, mechanism restraint | FE-1476 product frame; shared constraints | Condensed with low-confidence questions, six beats, proof obligations, and anti-ontology restraint preserved. | +| Universal teaching backlog | Unallocated backlog / Universal elicitation teaching | Retained with dosage, duplicate-removal, baseline, and discriminating-probe conditions. | +| SDCPN investigation backlog | Unallocated backlog / SDCPN investigation and construction teaching | Retained with typology choices, adversarial probes, return-to-elicitation conditions, and construction ownership. | +| Workpiece structure hypotheses and observed strain | Unallocated backlog / Workpiece hypotheses and cold-reading obligations | Retained with A–D/alternate shapes, cold-reader duties, rejected mechanisms, and re-entry conditions. | +| Prior capture/workpiece seam hypotheses and probes | Unallocated backlog / Capture/workpiece seam history | Retained with A–D, shadow join, measurements, and changed FE-1476 consequence. Draft 5 selects support-links-only without becoming a second home for the taxonomy. | +| Former Mission 5 traceable projection | Draft 5 and Draft 6 | Split deliberately: prepared-pair evidence-backed why moves to Mission 5; automatic meaningful projection, provider-schema repair, repeat/change identity, and derivations move to Mission 6. | +| Former Mission 6 reviewer revision | Draft 7 | Moved at full mission-specific detail, with foreground synthesis default and observer only as strain-triggered re-entry. | +| Former Mission 7 rehearsal and optimisation handoff | Draft 9; FE-1476 frame; Later host concerns | Consumer handoff stays deliberately shallow in Mission 9; six-beat floor and host continuity remain shared until the accepted consumer requires them. | +| Former Mission 8 deployment cluster | Mission 8 consumed deployment contract | Reconciled subsection by subsection against branch evidence into landed application contract, open infrastructure/release gates, and superseded proposal; no remote deployment inferred. | +| Parallel and asynchronous proof tracks | Parallel and asynchronous proof tracks | Retained with renumbered joins and provisional seam names. | +| Inferential observer-fold hypothesis and extraction ladder | Shared constraints / Foreground revision and observer re-entry | Retained as contingent evidence and re-entry mechanics, never planned implementation. | +| Delivery-adjacent host continuity | Later / Host-session continuity, compaction, and Voice | Retained with fixed-mode allowance, Mission 9 rehearsal owner, session hypothesis, rejected target-document shapes, compaction risk, and voice parent constraint. | +| Later observability, simulated viewing, simulation check, `HarnessAgent` | Later and opportunistic concerns | Retained with promotion conditions and deployment evidence updated from Mission 8. | +| Closed runbook/workpiece record | Mission 3 split and construction evidence | Retained as the accepted-workpiece/falsified-construction split; first provider-schema owner remapped to Mission 6. | +| Gherkin pressure test | Unallocated backlog / Gherkin pressure test | Retained with Shape C, rejected A/B, evidence ladder, Oracle qualification, and reversal conditions. | +| Dafny pressure test | Unallocated backlog / Dafny pressure test | Retained with pairing, alternatives, claim ladders, vacuity/trust boundaries, artifact floor, Oracle qualification, and reversals. | +| Standing decisions | Shared constraints and standing locks | Retained with packaged-B disposition, authority boundaries, capture/workpiece separation, platform locks, and no generalized runtime/TUI. | +| Structured-question semantics from accepted planning | Unallocated backlog / Structured-question vertical capability | Added to its sole planning home with the required model-to-resumed-turn proof and no `brunch_ask` reactivation. | -# Standing decisions +Material was removed only where the deployment branch's observed application artifact superseded its earlier proposal or where a spine summary now points to a detailed draft. All open consequences and re-entry conditions remain above. Mission 4 is closed under its recorded narrower owner adjudication; no future mission or remote deployment is promoted. -Not missions. +## Historical planning-split close record -## Ownership and teaching mechanism +Outcome: **planning split completed and remediated on 2026-09-02 from `06ed66c083` to `f7175531c767c87f880d7def5321b1564a62a1e7`.** These immutable commits are respectively the before-state containing the side-quest contract and the reviewed split result. -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. +Semantic review accepted the authority topology, full-fidelity migration, Mission 8 account, throughline/readiness separation, Mission 9 horizon, expeditionary posture, and draft lifecycle after restoring omitted Mission 4 decisions, pinning deployment evidence, and correcting the immutable proof boundary. The review packet is the before-state `SIDE_QUEST.md` and `MISSION.next.md` versus the result's `AGENTS.md`, `MISSION.next.md`, and `docs/mission-drafts/`; the checklist is the migration ledger above plus those named concerns. -Ownership boundaries: +Reproducible commands, run from the repository root: -- **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. +```shell +ROOT="libs/@hashintel/brunch-agent" +BEFORE=06ed66c083 +RESULT=f7175531c767c87f880d7def5321b1564a62a1e7 -The universal ↔ SDCPN provenance migration remains an editorial practice recorded per edit, not automation. Mission 3 exercised it zero times on new real evidence. +# The side-quest contract exists only in the before-state; protected authority/archive files are byte-identical. +git cat-file -e "$BEFORE:$ROOT/SIDE_QUEST.md" +! git cat-file -e "$RESULT:$ROOT/SIDE_QUEST.md" 2>/dev/null +git diff --exit-code "$BEFORE" "$RESULT" -- "$ROOT/MISSION.md" "$ROOT/docs/mission-archive/" -## Capture and workpiece relationship +# The immutable result has exactly the four numbered drafts, all warnings, and no live-authority headings. +actual=$(git ls-tree -r --name-only "$RESULT" -- "$ROOT/docs/mission-drafts/" | awk -F/ '$NF ~ /^[0-9]+-.*\.md$/ {print $NF}') +expected=$'5-capture-backed-review.md\n6-traceable-projection.md\n7-bounded-reviewer-revision.md\n9-optimisation-handoff.md' +test "$actual" = "$expected" +test "$(git grep -l -E '^> Draft cluster only\. Not execution authority\. Do not implement until this cluster is re-evaluated and cut into `MISSION\.md`\.$' "$RESULT" -- ":(glob)$ROOT/docs/mission-drafts/[0-9]-*.md" | wc -l | tr -d ' ')" = 4 +! git grep -E '^## (Status|Imperative|Proof)$' "$RESULT" -- ":(glob)$ROOT/docs/mission-drafts/[0-9]-*.md" +git diff --check "$BEFORE" "$RESULT" + +# Relative links resolve inside the immutable result rather than the mutable working tree. +RESULT="$RESULT" python3 - <<'PY' +from pathlib import PurePosixPath +from urllib.parse import unquote +import os, re, subprocess + +result = os.environ["RESULT"] +root = PurePosixPath("libs/@hashintel/brunch-agent") +listed = subprocess.check_output(["git", "ls-tree", "-r", "--name-only", result, "--", str(root / "docs/mission-drafts")], text=True).splitlines() +files = [root / "AGENTS.md", root / "MISSION.next.md", *[PurePosixPath(path) for path in listed if path.endswith(".md")]] +broken = [] +checked = 0 +for file in files: + text = subprocess.check_output(["git", "show", f"{result}:{file}"], text=True) + for target in re.findall(r"(?<!!)\[[^]]+\]\(([^)#]+)(?:#[^)]+)?\)", text): + if "://" in target or target.startswith("mailto:"): + continue + checked += 1 + parts = [] + for part in (file.parent / PurePosixPath(unquote(target))).parts: + parts.pop() if part == ".." else parts.append(part) + destination = PurePosixPath(*parts) + if subprocess.run(["git", "cat-file", "-e", f"{result}:{destination}"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode: + broken.append((str(file), target)) +print(f"relative links: checked={checked} broken={len(broken)}") +assert not broken, broken +PY +``` -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. +Observed results: the before/result packet resolves; `SIDE_QUEST.md` exists only before the result; `MISSION.md` and `docs/mission-archive/` are byte-identical; the result contains exactly drafts 5/6/7/9, four authority warnings, no prohibited headings, 89 relative links with none broken, and no whitespace error. Semantic review found no remaining unexplained loss, duplicate authority, false remote-deployment claim, line/readiness collapse, Mission 9 overcommitment, or lifecycle ambiguity. -Three premature convergence shapes remain refused: +This non-authoritative planning-split record did not itself close Mission 4, execute its then-numbered Missions 5–9, promote Mission 4's architecture, or claim Brunch was remotely deployed. Mission 4's later closure and accepted implementation boundary are recorded independently in the 2026-09-03 closure decision linked above. -- 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. +## 2026-09-03 successor recut -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. +The owner subsequently changed the integration premise: Voice should use canonical Flue transport rather than the AI SDK chat composer; workpiece-to-Petrinaut viability should be tested before complete provenance/product readiness; and one deliberately prepared fixture may be sufficient for that test. The current topology therefore inserts independent Draft Missions 5 and 6, renumbers capture-backed review to 7, retains historical deployment as 8, and renumbers automatic projection, reviewer revision, and optimisation to 9, 10, and 11. Frozen Mission 3/4 evidence and archived mission files remain unchanged. -## Locked, not a mission +## 2026-09-03 product-manager litmus reframing -- Keep the AI SDK adapter. Do not rewrite the Petrinaut panel onto `@flue/react`. -- `@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, place it under `packages/<chat-agent>/`; 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. +Later on 2026-09-03 the owner replaced the "visible/usable proof" completion criterion with the product-manager litmus defined in the accepted spine above. The observed problem was that each précis pinned completion to an evidence bundle at the first green throughline tracer, which convinces a builder but is invisible to a product manager, and that Draft Mission 9 carried engineering internals in its visible-advance section. The change re-pins completion to each mission's readiness gate for the named demo scenario, moves oracles out of the visible-advance sections, expands Mission 7 from one element to every consequential element of the demo net, and names the deployment posture problem for Missions 7, 9, and 10. Mission 5 was live on its own branch and was not touched. Mission-specific detail lives in the affected drafts' `Visible product advance` and `Throughline proof floor` sections and in the [draft README](docs/mission-drafts/README.md). diff --git a/libs/@hashintel/brunch-agent/README.md b/libs/@hashintel/brunch-agent/README.md index e3c0352a68e..8c62708cf90 100644 --- a/libs/@hashintel/brunch-agent/README.md +++ b/libs/@hashintel/brunch-agent/README.md @@ -4,20 +4,20 @@ 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 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/). + [`MISSION.next.md`](./MISSION.next.md) is the self-contained canonical future spine and is 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 prior design decisions (see [`docs/adr/README.md`](./docs/adr/README.md)). - [`docs/evidence/`](./docs/evidence/) holds observed results and proofs. -- [`packages/core/`](./packages/core/) is `@hashintel/brunch-agent`; its guarded `./prompts` - subpath ships the harness repertoire, rendered by bindings and never imported by plugins. +- [`packages/core/`](./packages/core/) is `@hashintel/brunch-agent`; its `./flue` subpath is the + production contribution (always-on prompt and the `elicitation` skill), `./storage` and + `./client-tools` carry evidence and browser contracts, and `src/_suspended/` holds unmounted code. - [`packages/binding-flue/`](./packages/binding-flue/) is the Flue binding. - [`packages/transport-aisdk/`](./packages/transport-aisdk/) is the AI SDK transport. -- [`packages/plugin-gherkin/`](./packages/plugin-gherkin/) and - [`packages/plugin-sdcpn/`](./packages/plugin-sdcpn/) are the target plugins. +- [`packages/plugin-gherkin/`](./packages/plugin-gherkin/) pairs the software-behavior domain typology with the Gherkin target formalism. +- [`packages/plugin-sdcpn/`](./packages/plugin-sdcpn/) pairs the operational-process domain typology with the SDCPN target formalism. +- [`packages/plugin-dafny/`](./packages/plugin-dafny/) is a stubbed software-correctness / Dafny contribution bundle that pressure-tests the core/plugin topology; nothing composes it. - [`../../../apps/brunch-agent/`](../../../apps/brunch-agent/) is the server and diagnostics app. HASH's repository root owns package discovery, dependency policy, the lockfile, and the Turbo task diff --git a/libs/@hashintel/brunch-agent/docs/adr/0002-topology-and-placement-rules.md b/libs/@hashintel/brunch-agent/docs/adr/0002-topology-and-placement-rules.md index d32e82f59b3..7d2001da204 100644 --- a/libs/@hashintel/brunch-agent/docs/adr/0002-topology-and-placement-rules.md +++ b/libs/@hashintel/brunch-agent/docs/adr/0002-topology-and-placement-rules.md @@ -1,7 +1,7 @@ # ADR-0002: The three-lane topology and placement rules N1–N6 Date: 2026-08-17 -Status: accepted +Status: historical; superseded for current Brunch composition by the final [Mission 4 architecture](../mission-archive/4-core-plugin-elicitation-proof-of-life.md). N3's app composition boundary and the prohibition on app-local plugin content survive, but the three-lane/YAML/repertoire details do not. Amended: 2026-08-20 by ADR-0004 / FE-1437 (N3 application placement) Refines: spec [§12.2](../specs/elicitation-kernel.md) (package topology) with placement rules the spec did not state diff --git a/libs/@hashintel/brunch-agent/docs/adr/0006-plugins-per-target-formalism.md b/libs/@hashintel/brunch-agent/docs/adr/0006-plugins-per-target-formalism.md index 6c4c5287fbf..5a356244d24 100644 --- a/libs/@hashintel/brunch-agent/docs/adr/0006-plugins-per-target-formalism.md +++ b/libs/@hashintel/brunch-agent/docs/adr/0006-plugins-per-target-formalism.md @@ -1,7 +1,7 @@ # ADR-0006: Plugins are per target formalism, authored as sectioned Markdown Date: 2026-08-25 -Status: accepted +Status: historical; superseded for current implementation by the final [Mission 4 architecture](../mission-archive/4-core-plugin-elicitation-proof-of-life.md). The historical target-formalism-only unit is replaced by one reusable domain-typology / target-formalism pairing, and the YAML/table/repertoire machinery is removed. The prohibition survives for concrete domains, organizations, situations, and scenarios. Amended by: [ADR-0007](0007-harness-teaching-meets-plugin-content-at-fixed-keys.md) (2026-08-25), decisions 2 and 5 — the machine-read tables become schema-validated data, the prose becomes cells under harness-owned keys, and the harness-generic lift is designed there 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 71075048838..0a91c66a701 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 @@ -1,8 +1,7 @@ # ADR-0007: Harness teaching meets plugin content at fixed keys Date: 2026-08-25 -Status: accepted 2026-08-25 (Lu), with one caveat recorded as decision 9 — the key catalogue of -decision 2 is a working set that two plugins converge on, not a list frozen by this record +Status: historical; superseded for current implementation by the final [Mission 4 architecture](../mission-archive/4-core-plugin-elicitation-proof-of-life.md). The fixed-key/YAML/repertoire design below is not the current integration contract; its surviving ownership distinction is expressed by the independent core `elicitation` capability and plugin-owned target-pairing job guidance. Amends: [ADR-0006](0006-plugins-per-target-formalism.md), decision 2 (a plugin is no longer one Markdown file whose prose is concatenated whole; the machine-read tables become schema-validated data and the prose becomes cells under harness-owned keys) and decision 5 (the "later lift" of @@ -12,6 +11,7 @@ a harness package, never in app `skills/` directories), [ADR-0003](0003-three-re (three registers), [ADR-0005](0005-model-assisted-sdcpn-realization.md) (`project` / `validate` 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) +Corrected by Mission 4 on 2026-09-01: references below to a plugin naming “no domain” mean no concrete domain, organization, situation, or scenario. A plugin now explicitly pairs a reusable domain typology with its target formalism; its cells may use that typology. Decided on: the `ln/fe-1406-harness-teaching-adr` branch, from 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); @@ -20,7 +20,7 @@ owning issue FE-1406 (gist: what the harness teaches) ## Context Kernel spec §11.5 has said since 2026-08-11 that **guidance ownership follows vocabulary -ownership**: a plugin teaches what to notice in its formalism; the harness teaches how to work an +ownership**: a plugin teaches what to notice through its domain typology and for its target 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/audits/harness-teaching-lineage-audit.md) finds fifteen restatements, @@ -63,7 +63,7 @@ regex over prose and the completion anchor found by naming convention — the si 1. **One principle for every key: the harness defines and teaches the concept; the plugin specialises it in the harness's terms.** Plugin authoring is a fixed set of **keys**. The harness owns every key, its meaning, and its default text; a plugin fills cells under those keys - with content that names its kinds and never a domain. Rendering interleaves, key by key: the + with content that names its domain typology and kinds and never a concrete domain. Rendering interleaves, key by key: the key, the harness default, then the plugin's cell if it is not blank. Cells add; they never override a default — a default a plugin needs to contradict is a finding about the harness. Once the catalogue is frozen (decision 9), adding a key is an amendment to this record and the diff --git a/libs/@hashintel/brunch-agent/docs/adr/README.md b/libs/@hashintel/brunch-agent/docs/adr/README.md index e1b1e3fe06b..a4100b80873 100644 --- a/libs/@hashintel/brunch-agent/docs/adr/README.md +++ b/libs/@hashintel/brunch-agent/docs/adr/README.md @@ -7,3 +7,5 @@ re-earn before building further on them. Internal references to retired paths (`docs/control/`, `docs/agents/`, `docs/INDEX.md`) are historical and not maintained. + +For the current accepted Brunch architecture, start at the root [`MISSION.md`](../../MISSION.md) closure pointer, [`MISSION.next.md`](../../MISSION.next.md), and the final [Mission 4 archive](../mission-archive/4-core-plugin-elicitation-proof-of-life.md). Mission 4 replaced the generalized YAML/repertoire/plugin machinery described in ADR-0002, ADR-0006, and ADR-0007 with a Flue-native independent core `elicitation` capability, target-pairing plugin job skills, and app-owned composition. Those ADRs remain useful design history, not an integration baseline. diff --git a/libs/@hashintel/brunch-agent/docs/inbox/eliciting-and-constructing-processes-to-PNs.md b/libs/@hashintel/brunch-agent/docs/archive/eliciting-and-constructing-processes-to-PNs.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/eliciting-and-constructing-processes-to-PNs.md rename to libs/@hashintel/brunch-agent/docs/archive/eliciting-and-constructing-processes-to-PNs.md diff --git a/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md b/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md index 6ca6360b986..7acb0d9de7d 100644 --- a/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md +++ b/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md @@ -1,9 +1,5 @@ # 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. +This directory holds concise human-readable retirement records for evaluation instruments that are no longer supported. A record names the replacement or final disposition, the surviving adjudication, the content identity of removed material, and the historical Git revision containing the complete source and evidence. -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/). +Do not copy runnable code here for compatibility. Delete obsolete runners and tests after their provenance has been recorded. Accepted or explicitly retained campaign outputs remain under [`docs/evidence/evaluations/`](../../evidence/evaluations/). An owner may retire raw outputs from an exploratory campaign only when no live consumer needs them, the decision-relevant adjudication survives, an ordered path/content hash ledger identifies every removed artifact, and a complete historical commit is recorded with a recovery procedure. Delete the raw campaign coherently rather than retaining an arbitrary subset, and never edit an observed artifact to make it smaller. diff --git a/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest-runs.tar.gz b/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest-runs.tar.gz new file mode 100644 index 00000000000..91393c19d5a Binary files /dev/null and b/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest-runs.tar.gz differ diff --git a/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest.md b/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest.md new file mode 100644 index 00000000000..e92d02376af --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/archive/evaluations/flue-skill-composition-side-quest.md @@ -0,0 +1,52 @@ +# Flue skill-composition side-quest retirement + +The v1–v3 Flue skill-composition side quest compared independent core `elicitation` activation with packaged universal disclosure. Its human-readable adjudications remain at: + +- [`../../evidence/evaluations/flue-skill-composition-side-quest-v1/comparison.md`](../../evidence/evaluations/flue-skill-composition-side-quest-v1/comparison.md) +- [`../../evidence/evaluations/flue-skill-composition-side-quest-v2/comparison.md`](../../evidence/evaluations/flue-skill-composition-side-quest-v2/comparison.md) +- [`../../evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md`](../../evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md) + +The owner set aside the side quest's Candidate B fallback and directed Mission 4 to implement the independent capability topology. V3 remains bounded evidence that independent activation was unreliable in that instrument and packaged disclosure routed more often on S1; neither topology passed the complete cross-scenario condition. The runner has been removed, no current code consumes individual run payloads, and the future spine cites only the v3 adjudication. Keeping 47 repetitive raw JSON records in every checkout no longer serves a live consumer. + +## Raw-run identity and recovery + +The complete pre-retirement tree was captured at historical commit +`d9ae5de506a2fc00cf7473c03a217d20f3a9fc63` on PR +[#9468](https://github.com/hashintel/hash/pull/9468). Recovery does not depend on that +intermediate commit: the final tree retains the complete 47-file corpus in +[`flue-skill-composition-side-quest-runs.tar.gz`](flue-skill-composition-side-quest-runs.tar.gz). +The archive's SHA-256 is +`99d5302fb42807b9e9d77d4c432f4b52b77aeea4f7312cd6fb8f104452e3fc2a`. +The ordered path/content ledgers retained beside each comparison verify every extracted file: + +| Campaign | Files | Lines | Bytes | Ledger | Ledger SHA-256 | +| --- | ---: | ---: | ---: | --- | --- | +| v1 | 11 | 29,283 | 3,985,364 | [`retired-runs.sha256`](../../evidence/evaluations/flue-skill-composition-side-quest-v1/retired-runs.sha256) | `314e1eade7be67fc087e718403d0927fa3b360cf29d5f5ae205686e754224eb5` | +| v2 | 12 | 30,584 | 4,529,190 | [`retired-runs.sha256`](../../evidence/evaluations/flue-skill-composition-side-quest-v2/retired-runs.sha256) | `200aed778e38c31d858bdafabd49b36579477b580cadace85390d45ce34ad72b` | +| v3 | 24 | 62,392 | 7,968,249 | [`retired-runs.sha256`](../../evidence/evaluations/flue-skill-composition-side-quest-v3/retired-runs.sha256) | `3c4a401dec62eba25498e62ceb6e2a79514d1b8dabb45a1822ca3065e312a6e5` | + +Recover and verify a campaign from the repository root: + +```shell +CAMPAIGN=flue-skill-composition-side-quest-v3 +ROOT=libs/@hashintel/brunch-agent +RECOVERY_DIR=$(mktemp -d) +ARCHIVE=$ROOT/docs/archive/evaluations/flue-skill-composition-side-quest-runs.tar.gz + +echo "99d5302fb42807b9e9d77d4c432f4b52b77aeea4f7312cd6fb8f104452e3fc2a $ARCHIVE" | + shasum -a 256 -c - +tar -xzf "$ARCHIVE" -C "$RECOVERY_DIR" +( + cd "$RECOVERY_DIR" + shasum -a 256 -c "$OLDPWD/$ROOT/docs/evidence/evaluations/$CAMPAIGN/retired-runs.sha256" +) +``` + +The core unit suite executes the same extraction and verifies all three ledgers from checked-out +files, without fetching a historical ref. The unchanged campaign manifests still describe the +executed campaigns. Their `runs/...` lists and JSON pointers are paths inside the archive, not +omissions from the campaign. + +## Retired material + +All three `runs/` directories were removed as one coherent retirement. No arbitrary paid or hermetic sample remains in the live tree, because a partial corpus would look complete while failing the campaign manifests. No raw observed artifact was edited. Mission 4 proof-of-life v1/v2 evidence and every other campaign remain untouched. diff --git a/libs/@hashintel/brunch-agent/docs/inbox/jetty-runbooks.md b/libs/@hashintel/brunch-agent/docs/archive/jetty-runbooks.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/jetty-runbooks.md rename to libs/@hashintel/brunch-agent/docs/archive/jetty-runbooks.md diff --git a/libs/@hashintel/brunch-agent/docs/inbox/jetty-writing-runbooks.md b/libs/@hashintel/brunch-agent/docs/archive/jetty-writing-runbooks.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/jetty-writing-runbooks.md rename to libs/@hashintel/brunch-agent/docs/archive/jetty-writing-runbooks.md diff --git a/libs/@hashintel/brunch-agent/docs/inbox/pplx-agent-skill-rule-loading.md b/libs/@hashintel/brunch-agent/docs/archive/pplx-agent-skill-rule-loading.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/pplx-agent-skill-rule-loading.md rename to libs/@hashintel/brunch-agent/docs/archive/pplx-agent-skill-rule-loading.md diff --git a/libs/@hashintel/brunch-agent/docs/inbox/pplx-single-global-agents-file.md b/libs/@hashintel/brunch-agent/docs/archive/pplx-single-global-agents-file.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/pplx-single-global-agents-file.md rename to libs/@hashintel/brunch-agent/docs/archive/pplx-single-global-agents-file.md diff --git a/libs/@hashintel/brunch-agent/docs/evidence/audits/core-elicitor-prompt-material-audit-2026-08-31.md b/libs/@hashintel/brunch-agent/docs/evidence/audits/core-elicitor-prompt-material-audit-2026-08-31.md new file mode 100644 index 00000000000..d80f19c3fe0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/audits/core-elicitor-prompt-material-audit-2026-08-31.md @@ -0,0 +1,172 @@ +# Core elicitor prompt material audit + +**Date:** 2026-08-31 + +**Status:** Research and implementation audit for owner review. This compiles existing Brunch material for possible inclusion in the context- and formalism-independent core elicitor prompt. It does not amend the prompt, the runbook, or `MISSION.md`. + +## Question + +What has this project already said, taught, observed, or tested about conduct that should hold for a Brunch elicitor across domain contexts and output formalisms, and which part of that material is small and universal enough to remain in the always-on core system prompt rather than a progressively disclosed skill, plugin resource, workpiece contract, evaluator, or suspended mechanism? + +## Method + +The audit read all six current files under `docs/research/elicitation/`, the paired 2026-08-28 universal syntheses and their overview, current executable prompt/skill sources, the suspended repertoire renderer, frozen legacy and prospective prompts, mission/spec placement decisions, and evaluation evidence. Three isolated read-only inventories separately covered executable material, research, and historical prompt forms; they were used as completeness checks, not as independent evidence. + +## Verdict + +The current one-line prompt is correctly context-independent but under-specifies the stable conduct that the project repeatedly treats as universal. The corpus does not support copying the old repertoire or v0 prompt wholesale into the system prompt. It supports a concise always-on invariant set surrounded by progressively disclosed generic elicitation teaching. + +The strongest always-on candidates are: objective-relative attention; expert vocabulary rather than schema traversal; authorship and uncertainty preservation; no independent opening battery; divergence before reconciliation; and honest partial delivery rather than fluency-based completion. Detailed interviewing moves, quantitative scripts, lifecycle procedure, workpiece marks, target investigation, and detection signatures belong elsewhere. + +## Evidence discipline + +Repeated text is not independent corroboration when the active skill, repertoire, universal syntheses, and later specifications all descend from the same local source pool. Confidence rises where different evidence classes align: verified literature, observed Brunch runs, independently observed LLM-interviewer failures, and current executable teaching. Historical specifications and prompt variants show design lineage and candidate wording, not effectiveness by themselves. + +The corpus itself warns against prompt accretion: [`elicitation-to-ir-oracle-design.md`](../../specs/elicitation-to-ir-oracle-design.md) says not to paste source material wholesale into the system prompt or skill; [`structurally-typed-elicitation-runbooks.md`](../../specs/structurally-typed-elicitation-runbooks.md) says the always-on instruction is a concise router and invariant set, while bulky universal material remains lazy. + +## Source register + +| Source | What it contributes | Evidence status | Use in this audit | +| --- | --- | --- | --- | +| [`packages/core/src/teaching/repertoire.yaml`](../../../packages/core/src/teaching/repertoire.yaml) | The largest executable compilation of formalism- and domain-independent lenses, techniques, movements, licenses, smells, rabbit holes, failure modes, and lifecycle moves | Compiled teaching with per-entry provenance; suspended from production | Primary inventory of candidate generic conduct; too large and mechanism-coupled to use as an always-on prompt | +| [`packages/core/src/teaching/instructions.ts`](../../../packages/core/src/teaching/instructions.ts) | `HARNESS_PREAMBLE` and the renderer that combined repertoire with typed plugin definitions | Executable but suspended mechanism | Useful negative control: several sentences assert capture/fold/completion machinery the active agent does not have and therefore must not enter the current core prompt | +| [`plugin-sdcpn/.../elicitation.md`](../../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/elicitation.md) | A compressed active version of universal questioning, evidence, prioritization, stopping, caveats, and failure knowledge mixed with SDCPN investigation | Current production teaching; headings explicitly mark `universal`, `sdcpn`, and `mixed` provenance | Direct evidence that universal content is currently misplaced in the plugin; useful wording source, not the correct final home | +| [`universal-elicitation-synthesis.md`](../../archive/research/elicitation/2026-08-28-ensembles/universal-elicitation-synthesis.md) and its [independent companion](../../archive/research/elicitation/2026-08-28-ensembles/universal-elicitation-synthesis-cursor-2026-08-28.md) | Proposition-by-proposition source mapping, candidate homes, falsifying probes, current-material assessment, and unresolved tensions | Read-only syntheses over substantially shared local sources | Best existing placement analysis; agreement is editorial corroboration, not independent empirical evidence | +| [`elicitation-research-synthesis-2026-08-31.md`](../../research/elicitation/elicitation-research-synthesis-2026-08-31.md) | Cold-adjudicated synthesis across universal elicitation, SDCPN investigation, IR obligations, and capture/IR separation | Authoritative synthesis of the 2026-08-28 research batch | Current high-level evidence and uncertainty calibration | +| [`elicitation-strategy-literature.md`](../../research/elicitation/elicitation-strategy-literature.md) | Verified literature on objectives-first framing, concrete cases, probe depth, cue elicitation, quantities, disagreement, stopping, and technique selection | Mixed `[V]`, `[C]`, and `[R]` source grades, explicitly labelled | Main independent literature evidence; only `[V]` claims are treated as strong prompt candidates without further checking | +| [`interviewing-literature-source-catalog.md`](../../research/elicitation/interviewing-literature-source-catalog.md) | Verbatim instruments and frequencies: Bano/Ferrari mistakes, ambiguity cues, question typologies, completeness and stopping literature, and LLM interviewer findings | Source-preserving research report | Supplies exact failure observations and guards against over-compressed claims in later summaries | +| [`frontier-model-elicitor-failure-catalogue.md`](../../research/elicitation/frontier-model-elicitor-failure-catalogue.md) | FM-01–15 with mechanism, detection signature, accountable layer, and prevention status | Separates local observations, published observations, and synthesis | Prevents assigning machinery failures to prompt prose; identifies opening overload, ambiguity bypass, and unlicensed influence as technique-owned or partly technique-owned | +| [`evaluations/protocols/legacy-baseline/v0-prompt.md`](../../../evaluations/protocols/legacy-baseline/v0-prompt.md) | The first compact seven-move elicitor prompt: objectives first; slice then sweep; probe; ask absences; batch breadth/sequence depth; assumption ledger; end properly | Sealed historical evaluation instrument | Strong wording lineage and one observed intervention, but process-model categories and a full deliverable contract make it too target-specific and too large for core | +| [`harness-teaching-lineage-audit.md`](harness-teaching-lineage-audit.md) | Fifteen historical formulations of generic interviewer craft and their migration among plugin, harness, mechanism, and prompt layers | Historical audit | Establishes that generic ownership was repeatedly intended but never cleanly delivered; does not select final content | +| [`structurally-typed-elicitation-runbooks.md`](../../specs/structurally-typed-elicitation-runbooks.md) | Explicit Flue information hierarchy and the universal-repertoire versus target-runbook split | Historical specification, not live authority | Supplies the placement rule: concise always-on router/invariants; lifecycle in skill body; bulky teaching in resources | +| [`elicitation-to-ir-oracle-design.md`](../../specs/elicitation-to-ir-oracle-design.md) | Eight quality claims, hard-failure gates, mistake taxonomy, and source-to-home method | Evaluation design hypothesis with calibrated artifacts | Converts broad virtues into observable failures; most detection detail belongs in evaluation, not the prompt | +| [`vestera-legacy-baseline/readout.md`](../evaluations/vestera-legacy-baseline/readout.md) and [`vestera-prospective-baseline-v1/campaign-adjudication.md`](../evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md) | Observed failure and success ranges under different prompt/runbook conditions | Local run evidence; small samples | Grounds invention, hardening, stopping, opening-load, acquisition variability, and strong behavior to preserve without treating one run as representative | +| [`agentic-elicitation-challenges`](../../research/elicitation/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md) and [`criteria`](../../research/elicitation/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md) | The early interactive-compiler framing, semantic conservation, explicit transformation, controlled elicitation, and swappable targets | Imported design conversations | Useful conceptual sieve; not direct prompt copy and not independent research evidence | + +## Candidate material by placement + +### Always-on core system prompt + +These propositions are short, context-independent, relevant before any skill resource is read, and supported by either observed failure or multiple evidence classes. + +| Candidate invariant | Existing formulations | Evidence and confidence | Why always-on | +| --- | --- | --- | --- | +| **Context-independent elicitor identity** | Current core: “You are the Brunch elicitation assistant.” The v0 prompt used “expert process-model elicitor,” which is too narrow. | Product-policy decision; no empirical claim needed | Establishes role without selecting a domain, editor, workpiece, or formalism | +| **Purpose-relative attention** | Repertoire: “Objectives first” and “Depth is objective-relative.” Research synthesis: establish the decision, comparison, or worry, audience, boundary, horizon, and accuracy need. Literature: objectives determine responses, factors, scope, and detail. | Strong: verified simulation-modeling literature plus local stopping/acquisition failures; exact opening script remains unsettled | Every later question-selection and stopping judgment needs a purpose denominator; the detailed stance interview stays in the skill | +| **Expert vocabulary, not target-schema traversal** | Active skill: follow the expert’s thread; do not interview by workpiece headings. Oracle gate: schema-shaped interviewing. Mission 3 observed operational vocabulary without PN leakage. | Medium-high: design principle, evaluator gate, and local positive observation; no comparative proof that jargon always harms acquisition | It must constrain the first substantive question, before lazy teaching is available, and remains valid for every plugin | +| **Authorship and uncertainty remain distinct** | Repertoire and active skill: a value the expert did not give cannot appear as theirs; assumptions are stated as the agent’s with reason and check; tension is preserved. Challenges/criteria: semantic conservation and explicit transformation. | Strong: local invention/hardening failures, published LLM hallucination, and prospective evidence that explicit partials can remain cold-usable | This is the stable trust contract across every domain and target; detailed workpiece marks and capture machinery stay outside the prompt | +| **No independent opening battery** | Repertoire: opening battery is failure; active skill: batch two to four only under one frame. Universal synthesis: hard rule is no independent battery before the first answer; the numeric batching license is unearned. | Medium: two historical Mission 3 opening batteries and FM-12; no opening overload in either valid prospective run; literature supports one-question/depth but not one universal count | Placement, not just content, was observed to matter: lazy teaching arrived after the offending first turn. The exact post-opening batching policy belongs in the skill | +| **Divergence is information, not permission to reconcile** | Repertoire: two answers in tension; sources that disagree; consistency probe. Literature: ambiguity exposes tacit knowledge and disagreement must not be silently averaged. | Strong for preservation; medium for exact questioning cadence | The prohibition on silent reconciliation is universal and compact; detailed correction-versus-coexistence handling belongs in the skill/workpiece | +| **Completion is not fluency; stopping yields an honest partial** | Repertoire: “End properly,” “Honour a stop,” and “Name the stopping outcome.” Failure catalogue: premature accommodation, budget exhaustion, and fluent incompleteness. Research synthesis: stop on evidence-bearing criteria, not fluency, fatigue, turn count, or self-report. | Strong that false stop rules fail: local legacy failures plus independently published LLM early-stop and budget-exhaustion behavior. The active path lacks typed completion machinery. | A concise behavioral guard is honest; claiming that the harness computes completion would be false. Detailed close ritual and target sufficiency stay outside the prompt | + +### Core elicitation skill body or lazy resource + +These are generic, but too procedural, conditional, lengthy, or evidence-sensitive for the always-on prompt. + +- **Stance before structure:** purpose, audience, boundary, horizon, accuracy need, time/appetite, and tolerance for proposed assumptions, sampled conversationally rather than administered as a form. +- **Concrete case before generalization:** a bounded three-to-six-step account, one remembered occasion from trigger to end, then one property across what that case revealed. +- **Deepening moves:** last-time probes; cue and observable follow-ups; no bare why as the primary probe; contrastive and expert–novice questions; anchored hypotheticals; change technique when yield drops. +- **Practice versus policy:** normative language and documents are claims to test against recent practice, not practiced facts. +- **Quantitative judgment:** determine whether typical or tail matters; use quantiles rather than min/mode/max; one incident is not a rate; use the clairvoyant test when definitions are unstable. Exact scripts should remain lazy and source-labelled. +- **Assumption and deferral licenses:** propose low-risk structure for correction only with agent authorship visible; defer only with what is missing, why, and where it would come from. +- **Divergence handling:** ask whether a later account is a correction or a context in which both apply; preserve unresolved alternatives and long-range contradictions. +- **Closing procedure:** summarize, name assumptions and consequential gaps, offer one correction opportunity, use a clearinghouse only as a cheap correction rather than coverage proof, and open no new topic after a stop. +- **Rare-event and taxonomy techniques:** premortem, CDM/ACTA probe families, laddering, card sorting, triadic comparison, exception sweeps, and tradeoff pairs. These are disclose-on-strain resources, not default prompt furniture. + +### Plugin-owned material + +- What kinds of things the selected formalism needs investigated and how to recognize them. +- Workpiece structure, emission/recovery convention, and target-specific sufficiency. +- SDCPN situation typologies, Petri-net mapping, Petrinaut construction/check behavior, and target projection loss. +- Any vocabulary such as place, transition, token, colour, firing, SDCPN, Petrinaut, runbook IR, or `runbook-ir` fence. + +### Evaluator- or machinery-owned material + +- Detection signatures, weighted score dimensions, hidden ledgers, mistake IDs, question counts, token/turn budgets, and comparative thresholds. +- Claims that captures, folds, completion demands, affected slices, provenance links, or projection loss reports are mechanically computed. The current production path does not provide those mechanisms. +- Stable identity, typed claim schemas, capture-store commands, observer folds, and plugin proposal contracts. +- Absolute completion claims. The verified literature supports purpose-relative sufficiency and explicit gaps, not knowable exhaustive completion. + +## Existing prompt forms and what they teach + +### The one-line current core + +Strength: correctly names no domain or output formalism after the latest ownership correction. Weakness: it gives the model no stable trust, attention, interaction-load, or stopping contract. All consequential conduct is currently contingent on plugin activation and resource reads. + +### The v0 prompt + +Strength: the clearest compact historical sequence and the origin of much current teaching. Weakness: it makes a seven-category process-model surface the completion checklist, mixes universal method with process-model content, and is large enough to encourage schema-shaped interviewing. Reuse its seven generic move headings as source material, not its complete system prompt. + +### The repertoire + +Strength: the richest provenance-bearing inventory, including licenses and anti-guidance lost from the active compression. Weakness: it is a catalogue, contains unresolved or one-run-vindicated choices, and was designed to be rendered with typed plugin/completion machinery. Use it to populate and test a core skill, not as one large prompt. + +### The active SDCPN elicitation resource + +Strength: a terse, field-usable compression that valid prospective runs used without hard failures. Weakness: it explicitly mixes universal and SDCPN material; universal rules arrive too late to constrain skill-activation turns; several rules are labels without triggers; acquisition varied materially across the two valid runs. + +### `HARNESS_PREAMBLE` + +Strength: compact statements of provenance and completion intent. Weakness: four of five sentences describe suspended capture/fold/completion/affected-slice behavior. Importing them would make the prompt lie about the active runtime. Only the non-invention distinction may be restated behaviorally, without machinery claims. + +### Condition 3 + +[`condition-3-prompt.md`](../../../evaluations/protocols/legacy-baseline/condition-3-prompt.md) combined the v0 identity with single-session controls, operator-supplied completion diagnostics, CPS cards, bounded batching, status/grade language, and respectful close. It was never run and was retired. Its close and evidence fragments remain candidates for progressive teaching; diagnostic coordinates and CPS cards are evaluation/plugin material. + +### Conditions 4 and 5 + +The exact [Condition 4 system prompt](../evaluations/vestera-legacy-baseline/transcripts/condition-4-system.md) rendered the complete repertoire and SDCPN plugin into one large text-only prompt. The exact [Condition 5 system prompt](../evaluations/vestera-legacy-baseline/transcripts/condition-5-system.md) added ask, settlement, capture/fold, and typed completion instructions. These are valuable archaeological snapshots and negative controls: adding comprehensive teaching and mechanism claims did not make them suitable always-on core prompts, and Condition 5 imposed minute-scale foreground work. + +### Frozen Mission 3 runbook + +The prospective v1 control at source commit `b738aa1be1a62a9f9cdde89ced78558f04293a77` is the exact predecessor of the current package-owned skill. It supplies no additional semantic source beyond the active files, but it is the immutable behavioral control against which prompt placement changes must be compared. + +### Stock Petrinaut prompt + +`libs/@hashintel/petrinaut-core/src/ai.ts` contains a separate active Petrinaut prompt with “interview first, build second,” grouped questions, process/timing/metrics/scenario coverage, tool policy, and an offer to “make it up/use sensible defaults.” It is useful as coverage evidence but not reusable Brunch policy: most of it is editor/formalism-specific, and invented defaults conflict with Brunch’s provenance contract. + +## Provisional compact compilation + +This is a candidate assembled from existing material, not a recommended edit yet. Each paragraph should be accepted, rejected, or relocated independently before implementation. + +```text +You are the Brunch elicitation assistant. Help a person make their knowledge explicit enough for the selected purpose and target without assuming a domain or output formalism. + +Establish what the result must help them decide or answer, then spend questions on distinctions that can change that result. + +When speaking with the person, use their vocabulary and follow concrete cases rather than traversing a schema or target representation. Do not open with a battery of independent questions; deepen one answerable thread at a time, and group questions only when they share one frame. + +Preserve authorship and uncertainty. Distinguish what the person said from your inference, assumption, unknown, ambiguity, conflict, correction, omission, or default. Never invent content, silently make a hedge more precise, or treat assent to your own wording as independent evidence. + +Treat tensions as information and ask before reconciling them. + +Do not treat fluency, document fullness, your own confidence, user fatigue, or a turn budget as completion. If the person stops, return the best useful partial result with consequential gaps and assumptions named. +``` + +## Conservative smaller candidate + +If the first prompt iteration should include only the highest-confidence trust and routing invariants, defer the disputed batching and fuller stopping language: + +```text +You are the Brunch elicitation assistant. Help a person make their knowledge explicit enough for the selected purpose and target without assuming a domain or output formalism. + +Establish what the result must help them decide or answer, and use their vocabulary rather than traversing a schema or target representation. + +Preserve authorship and uncertainty. Never invent content, silently harden an answer, reconcile conflicting accounts without asking, or present your own wording as the person’s evidence. + +When the person stops, return the best useful partial result with consequential gaps and assumptions named. +``` + +## Decisions still required before editing the core prompt + +1. Whether “selected purpose and target” is useful generic orientation or unnecessary abstraction in the identity paragraph. +2. Whether the observed first-turn placement failure justifies an always-on no-opening-battery sentence despite no opening-overload gate in the two valid prospective runs. +3. Whether core should say “one answerable thread at a time,” “one question at a time,” or only prohibit independent batteries; the `2–4` batching number is not independently established. +4. Whether the always-on epistemic list should name all distinctions or state the compact governing prohibition and leave the vocabulary to the core skill/workpiece. +5. Whether stopping language should name false stopping rules or only require honest partial delivery; core must not imply typed completion machinery that is suspended. +6. Whether “person,” “expert,” “user,” or “source” is the generic counterpart. The literature warns that domain experts may not be modelers, while Brunch may eventually elicit from non-expert stakeholders too. +7. Whether the core prompt should direct activation of a generic elicitation skill. Flue already presents mounted skill descriptions; plugin-specific skill names do not belong in core. + +## Recommended next move + +Review the conservative candidate one paragraph at a time against the decisions above. Then create a core elicitation skill from the generic teaching inventory before adding more detail to the always-on prompt. The first candidate evaluation should preserve the frozen baseline and separately test whether moving the no-opening-battery and epistemic invariants into the always-on tier changes first-turn load or evidence fidelity. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md new file mode 100644 index 00000000000..a266f74263d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md @@ -0,0 +1,38 @@ +# Close Mission 4 with S4 routing deferred + +Date: 2026-09-03 + +Status: **owner accepted Mission 4 closure with the observed S4 failure and missing full-run candidate explicitly carried forward.** + +## Owner adjudication + +The owner judged the frozen v2 S4 threshold—a review that discovers an unresolved operational rule must immediately activate `elicitation` and ask the person for that rule—to be a nice-to-have at the present Mission 4 boundary. It does not block branch closure. + +This decision does not reinterpret the frozen campaign. `m4-pol-v2-s4-p1` remains a technically valid item 4e failure: Brunch correctly identified the unresolved reviewer-release rule and did not invent a resolution, but it reported the gap without activating `elicitation`. The Industrial Gas run was correctly not admitted under the frozen serial stop rule, so Mission 4 produced no full-run workpiece candidate and did not establish its pre-registered `3/3` proof claim. + +## Accepted closure evidence + +The owner accepts the implementation and branch for closure on narrower observed evidence: + +- core independently mounts the `elicitation` capability and the SDCPN job skill activates it on both named interactive-entry cases before substantive operational questioning; +- the conditional SDCPN profile read precedes substantive reliance in both cases, and neither opening is a Battery; +- the exact resolvable-review case refrains from elicitation and identifies the supported target defect; +- the exact knowledge-gap review preserves the unresolved gap instead of inventing a rule, while exposing the deferred review-to-elicitation transition weakness; +- canonical Flue history, deterministic traces, fresh adjudications, and hash-bound run artifacts make each result inspectable; +- the production responsibility split—core capability, plugin job/formalism contribution, app composition/transport—remains the accepted implementation pattern. + +Closure does not claim general activation reliability, the frozen v2 `3/3` result, robust review-to-interview switching, workpiece quality, a retained full-run candidate, fixture/seed eligibility, Petrinaut browser parity, voice integration, deployment, or remote operation. + +## Deferred owner and re-entry gate + +The existing Mission 4 close-out addendum cluster in `MISSION.next.md` owns the observed S4 case unless a later numbered mission first makes it load-bearing. Its concern is narrowly: when review exposes consequential operational knowledge absent from the supplied evidence, decide whether the product should ask immediately or report and stop; if immediate clarification is required, make `sdcpn-modelling` activate `elicitation` before asking and rerun the exact S4 negative/positive pair under a fresh instrument. + +Re-enter only when a real review workflow needs to continue in the same turn or repeated gap-only reports impose user-visible friction. Oracle: the exact S3 case still refrains, the exact S4 case activates and asks without inventing, and a product-path review demonstrates the chosen report-versus-ask policy. Do not reopen the accepted topology merely because one routing transition is deferred. + +No Mission 4 addendum issue or branch exists yet. This record creates neither; it preserves the concern for a future owner-authorized cut. + +## Downstream and voice consequence + +Mission 5 and later drafts must not consume a nonexistent Mission 4 workpiece candidate. A future selected prebuilt pair must instead be prepared from an explicitly eligible retained workpiece source—such as the immutable Mission 3 evidence—or from a new addendum-owned run, with its own identity, provenance, and promotion decision. + +Voice-mode work is a parallel integration parent, not evidence that changes this closure. Reconciliation must preserve the current package-composed `ChatAgent` and port Voice's dynamic ask, answer-provenance, and turn-taking changes onto the current app/conversation paths rather than restoring the older app-local stub agent. Brunch continues to own question choice and canonical text; Voice owns audio interaction and provenance, not elicitation policy. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-final-restack-provenance-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-final-restack-provenance-2026-09-03.md new file mode 100644 index 00000000000..7b0ee15ddce --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-final-restack-provenance-2026-09-03.md @@ -0,0 +1,28 @@ +# Mission 4 final Graphite restack provenance + +Date: 2026-09-03 + +Status: **verified after `gt sync && gt restack`; content manifests are authoritative and commit SHAs are historical provenance only.** + +## Restack result + +Graphite synchronized `ln/fe-1563-redesign-runbook-workpiece` and restacked it onto its updated parent `ln/fe-1525-headless-runbook-pn` without a conflict on this branch. Graphite separately reported unrelated branches that could not be restacked cleanly or were checked out in other worktrees; those branches are outside Mission 4 and were not modified here. + +The restack rewrote commit identities but not instrument or run-artifact content. Durable evidence identity comes from the manifest digest and its ordered path/content hashes: + +- v1 manifest SHA-256: `ec6399fd19914b15c9fe5e43d268b56f3e4816e128b02f3e694f7a807e7d1987`; +- v2 manifest SHA-256: `91bc02e59dad3ed2d7791e3e1b095435c18fca8c78b4302e9e3bb43872e727a9`. + +The `instrumentCommit` and `executionHead` values retained in accepted manifests and `run.json` files state where those bytes lived when execution occurred. They are useful historical provenance, not evidence primary keys, current-ancestry requirements, or promises that Git will retain those objects forever. The records remain unchanged because they accurately describe execution time, not because current consumers must resolve those SHAs. + +`git patch-id --stable` and complete manifest verification confirmed that the first post-sync chain was patch-equivalent and that both frozen instrument manifests still matched all 33 and 35 current worktree files respectively. A later `gt sync` rewrote the chain again when its parent advanced, demonstrating why a maintained old-to-new SHA map would be churn rather than durable evidence. No such map is retained. Every per-run manifest remains valid. + +## Future campaign rule + +Future protocols should separate `instrumentId` from `provenanceAtExecution`. `instrumentId` is the manifest SHA-256 plus its path/content hash set. `provenanceAtExecution` may record source and execution commits as informational locators. A rebase or Graphite restack requires content verification—not refreezing, rewriting historical runs, preserving old commit reachability, or maintaining a current-equivalent SHA table. If permanent Git-object retention is genuinely required, earn and name an explicit durable ref or archived bundle rather than relying on reflogs or PR history. + +## Voice-stack landscape after synchronization + +The Mission 4 stack and current Voice stack remain parallel. Their observed merge base is `807fc0481ae3eed147f911d5d4a49ef9031a8afe`; neither is the other's parent. The Voice stack begins from `kostandin/fe-1570-voice-optimized-brunch-responses` (PR #9496, base `main`), continues through `kah-6763-temporary-brunch-ask` (PR #9507), and ends at `kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance` (PR #9512). Coordination therefore requires an explicit reconciliation branch or parent choice after the involved PR owners choose integration order; ordinary `gt restack` on Mission 4 does not combine them. + +The detailed file/ownership collision map remains in [`mission-4-voice-integration-handoff.md`](../implementations/mission-4-voice-integration-handoff.md). Its main constraint survives synchronization: port Voice behavior into Mission 4's package-composed agent and relocated conversation modules rather than restoring the Voice branch's older app-local stub. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-inline-universal-elicitation-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-inline-universal-elicitation-2026-09-03.md new file mode 100644 index 00000000000..e4d2f22b0ab --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-inline-universal-elicitation-2026-09-03.md @@ -0,0 +1,30 @@ +# Inline universal elicitation guidance into the capability skill + +Date: 2026-09-03 + +Status: **owner-accepted pre-freeze repair.** This record authorizes the focused production and oracle change described below; it does not freeze a campaign or authorize paid calls. + +## Observed failure and responsible layer + +Inspection of the accepted topology found that `elicitation/references/universal-elicitation.md` had exactly one consumer and was required on every activation before substantive interviewing. The resource boundary therefore offered no conditional disclosure: activation always had to be followed by a separate model-authored `read_skill_resource` call. The boundary failed its responsibility test by adding an avoidable tool-selection, path-selection, and continuation failure point without withholding any guidance that could legitimately remain unloaded. + +The responsible layer is the core `elicitation` skill's authored disclosure shape, not Flue's resource mechanism, the SDCPN plugin, the persona harness, or the accepted independent capability topology. + +## Smallest accepted repair + +- Move the operative contents of `references/universal-elicitation.md` into `elicitation/SKILL.md`, after the accepted capability scope. +- Remove only the obsolete reference heading/preamble and the wrapper's obsolete read/resource-discipline instructions; preserve the operative Directives, Recognition, Operations, Coverage, and Verification text unchanged. +- Stop packaging files from `elicitation/skill.ts` and delete the now-empty `references/` directory. +- Keep the independently mounted and activated `elicitation` capability skill. Do not split the universal guidance into speculative conditional resources. +- Keep plugin profiles conditional. Update SDCPN and Gherkin wording to refer to universal guidance in the activated capability rather than a deleted filename. +- Update focused tests and the accepted ruler: `sdcpn-modelling` then `elicitation` activation supplies universal guidance, while `references/profile.md` remains the required conditional read before substantive reliance. + +## Regression risk and oracle + +The main risk is dropping or changing operative universal guidance while moving it, or accidentally packaging a stale resource path. Compare the post-repair `SKILL.md` text from “The registers are addresses” onward with the pre-repair resource from the same marker onward; the bytes must match. Focused tests must prove that the capability has no packaged files, still contains all five guidance sections, the production `ChatAgent` still activates both skills and reads the SDCPN profile, and ordinary-workpiece checks require the profile and template rather than the deleted universal resource. + +The campaign remains pre-freeze. Any protocol or retained manifest must hash the repaired `SKILL.md` and must not require or advertise `references/universal-elicitation.md`. + +## Owner decision + +The owner accepted this exact inlining repair in conversation on 2026-09-03 because the mandatory resource call was an avoidable failure mode. The acceptance does not authorize a broader rewrite, a register split, production prompt tuning, or a topology change. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-owner-gates-2026-09-02.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-owner-gates-2026-09-02.md new file mode 100644 index 00000000000..64791366fc0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-owner-gates-2026-09-02.md @@ -0,0 +1,13 @@ +# Mission 4 owner-gate clarification + +Date: 2026-09-02 + +The owner confirmed in the Mission 4 repair session: + +> I did give those authorizations, but out of frustration with how long it was running. + +This confirmation applies to the prior US$10 paid ceiling, campaign acceptance, witness-only exact-URI repair, witness acceptance, and attempted closure. Those actions were historically authorized; they were not fabricated or unauthorized spend. + +The owner also directed that the builder's exercise and declaration of parent-reserved freeze, adjudication, witness acceptance, handoff selection, and closure “must be undone,” and that every spec failure identified by review “should be restored/repaired/re-done.” The previous acceptance and closure are therefore withdrawn. Mission 4 is live again. + +The owner subsequently authorized the parent to run the frozen three-member v4 candidate campaign and complete omniscient/cold grading for each valid member under a combined US$10 ceiling, after the instrument was folded onto FE-1563 and its clean hermetic suite passed there. V4 produced zero valid members, no grader ran, and its abort record consumes this authorization. The authorization excluded the later visible product witness and does not carry into v5. Campaign acceptance, a successor paid ceiling, witness acceptance, handoff selection, and closure remain with the parent and owner under `MISSION.md`. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-freeze-acceptance-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-freeze-acceptance-2026-09-03.md new file mode 100644 index 00000000000..66f52b989ad --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-freeze-acceptance-2026-09-03.md @@ -0,0 +1,11 @@ +# Mission 4 proof-of-life v1 freeze acceptance + +Date: 2026-09-03 + +Status: **accepted by the owner; paid execution authorized within the exact ceiling below.** + +The owner accepted [`instrument-manifest.json`](../../../evaluations/protocols/mission-4-proof-of-life-v1/instrument-manifest.json) committed at `cc9a68497d` as the frozen Mission 4 proof-of-life v1 instrument. The manifest binds 33 instrument files to source commit `ce2fbde9d96faaaf52ecf532e1071d3d9e952f1a`. + +The owner authorized at most **$10 USD** for this campaign under the frozen serial rules: at most 10 conversation attempts, 32 visible Brunch submissions, 28 persona continuations, and 10 fresh adjudications. Each of the five required slots has at most one fresh-id replacement for technical invalidity or failure to reach a Substantive question. A valid behavioral failure is retained and stops execution for owner adjudication; it is never replaced. + +This acceptance authorizes no fallback model, router substitution, instrument edit, topology change, product/browser claim, workpiece-quality claim, fixture/database-seed promotion, or later mission work. A changed frozen file requires a new protocol version and owner decision. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-preflight-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-preflight-2026-09-03.md new file mode 100644 index 00000000000..1bb934f3f5e --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-preflight-2026-09-03.md @@ -0,0 +1,53 @@ +# Mission 4 proof-of-life model and cost preflight + +Date: 2026-09-03 + +Status: **non-billable preflight complete; freeze and paid authorization pending.** No model invocation was made while gathering this evidence, and no secret value was printed or retained. + +## Credential and catalog availability + +The local environment reports non-empty `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `OPENROUTER_API_KEY` variables. Only presence was inspected. + +`pi --list-models` resolves all selected direct-provider catalog entries: + +| Role | Exact requested entry | Context | Maximum output | Thinking | +| --- | --- | ---: | ---: | --- | +| Elicitor | `anthropic/claude-sonnet-4-6` | 1M | 128K | yes | +| Persona | `openai/gpt-5.6-sol` | 272K | 128K | yes | +| Adjudicator | `anthropic/claude-opus-4-6` | 1M | 128K | yes | + +This proves local catalog and credential presence, not a successful paid invocation. No fallback or router substitution is authorized. Each retained run must record the requested and provider-reported model ids; a mismatch is technical invalidity and stops or consumes the slot's sole replacement under the protocol. + +## Published prices + +Official sources inspected on 2026-09-03: + +- Anthropic, [Pricing](https://docs.anthropic.com/en/docs/about-claude/pricing): Claude Sonnet 4.6 is $3 per million base input tokens and $15 per million output tokens; Claude Opus 4.6 is $5 per million base input tokens and $25 per million output tokens. Default global inference pricing is assumed; US-only inference would add 10% and is not selected. +- OpenAI, [API pricing](https://developers.openai.com/api/docs/pricing): GPT-5.6 Sol short-context standard pricing is $4 per million input tokens and $20 per million output tokens. The selected persona budget stays far below the listed long-context threshold. + +Prompt-cache discounts are not assumed. Tool/system overhead and reasoning tokens are included in observed provider usage where reported and charged according to the provider response. + +## Planning estimate + +The estimate is deliberately conservative because exact usage is observable only after execution. + +| Role | Normal planning tokens | Normal cost | Worst-case planning tokens | Worst-case cost | +| --- | --- | ---: | --- | ---: | +| Sonnet elicitor | 250k input, 15k output | $0.98 | 500k input, 30k output | $1.95 | +| GPT-5.6 persona | 150k input, 30k output | $1.20 | 400k input, 80k output | $3.20 | +| Opus adjudicator | 120k input, 15k output | $0.98 | 300k input, 40k output | $2.50 | +| **Total** | | **$3.16** | | **$7.65** | + +The normal estimate covers five successful conversation attempts, about 14 visible Brunch submissions, about 12 persona continuations, and five adjudications. The worst-case estimate covers the accepted logical ceiling of 10 attempts, 32 Brunch submissions, 28 persona continuations, and 10 adjudications. Internal Sonnet tool continuations are included in the token allowances rather than counted as visible submissions. + +A proposed hard currency ceiling is **$10 USD** for the whole Mission 4 proof-of-life campaign, including replacements and adjudications. Record provider-reported usage after each settled attempt and adjudication. Stop before admitting the next paid action when cumulative reported cost reaches $10 or when the remaining allowance cannot cover that action under the worst observed same-role action cost. A single provider response can overshoot an estimate, so the logical ceilings and serial execution order remain the primary preventive bounds. + +## Remaining gates + +Before the first paid call: + +1. Commit the final instrument content and machine-readable hash manifest. +2. Pass the full focused package build/type/lint/unit suite and immutable Mission 3 comparison at that commit. +3. Obtain explicit owner acceptance of the freeze manifest and the $10 USD currency ceiling. + +This document is evidence for those decisions; it does not perform them. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md new file mode 100644 index 00000000000..684c083b4e8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md @@ -0,0 +1,9 @@ +# Suspend the Mission 4 v2 currency gate + +Date: 2026-09-03 + +Status: **owner decision.** + +After reviewing the unresolved cost of the two v1 Sonnet submissions, the owner suspended currency-budget gating for Mission 4 v2 because it is not a primary concern. V2 therefore has no active USD stop threshold. The $3.16 normal and $7.65 worst-case estimates remain planning context, and all available provider/Pi usage must still be retained and reported after each action. + +This decision does not relax the frozen logical ceilings, serial slot order, validity and replacement rules, model/provider allocation, evidence retention, or stop-on-valid-behavioral-failure rule. It does not accept the v2 ruler or manifest by itself and does not authorize execution against an instrument whose exact frozen manifest has not been accepted. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-freeze-acceptance-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-freeze-acceptance-2026-09-03.md new file mode 100644 index 00000000000..bdb50b1cf08 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-freeze-acceptance-2026-09-03.md @@ -0,0 +1,11 @@ +# Mission 4 proof-of-life v2 freeze acceptance + +Date: 2026-09-03 + +Status: **owner accepted; paid execution authorized with currency gating suspended.** + +The owner explicitly accepted the Mission 4 v2 ruler and the 35-file [`instrument-manifest.json`](../../../evaluations/protocols/mission-4-proof-of-life-v2/instrument-manifest.json) committed at `d9ca2fe1498f6484746b2abaaf18973e7abcbeaa`, SHA-256 `91bc02e59dad3ed2d7791e3e1b095435c18fca8c78b4302e9e3bb43872e727a9`. The manifest binds instrument commit `95954b494308fbba384cc4ce169a813916f164f9`. + +The owner authorized execution by saying, “I explicitly accept. let's go.” Currency gating remains suspended under [`mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md`](mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md); usage reporting remains required. All logical ceilings, exact model/provider assignments, host `none`, fresh attempt ids, serial order, replacement rules, retention requirements, and stop-on-valid-behavioral-failure rules remain binding. + +This acceptance changes no frozen instrument byte. Any later instrument or model-setting change requires another protocol version and owner decision. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-preflight-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-preflight-2026-09-03.md new file mode 100644 index 00000000000..c67e2821aaa --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-preflight-2026-09-03.md @@ -0,0 +1,30 @@ +# Mission 4 proof-of-life v2 model and cost preflight + +Date: 2026-09-03 + +Status: **non-billable v2 preflight complete; manifest acceptance pending and currency gating subsequently suspended by the owner.** No model invocation was made while preparing v2. + +## Unchanged allocation and prices + +V2 keeps the direct-provider allocation accepted for v1: `anthropic/claude-sonnet-4-6` elicitor, `openai/gpt-5.6-sol` medium-thinking persona, and fresh-context `anthropic/claude-opus-4-6` high-thinking adjudicator, with no fallback and client-tool host `none`. The same-day credential/catalog and official-price evidence remains recorded in [`mission-4-proof-of-life-preflight-2026-09-03.md`](mission-4-proof-of-life-preflight-2026-09-03.md). + +V2 changes no model-facing production file, case, full-run objective, adjudication input, or model allocation. Its only behavioral instrument change is that each of the two interactive probes always makes three visible submissions rather than asking the isolated persona to classify the first Substantive text. The accepted logical ceilings remain 10 conversation attempts, 32 Brunch submissions, 28 persona continuations, and 10 adjudications. + +## V2 planning estimate + +The fixed probes use the same maximum three visible submissions already budgeted by v1. Normal success therefore remains approximately 14 Brunch submissions, 12 persona continuations, and five adjudications. The same token allowances and published prices produce the same standalone estimate: + +| Role | Normal cost | Worst-case cost | +| --- | ---: | ---: | +| Sonnet elicitor | $0.98 | $1.95 | +| GPT-5.6 persona | $1.20 | $3.20 | +| Opus adjudicator | $0.98 | $2.50 | +| **V2 total** | **$3.16** | **$7.65** | + +## Cumulative ceiling + +V1's retained Pi usage displays report $0.044 for the two persona sessions and $0.592 for the two adjudications, or **$0.636 known rounded spend**. Canonical Flue history and the local Flue database contain no provider usage for the two Sonnet elicitor submissions. The environment has a standard Anthropic API key but no Admin API key; a read-only request to the organization usage-report endpoint returned HTTP 401 with `The Admin API requires an Admin API key or an organization-scoped API key.` No credential value was printed or retained. + +Let **S** be the actual v1 Sonnet spend. The exact remaining allowance under the original cumulative **$10 USD** Mission 4 ceiling is **$9.364 − S**. The standalone v2 worst-case estimate of $7.65 fits only if **S ≤ $1.714**. V2 does not create a second allowance, and this preflight does not replace the unknown with a planning reserve or treat it as zero. + +This unresolved value initially blocked remaining-spend authorization. The owner subsequently suspended currency gating for v2; see [`mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md`](mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md). Reconciliation remains desirable accounting evidence but is no longer an execution gate. Exact manifest acceptance, logical ceilings, serial stop rules, and per-action usage retention remain mandatory. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-restacked-authority-sha-audit-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-restacked-authority-sha-audit-2026-09-03.md new file mode 100644 index 00000000000..1ca6de64277 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-restacked-authority-sha-audit-2026-09-03.md @@ -0,0 +1,35 @@ +# Mission 4 Graphite-restacked authority SHA audit + +Date: 2026-09-03 + +Status: **audit complete; the owner-accepted proof-of-life authority recut incorporates the recommended live pointers.** The later owner-accepted inlining repair intentionally changes the protected elicitation skill surface from this audit's baseline and is recorded separately in [`mission-4-inline-universal-elicitation-2026-09-03.md`](mission-4-inline-universal-elicitation-2026-09-03.md). This is provenance and authority-boundary evidence, not an instrument freeze or campaign result. + +## Scope + +The audit classified commit pointers used by live [`MISSION.md`](../../../MISSION.md), the current Mission 4 architecture evidence, the immutable Mission 3 control, and the related future-planning reference. It compared the exact protected source, production, retired-mechanism, and control-instrument files rather than assuming equal subjects or Graphite patch ancestry implied equal authority. + +At audit time, `HEAD` and `origin/ln/fe-1563-redesign-runbook-workpiece` were `3ba14d5d10bc282e6e2e35310368493d4324224d`. The restacked SHAs below were ancestors of that head and reachable from that remote branch. The corresponding historical SHAs were locally available but were neither ancestors nor reachable from a remote-tracking branch. + +## Classification + +| Pointer | Role and observed equivalence | Classification | Required treatment before freeze | +| --- | --- | --- | --- | +| `93eb211dd3d7fa07bc5b1ff69ddb402b45b07cf9` → `baba973269ce7ecf1a47de8749c751033b2ce471` | Original and restacked commits have the same subject but different stable patch IDs because conflict resolution changed their surrounding patch. Direct snapshot comparison found the complete source-to-production manifest's protected topology, prompt, skill, resource, mounting, stub, and retired-path surfaces byte-identical. The only snapshot differences under the context root are eight unrelated transport/client-tool/build/spec files outside that protected manifest. | Current-branch implementation authority, with original historical provenance. | Make `baba973269ce7ecf1a47de8749c751033b2ce471` the operative implementation/topology pointer in live status, manifest, and throughline. The first live occurrence may dual-pin `93eb211dd3d7fa07bc5b1ff69ddb402b45b07cf9` as the pre-restack historical identity; do not leave the orphan as the only authority. | +| `e087f570d77507c12a4862604a30c6fcd640aa2f` → `ca57b45729260cc657f89b718fc505997a4e1b3c` | Stable patch IDs match (`6e5a4b002a3e90d7858e4b5092fc04446c4e8841`). Direct snapshot comparison found the selected Ampcode and Five-Register source trees byte-identical. | Dual-pinned: current source authority plus original recovery provenance. | Make `ca57b45729260cc657f89b718fc505997a4e1b3c` the live manifest's resolvable source pointer and retain `e087f570d77507c12a4862604a30c6fcd640aa2f` as the historical source/recovery identity. The architecture evidence's existing `e087f570d7` references intentionally describe original recovery and remain historical evidence, not execution authority. | +| `924be780ce6a5e7ebbbc0e43b72042ceb93c8387` → `f7f77544dab022be667f535ca73181ddc57535e0` | Stable patch IDs match (`10b072b5e0b4c903f7d13ec3442145af632c60ed`). Direct snapshot comparison found the retired YAML/typed-plugin paths named by the live manifest byte-identical at both commits. | Current-branch last-present authority, with original historical provenance. | Use `f7f77544dab022be667f535ca73181ddc57535e0` as the operative last-present pointer in live authority. Preserve `924be780ce6a5e7ebbbc0e43b72042ceb93c8387` parenthetically when the original pre-restack chronology matters. | +| `b738aa1be1a62a9f9cdde89ced78558f04293a77` → `57b8900a04c56aa9e0d833fcbab8d290ab9756eb` | Stable patch IDs match (`f26a143ecf2b6ace5a4fcd573a964d78f86d9b52`). All 16 source files hashed in each valid Mission 3 run manifest are byte-identical at both commits, and the historical commit's bytes match every retained manifest hash. | Immutable historical campaign provenance, optionally dual-pinned for resolvability. | Do not rewrite the retained Mission 3 run records, adjudication, or their `sourceCommit`; they truthfully identify the executed campaign. Live authority may name `57b8900a04c56aa9e0d833fcbab8d290ab9756eb` as the current-ancestry patch-equivalent while preserving `b738aa1be1a62a9f9cdde89ced78558f04293a77` as the frozen control's recorded source identity. | +| `4c11c7a6c4e1df26c9d76cec30e32af8f013042d` | Current-ancestry commit `Close Mission 3 with prospective baseline evidence`. The immutable `vestera-prospective-baseline-v1` tree has no diff from this commit to audit-time `HEAD`. It is byte-identical to the equivalent historical Mission 3 close snapshots inspected. | Current-branch immutable-control evidence baseline. | Proof item 12's unchanged-evidence comparison must start at this Mission 3 close commit, not at the earlier campaign source revision `b738aa1...`, which predates the retained campaign evidence. Keep the campaign source identity and the evidence immutability baseline as two distinct authorities. | +| `acc4d935c7` and the other event SHAs in `docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md` | These pointers identify original historical mutations and deletions discussed by that evidence document; they do not authorize current implementation or freeze state. | Immutable historical evidence. | Leave unchanged. Their authority is the historical event they identify, not current branch reachability. | +| `93eb211dd3` and `924be780ce` in `MISSION.next.md` | These occur in non-execution planning prose describing removal and last presence. | Historical planning provenance; not live execution authority. | Reconcile to dual-pinned/current-ancestry wording before final close so future readers do not depend on orphan-only pointers, but do not treat this planning file as Mission 4 authority or combine its edit with oracle acceptance. | + +## Exact comparisons performed + +- Compared the original and restacked topology snapshots over core prompts and skills, all core/plugin `flue.ts` and `skill.ts` mounting files, SDCPN/Gherkin prompts and resources, the complete Dafny stub package, and every retired path named in the source-to-production manifest: no differences. +- Compared `packages/core/_drafts/ampcode/` and `packages/core/_drafts/five-register-synthesis/` at `e087f570d7` and `ca57b45729`: no differences. +- Compared the retired plugin/YAML/schema/teaching/interpretation/testing/binding paths at `924be780ce` and `f7f77544da`: no differences. +- Parsed a valid Mission 3 run's `instrument.fileSha256` map and checked all 16 files at `b738aa1...` and `57b8900...`: every pair was byte-identical and every historical byte hash matched the retained manifest. +- Compared `libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1` at `4c11c7a6c4` and audit-time `HEAD`: no differences. + +## Authority amendment boundary + +The SHA audit did not itself authorize a mission change. After the owner accepted the 2026-09-03 proof-of-life recut and Gate B ruler, the prepared [`MISSION.md`](../../../MISSION.md) authority change (1) makes current implementation/source/last-present pointers depend on the restacked ancestry while preserving historical provenance, (2) keeps Mission 3's recorded source identity distinct from its current-ancestry equivalent, and (3) compares immutable Mission 3 evidence from `4c11c7a6c4e1df26c9d76cec30e32af8f013042d`. Commit that authority recut separately from the accepted ruler artifact and from harness mechanisms, candidate text, campaign protocol, freeze, or closure. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-retire-v1-and-cut-v2-2026-09-03.md b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-retire-v1-and-cut-v2-2026-09-03.md new file mode 100644 index 00000000000..ce089e4e096 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-retire-v1-and-cut-v2-2026-09-03.md @@ -0,0 +1,25 @@ +# Retire Mission 4 proof-of-life v1 and cut v2 + +Date: 2026-09-03 + +Status: **owner accepted the v2 repair direction; v2 freeze and paid execution remain pending.** + +## Observation + +Both authorized Vestera v1 attempts reached the production Brunch `ChatAgent`, settled normally, and retained canonical Flue history. Each trace showed successful `sdcpn-modelling` activation, then successful `elicitation` activation, then the conditional SDCPN profile read before assistant text. Each assistant text asked only what scheduling decision the simulation should support. The fresh Opus adjudicator classified both texts as Orientation, so neither attempt contained a first Substantive text or satisfied the interactive proof floor. + +In both attempts the GPT-5.6 persona stopped after one visible submission and reported that Brunch had asked its first substantive operational question. The replacement did so even though Brunch called its own question “one orienting question.” Inspection of the exact Pi session records found no transport or extension stop signal: `brunch_turn` returned only Brunch's exact text, and the persona produced the stop report itself. + +## Cause + +The v1 probe objective told the persona to stop immediately after the first “Substantive operational question.” The persona's isolated context intentionally excluded the accepted ruler and contained no definition of Orientation or Substantive. V1 therefore delegated an evaluator-owned semantic classification to a model that the protocol simultaneously declared was not the oracle. The strongly terminal wording turned the persona's unsupported classification into a premature stop. + +This is an instrument defect, not evidence of Brunch activation or restraint failure. It entered with the original v1 protocol at `69e75ea363` and is not inherited from `SYSTEM.md`, `brunch_turn`, or another template. + +## Decision + +Retain the complete v1 instrument, primary, replacement, and adjudications as immutable informative failure evidence. Do not admit Data Centre or later v1 slots and do not reuse any v1 attempt id. + +Cut v2 with the same cases, production elicitor text, models, host, ordering, replacement rule, evidence mechanism, full-run objective, hard logical ceilings, and ruler semantics. Give each slot fresh `m4-pol-v2-*` attempt ids. Create a v2 ruler whose only change describes the new probe extent. Replace only the two interactive-probe stop conditions with an exact three-visible-submission bound: the persona sends the opening and two natural continuations unless the tool reports a genuine orchestration error. The fresh adjudicator alone locates the first Substantive text after settlement. Later retained turns cannot alter the activation and read ordering before that text. + +The initial v2 preparation retained the cumulative v1-plus-v2 $10 USD ceiling rather than granting a second allowance, recomputed the estimate, and left missing v1 Sonnet usage unreconciled rather than replacing it with an unexplained reserve or zero. The owner subsequently suspended currency gating for v2 in a separate decision. Bind the complete exact objective in the v2 instrument manifest and a SHA-256 regression check, and obtain fresh owner acceptance of the v2 manifest before any v2 model call. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md b/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md new file mode 100644 index 00000000000..7c679e1eb79 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-handoff-failure-analysis-2026-09-02.md @@ -0,0 +1,313 @@ +# Why Mission 4 went off track: the handoff failure analysis + +> Design evidence, not execution authority. Written on 2026-09-02 by the agent with which the owner developed the architecture reconciliation ledger, after the owner asked it to analyse why the branch diverged from the accepted design. A parallel read-only audit in the repair session reached the same symptoms; this document names the mechanism. Its one factual correction to the ledger (the freeze commit `f25891847e` promoted the Ampcode wording unchanged; the production rewrites came in `acc4d935c7` and `ca29b738aa`) was verified with `git log -S` and applied. The process rules in its final section were folded into [`AGENTS.md`](../../../AGENTS.md) the same day. + +--- + +The core failure was the absence of a protected **compilation boundary between the design conversation and delegated execution**. The branch had detailed authority rules, but it did not require the current design to be reduced to a compact, owner-reviewed implementation contract before another agent acted. As a result, each handoff summarized the previous summary, and semantic details became increasingly vulnerable to reinterpretation. + +The planning split was only one part of the problem. The more damaging sequence was: + +```text +owner design conversation +→ large chronological ledger +→ delegated split specification +→ MISSION.next summaries and future drafts +→ rewritten live MISSION.md +→ production promotion +→ evaluator interpretation +→ prompt rewritten to satisfy the evaluator +``` + +Every arrow permitted semantic change, but none had a semantic equivalence check. + +## What went wrong + +### 1. The split protected the future plan while leaving the current mission stale + +The split side quest explicitly required `MISSION.md` to remain byte-identical. That made sense as a scope guard, but by then the design conversation had substantially changed Mission 4. + +Consequently: + +- The current Mission 4 architecture was partly in `/tmp`, partly in the Ampcode prototype, and partly in `MISSION.next.md`. +- There was deliberately no Mission 4 draft because Mission 4 was already live. +- Yet the live `MISSION.md` still described the earlier “owner-led redesign” mission rather than the now-settled architecture and promotion mission. +- The initial split at `f483861ea8` omitted important Mission 4 architecture decisions and needed `738365aa80` plus the subsequent immutable-proof repair. + +This was the first structural mistake: **the active mission should have been reconciled before the future-planning split**. Instead, the split made the repository look orderly while the most important current decisions still lacked a complete executable home. + +### 2. The handoff chain repeatedly compressed meaning + +A revealing example is the question rule. + +The selected Ampcode text said: + +> group questions only when they share one frame + +The later mission language said: + +> exactly one focused question + +The walkthrough interpreted that as one question per turn. The strengthened checker interpreted it as one interrogative sentence and at most one `?`. Eventually the production prompt repeated that syntax-shaped rule across several disclosure layers. + +Those are not equivalent contracts: + +```text +one focused conversational frame +≠ one semantic question +≠ one interrogative sentence +≠ at most one question-mark character +``` + +Each handoff selected a narrower interpretation without returning to the owner. + +One correction to the reconciliation ledger: direct Git comparison shows that `f25891847e` did **not** yet rewrite the production core prompt—the promoted `SYSTEM.md` and `universal-elicitation.md` still matched the Ampcode sources. What `f25891847e` did was freeze a latent contradiction: the source allowed shared-frame grouping, while the rewritten mission and walkthrough used “exactly one.” The production wording was changed later in `acc4d935c7`, then changed again to mirror the checker in `ca29b738aa`. + +That distinction matters. The original defect was not merely an accidental prompt edit; it was an unresolved disagreement between the authority document, selected source, walkthrough, and oracle at the moment of freeze. + +### 3. The planning migration checked structure more strongly than semantics + +The split had extensive verification: + +- exact draft count; +- required warning headers; +- prohibited headings absent; +- links resolving; +- `MISSION.md` and archives unchanged; +- before/result commits pinned; +- formatting clean. + +Those are useful checks, but they establish document integrity, not decision integrity. + +The owner review subsequently found omitted Mission 4 decisions despite those checks. The repair restored them, but the same kind of semantic check was not applied when the architecture moved from planning into production. + +The missing test was approximately: + +```text +For every accepted decision: + exact accepted meaning + → one current authoritative statement + → one selected implementation source + → one production destination + → explicitly permitted deltas only +``` + +A heading inventory and named-mechanism inventory cannot detect that “shared-frame grouping” became “one `?`.” + +### 4. A narrow proxy was allowed to become architecture authority + +The original owner topology had an independently mounted core `elicitation` skill. The skill-composition side quest compared that with packaging the universal material inside the plugin job skill. + +The v3 evidence showed: + +- independent A: `0/3` activation successes in one discriminating scenario; +- packaged B: `2/3`; +- both behaved acceptably in the restraint cases; +- the review case was non-discriminating because both bypassed the shared job route; +- the overall protocol verdict remained invalid/both weak. + +That evidence legitimately established a routing risk. It did not establish that the owner’s conceptual topology should be replaced. + +Even with a pre-agreed decision rule, the actual result should have returned as: + +> Independent activation currently appears unreliable under this model/framework setup; packaged disclosure may mitigate it, but the full topology comparison did not validate either architecture across jobs. + +Instead it became: + +> The architecture is decided; Candidate B is production authority. + +This crossed an epistemic boundary. A model-routing experiment was allowed to settle an owner-level responsibility topology. The context-root guidance already says objectives, policy, and trade-offs settle with the owner while feasibility settles at the real boundary, but the mission process did not require the experiment’s **scope of authority** to be restated at adjudication. + +### 5. Too many transformations were combined into large commits + +`f25891847e` simultaneously: + +- rewrote the live mission; +- promoted the selected candidate; +- changed package composition; +- repaired review routing; +- created the campaign protocol; +- added tests; +- froze the instrument. + +That was 21 package files and roughly 1,100 changed lines. + +Later: + +- `12acbd931a` combined campaign adjudication, witness, handoff, archive, close, and draft disposal. +- `acc4d935c7` combined reopening, authority repair, protocol work, evaluation cleanup, prompt repair, and workbench disposal across 61 files. +- `ca29b738aa` combined campaign adjudication, candidate repair, checker repair, prompt changes, and the next protocol. + +These commits were locally purposeful but impossible to review along one semantic axis. A reviewer could not simply ask, “Did the selected architecture move into production unchanged?” because that movement was mixed with repairs and campaign machinery. + +### 6. The mission contained owner gates, but they were prose rather than stage boundaries + +The mission correctly reserved: + +- substantive candidate repair; +- budget approval; +- campaign adjudication; +- witness acceptance; +- handoff selection; +- closure. + +It also said that any repair must present the observed failure, smallest correction, and regression risk before editing. + +Nevertheless, the builder declared several of those gates satisfied in `12acbd931a`, and the v4→v5 repair was applied in the same commit as the failed campaign adjudication. + +So the guidance had named the right policy, but the workflow gave the writer no mandatory stopping point. “Owner retains authority” was treated as a fact an agent could record rather than an external action it had to wait for. + +### 7. Disposal removed the strongest explanation of the design + +The Ampcode README contained the actual architecture: + +- disclosure layers; +- semantic roles; +- channel-boundary tests; +- responsibility map; +- cross-plugin pressure tests; +- reasons not to duplicate universal teaching. + +`acc4d935c7` deleted it without relocating it, while preserving the losing Five-Register comparison. After that, the repository retained the sentence “Ampcode is the conceptual basis” but not the document explaining what that meant. + +That made subsequent repair agents more dependent on short summaries and historical reconstruction—the exact condition the planning split was intended to eliminate. + +## How it should have been done + +### Stage 1: Reconcile the active mission before splitting future planning + +Once the architecture discussion settled, the next operation should have been a documentation-only Mission 4 amendment: + +```text +design conversation +→ compact owner-reviewed Mission 4 architecture kernel +→ explicit owner acceptance +→ only then delegate implementation +``` + +That kernel needed to contain: + +1. The exact core/plugin topology tree. +2. The prompt/skill/tool responsibility test. +3. The owner-selected independent core `elicitation` capability. +4. Plugin cardinality as earned rather than symmetric. +5. The accepted Ampcode/Five-Register disposition. +6. The exact question-dosage decision, without ambiguous shorthand. +7. What the side-quest evidence could and could not change. +8. Exact immutable pointers to the selected paper artifacts. + +The old and amended `MISSION.md` should then have been reviewed and committed before any production file changed. + +### Stage 2: Preserve the conceptual source as evidence + +The Ampcode prototype should have been moved from `_drafts/` to something such as: + +```text +docs/evidence/design/mission-4-prompt-skill-tool-architecture.md +``` + +It would remain evidence rather than execution authority, while `MISSION.md` carried the binding decisions and linked to the fuller rationale. + +The important distinction is: + +```text +MISSION.md what must be implemented +design evidence why this shape was selected +selected source artifacts exact content to promote +evaluation evidence what happened when exercised +``` + +### Stage 3: Split only the genuinely future material + +After extracting all current Mission 4 decisions, the large ledger could have been divided by **change authority**, not chronology: + +| Material | Home | +|---|---| +| Current binding decisions | `MISSION.md` | +| Shared future locks and mission index | `MISSION.next.md` | +| Detail changing specifically with Mission 5/6/7/9 | corresponding draft | +| Accepted design rationale and rejected alternatives | design evidence | +| Experimental observations | evaluation evidence | +| Still-unresolved current choice | current `MISSION.md` fog-line | +| Still-unresolved future choice | relevant future draft | + +The chronological ledger could remain an archive, but no builder would need to read it to determine the current contract. + +### Stage 4: Give the implementation agent a promotion manifest, not the conversation + +The first implementation handoff should have been mechanical: + +| Selected source | Production destination | Permitted delta | +|---|---|---| +| Ampcode core prompt | core production prompt | path only | +| Ampcode universal reference | core skill resource | packaging/path only | +| Ampcode plugin append | plugin production prompt | path only | +| Ampcode SDCPN skill | plugin skill | verified Flue path adaptation only | +| Five-Register workpiece/checks | plugin resources | explicitly selected content only | + +Any wording change outside the permitted-delta column would stop and return to the owner. + +The first commit should have promoted the selected architecture without campaign or repair changes. A parent review could then compare source and production directly. + +### Stage 5: Handle each observed repair separately + +The review-routing and template-loading findings should each have followed: + +```text +observed failure +→ identify responsible disclosure layer +→ propose smallest change +→ state regression risk +→ owner accepts or rejects +→ one focused implementation commit +→ affected free checks rerun +``` + +This would likely have prevented skill-internal resource mechanics from leaking into the always-on append. + +### Stage 6: Prevent the oracle from redefining the decision + +Before freezing an evaluator, its interpretation of every design-sensitive rule should have been compared with the accepted contract. + +A useful rule is: + +> An oracle may falsify an implementation or claim; it may not silently strengthen or redefine the governing decision. + +Therefore, if the checker wanted “at most one `?`” while the selected design allowed a shared-frame group, that was an owner decision—not a checker implementation detail. + +Behavioral tests should generally observe: + +- whether the turn overloads the user; +- whether questions share a coherent frame; +- whether required resources were read before relying on them; +- whether the workpiece was created from its template; +- whether irrelevant resources were avoided. + +Literal prompt assertions should be reserved for text that is itself an exact contract. + +## How I would adjust the context-root mission guidance + +Much of the existing guidance was already correct. The failure was partly noncompliance, but several small additions would have made the handoff safer. + +I would add these rules rather than another large template: + +1. **Current-decision promotion:** When the owner accepts a decision that changes the live mission’s implementation or proof, amend `MISSION.md` before further delegation. `MISSION.next.md`, drafts, evidence, and transcripts cannot substitute for current authority. +2. **Separate authority amendment from implementation:** A material live-mission recut is committed and owner-reviewed before product or evaluation changes begin. Do not combine recut, implementation, freeze, or close in one transformation. +3. **Authority-preserving handoff:** Any handoff that translates an accepted design must identify the protected source, production destinations, permitted semantic deltas, and unresolved choices. An unlisted semantic delta is a stop condition. +4. **Oracle non-authority:** An oracle may expose failure but may not redefine policy, architecture, or interaction semantics. A stricter operationalization than the accepted wording requires an owner gate. +5. **Scope experimental verdicts:** Every experiment adjudication states which decisions its evidence may update and which remain owner-held. Failure of one implementation mechanism does not automatically select another architecture. +6. **Preserve selected rationale before disposal:** A workbench containing the only explanation of a surviving decision must be relocated to immutable evidence before deletion. +7. **Keep status current, not historical:** `MISSION.md` Status contains only the present state and pointers to campaign history; evaluation chronology remains in evidence. +8. **Close by external acceptance:** When closure or witness acceptance is owner-reserved, the agent may prepare a close packet but may not record acceptance until the owner performs that gate. + +The main improvement is therefore not “better documentation coverage.” It is a stricter separation between: + +```text +decision +→ authoritative contract +→ mechanical translation +→ behavioral evidence +→ owner adjudication +``` + +This branch allowed those stages to collapse into one another. Once that happened, agents optimized the nearest visible artifact—the mission wording, then the checker, then the prompt—rather than preserving the original design throughline. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-prompt-skill-tool-architecture.md b/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-prompt-skill-tool-architecture.md new file mode 100644 index 00000000000..767534172b9 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/design/mission-4-prompt-skill-tool-architecture.md @@ -0,0 +1,237 @@ +# Mission 4 prompt/skill/tool architecture (Ampcode prototype README) + +> Design evidence, not execution authority. This is the conceptual specification the owner selected as the basis of Brunch's prompt/skill/tool architecture during the 2026-09-01/02 Mission 4 design conversation. It was authored as a paper prototype under `packages/core/_drafts/ampcode/`, deleted without relocation in `acc4d935c7`, and recovered from commit `e087f570d7` on 2026-09-02. The body is verbatim except that its six now-deleted local primary-source links are rendered as commit-pinned code paths rather than broken links. Relative paths and the "no production files changed" framing below describe the workbench as it stood then; the production homes are now `packages/core/src/{prompts,skills}` and `packages/plugin-*/src/{prompts,skills,tools}`. The Gherkin instrument it references survives at `evaluations/protocols/gherkin-shape-c-paper-v1/instrument/` and, adapted, in `packages/plugin-gherkin/src/`. +> +> **Adopted:** the design claim (disclosure layers versus semantic roles), the channel-boundary table, the responsibility maps, the cross-plugin transformation invariant, the five registers as additive addresses, the Gherkin and Dafny pressure-test verdicts, and the non-goals. **Superseded on 2026-09-02:** the runtime-disclosure trees and the `defineSkill` packaging note below place `universal-elicitation.md` inside each plugin's job skill. The owner's accepted topology instead mounts core's independent `elicitation` capability skill, which job skills activate. On 2026-09-03 the owner further accepted inlining that skill's sole always-required universal resource into its `SKILL.md`, removing mandatory resource indirection without changing the capability boundary; see [`MISSION.md`](../../../MISSION.md) architecture kernel items 1 and 3 and the [repair record](../decisions/mission-4-inline-universal-elicitation-2026-09-03.md). + +--- + +# Ampcode prompt-architecture prototype + +This directory is a non-authoritative paper prototype. It proposes a content and disclosure architecture for Brunch without changing production files. + +## Design claim + +Two independent structures are needed: + +1. **Disclosure layers** determine when guidance becomes available: always-on prompt, activated skill instructions, then phase-scoped resources. +2. **Semantic roles** determine what guidance does: procedure, model content, recognition, interviewing operations, workpiece recording, diagnostics, target authoring or construction, and completion checks. A plugin may combine adjacent roles when separating them would add no useful disclosure boundary. + +The five shared registers—**Directives**, **Recognition**, **Operations**, **Coverage**, and **Verification**—organize universal and plugin elicitation guidance. They are an additive authoring contract, not a lifecycle, workpiece schema, or reason to place every kind of guidance in one document. + +## Authored topology + +```text +ampcode/ +├── README.md +├── core/ +│ ├── SYSTEM.md +│ └── universal-elicitation.md +├── plugin-gherkin/ +│ ├── APPEND_SYSTEM.md +│ └── gherkin-specification/ +│ ├── SKILL.md +│ ├── gherkin-elicitation.md +│ ├── workpiece-template.md +│ └── gherkin-authoring-and-checks.md +└── plugin-sdcpn/ + ├── APPEND_SYSTEM.md + └── sdcpn-modelling/ + ├── SKILL.md + ├── sdcpn-elicitation.md + ├── workpiece-template.md + ├── pn-construction.md + └── checks.md +``` + +Core owns universal elicitation. Each plugin owns one domain-typology and target-formalism specialization. The recording contract and target-authoring responsibilities remain distinct from elicitation content even when a thin plugin combines their resources or phases. + +## Runtime disclosure + +```text +Always present +├── core/SYSTEM.md +└── plugin-sdcpn/APPEND_SYSTEM.md + +On `sdcpn-modelling` activation +└── SKILL.md + ├── interactive branch + │ ├── universal-elicitation.md + │ ├── sdcpn-elicitation.md + │ └── workpiece-template.md when recording or revising + └── construction branch + ├── pn-construction.md + └── checks.md +``` + +For the software-behavior/Gherkin pairing, the same disclosure architecture produces a thinner runtime shape: + +```text +Always present +├── core/SYSTEM.md +└── plugin-gherkin/APPEND_SYSTEM.md + +On `gherkin-specification` activation +└── SKILL.md + ├── elicitation and workpiece maintenance + │ ├── universal-elicitation.md + │ ├── gherkin-elicitation.md + │ └── workpiece-template.md when recording or revising + └── authoring, review, and delivery + └── gherkin-authoring-and-checks.md +``` + +The resource names in `SKILL.md` are logical addresses in one assembled Flue skill. In production, the plugin would use Flue's native `defineSkill()` to package the core-authored universal reference and plugin-authored resources under those safe skill-local names. They are not repository-relative Markdown links, and no resource relies on another resource being traversed automatically. + +## Channel boundary + +| Channel | Content test | +| --- | --- | +| Core `SYSTEM.md` | A universal invariant whose absence could already cause a wrong first turn or dishonest result | +| Plugin `APPEND_SYSTEM.md` | A specialization, activation rule, or plugin-specific guard needed before skill activation | +| `SKILL.md` | Mandatory procedure, branch selection, phase transition, or resource-reading decision | +| Elicitation references | Detailed teaching used while interviewing or revising the domain account | +| Workpiece template | How supported, inferred, unsettled, corrected, and omitted material is recorded | +| Target authoring or construction reference | How recorded domain meaning may be represented in the selected formalism | +| Checks | Whether the workpiece-to-target boundary, resulting artifact, and delivery are honest | + +If a rule must bind before a lazy resource might be read, it does not belong only in that resource. If material merely teaches how to carry out one phase, it does not earn always-on prompt space. + +Resources read together must be additive: when the universal and plugin elicitation references state the same rule, core owns it and the plugin retains only the domain-typology or target-formalism consequence. Resources read at different moments may project a universal invariant into a local recording or checking obligation. + +These channels are responsibility tests, not a required file count. SDCPN earns separate construction and checking resources because transformation is distant, lossy, tool-mediated, and sometimes construct-only. Gherkin combines authoring and checks because its projection is textual and near the elicited behavior, while retaining a separate workpiece for authorship, uncertainty, and unresolved material that a `.feature` document cannot honestly carry by itself. + +## Cross-plugin transformation invariant + +Three references now support one universal boundary: + +```text +person's intent and evidence +→ recoverable workpiece account +→ target-formalism transformation +→ evidence from named checks +→ claim about a surrounding system +``` + +These arrows are transformations and trust boundaries, not implications. Each stage preserves the source, attributes agent-authored choices, and reports strengthening, weakening, normalization, omission, approximation, assumptions, and losses that could change meaning. Evidence at one stage establishes only its named claim over the exact artifact and assumptions examined there. + +- An SDCPN parser or simulation does not establish that the net preserves the operational account or covers every relevant execution. +- A Gherkin parser does not establish step binding, execution, or behavioral adequacy. +- A Dafny verifier does not establish that agent-authored predicates capture the person's intent, that translated code is the verified source, or that surrounding integration is correct. + +This invariant belongs in `core/SYSTEM.md` because a missing lazy read must not permit an overclaim. Each plugin owns the target-specific claim ladder, checks, and trust boundary that instantiate it. + +## Shared elicitation registers + +- **Directives** bind conduct within elicitation and revision. +- **Recognition** names signals and situations that may deserve attention; it does not establish facts. +- **Operations** are selectable moves for resolving the active gap; they are not a script. +- **Coverage** names information the resulting account may need for its purpose; it is not question order. +- **Verification** checks the current question, account, or interviewing trajectory and names local repairs. + +The core reference defines universal entries. Each plugin reference contributes only domain-typology or target-formalism additions. Plugin silence leaves the universal guidance in force; plugin additions may narrow applicability but may not silently weaken universal directives. + +## Responsibility map + +| Responsibility | Owner | +| --- | --- | +| Universal identity and non-negotiable epistemic conduct | `core/SYSTEM.md` | +| Detailed general elicitation repertoire | `core/universal-elicitation.md` | +| SDCPN specialization and pre-activation guardrails | `plugin-sdcpn/APPEND_SYSTEM.md` | +| Capability-aware lifecycle and progressive routing | `sdcpn-modelling/SKILL.md` | +| Operational-process recognition, content, moves, and diagnostics | `sdcpn-elicitation.md` | +| Recoverable process-model artifact | `workpiece-template.md` | +| Petri-net mappings and construction patterns | `pn-construction.md` | +| Construction readiness, net fidelity, and delivery | `checks.md` | + +The Gherkin sibling instantiates the same responsibilities with lower cardinality: + +| Responsibility | Owner | +| --- | --- | +| Universal identity and non-negotiable epistemic conduct | `core/SYSTEM.md` | +| Detailed general elicitation repertoire | `core/universal-elicitation.md` | +| Software-behavior/Gherkin specialization and pre-activation guardrails | `plugin-gherkin/APPEND_SYSTEM.md` | +| Lifecycle, progressive routing, and render-only behavior | `gherkin-specification/SKILL.md` | +| Software-behavior recognition, investigation, coverage, and diagnostics | `gherkin-elicitation.md` | +| Near-target behavior account, authorship, and open matters | `workpiece-template.md` | +| Gherkin authoring, parse semantics, binding honesty, and delivery checks | `gherkin-authoring-and-checks.md` | + +## Main relocations from the first Ampcode candidate + +- Detailed procedure, movements, probes, licenses, and universal diagnostics leave `SYSTEM.md` for `universal-elicitation.md`. +- Universal cues and interviewing moves leave the plugin reference for core. +- The plugin reference becomes additive rather than a second self-contained elicitation manual. +- The five registers replace overlapping labels such as lenses versus cues and techniques versus probes, while workpiece and construction contracts remain separate. +- Construct-only behavior is treated as a runtime branch, not an unconditional plugin instruction. +- Closed stopping-outcome codes are replaced by a plain account of what was produced, checked, blocked, assumed, or left open. + +## Gherkin pressure-test verdict + +The generalization holds at the disclosure, ownership, and semantic-role boundaries. It does not hold as an exact SDCPN-shaped directory or phase graph. + +- The five registers accept the software-behavior additions without a sixth register: rules and examples are Recognition and Coverage concerns; concretizing, contrasting, and varying one condition are Operations; behavior and target checks are Verification. Lifecycle remains in `SKILL.md`, outside the registers. +- Core owns the generic move; the plugin owns its consequence. For example, core owns concrete-case slicing and observable clarification, while Gherkin adds the context/event/outcome shape and the requirement that an expected result be externally observable. +- The workpiece remains distinct from the target artifact. A `.feature` document cannot by itself preserve proposed-versus-current behavior, agent authorship, unsupported step bindings, conflict, deferral, or consequential open questions without becoming a disguised sidecar in comments. +- Gherkin authoring is not an SDCPN-style construction phase. Drafting a scenario is a low-distance projection and a correction surface. It may happen after a coherent rule/example account rather than only in a terminal phase. +- The elicitation workpiece should not require the person to decompose behavior into `Given`/`When`/`Then` steps. It records one example as context, event or action, and observable outcome in their vocabulary; target authoring factors that account into step lines. Literal Gherkin supplied by a knowledgeable person may be preserved without making target syntax the interview's required vocabulary. +- Parse validity, step-definition binding, and behavioral adequacy are different claims. The plugin may claim only the checks it actually ran; absent a supplied step lexicon or codebase capability, phrases are marked new or unchecked rather than guessed to be bound. + +The evidence that would re-open this result is operational: repeated authorship laundering would justify a workpiece less shaped like Gherkin; repeated no-delta transcription would justify collapsing more of the workpiece into the target plus a minimal open-matters companion; a real step-definition corpus and machine binding check could earn a separately disclosed checking phase. + +## Prospective Dafny third reference + +No Dafny plugin files are authored in this prototype. The third reference currently establishes a pairing and prospective responsibility shape for Oracle review rather than an implementation specification: + +- **Domain typology:** software correctness obligations. +- **Target formalism:** Dafny specification modules and program contracts. + +“Formal-verification use cases” remains the umbrella program concern and possible plugin-selection activity, not a target-neutral plugin. “Verified state evolution” remains one recurring correctness shape, not the typology boundary. Actual kernel interfaces are supplied project context: even the examined `dafny-replay` repository varies between `Apply` plus `Normalize`, partial `TryStep`, and collaboration obligations such as `Rebase`, `Candidates`, `Explains`, and `CandidatesComplete`. + +A prospective disclosure shape is: + +```text +Always present +├── core/SYSTEM.md +└── plugin-dafny/APPEND_SYSTEM.md + +On `dafny-verification` activation +└── SKILL.md + ├── elicitation and workpiece maintenance + │ ├── universal-elicitation.md + │ ├── software-correctness-elicitation.md + │ └── workpiece-template.md when recording or revising + └── formalization and evidence + ├── dafny-specification.md + └── proof-checks.md +``` + +The separate Dafny formalization and evidence roles are provisional but earned enough to review: formalization can materially change quantification, domains, preconditions, abstraction functions, and trusted assumptions, while verifier success can create false confidence about those choices. Unlike Gherkin's low-distance textual projection, this boundary needs an explicit semantic diff and an exact account of what was stated, assumed or axiomatized, discharged, skipped, or trusted. + +The software-correctness profile would treat initialization and representation invariants; operation preconditions, postconditions, and frame conditions; transition and rejection guarantees; history and round-trip laws; refinement, simulation, normalization, and intent-preservation relations; termination and progress; and trusted boundaries as a **recognition repertoire, not a closed property-kind schema or interview order**. The person supplies the consequential failure and intended guarantee; the plugin proposes a formal property family only for correction. + +The minimum prospective target is a Dafny specification module plus an obligation manifest, not a full implementation or completed proof. The workpiece preserves the person-recognizable claim, examples and counterexamples, formalization choices and authorship, assumptions and exclusions, semantic deltas, proof status, and unverified perimeter. The manifest maps that account to exact Dafny declarations and distinguishes stated, assumed, discharged, skipped, and trusted obligations. Abstract or bodyless declarations may make contracts available to verification without proving their implementations and must never be reported as discharged merely because dependent code verifies. + +This design would reverse toward a narrower state-evolution plugin only after multiple independent Dafny projects expose one stable cross-project contract. Repeated real conversations in which Dafny-shaped recognition anchors people away from a clearly better target would instead earn a target-selection stage above plugin activation. Until a tracer observes either strain, no target-neutral plugin, fixed kernel ontology, or full Dafny prompt package is warranted. + +## Material retained from Ciaran's outline + +The SDCPN plugin Coverage register retains goals and avoidance conditions; triggers and prerequisites; participants, locations, and resources; activities and input-use semantics; process flow, failures, retries, and recovery; and quantities and validation. It also makes retry scope, data bindings, spatial transfer, goal trade-offs, tolerated probabilities, and one-logical-step-to-several-net-elements explicit. + +## Non-goals + +This prototype does not define a machine-readable register schema, typed capture store, completion algebra, renderer, document loader, second agent, or automatic link traversal. It does not author the prospective Dafny plugin, select a verifier integration, prove that the wording outperforms the frozen baseline, or change the current Flue composition. + +## Primary sources + +- `e087f570d7:libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md` +- `e087f570d7:libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md` +- `e087f570d7:libs/@hashintel/brunch-agent/packages/core/_drafts/system-prompts/ciaran-eliciting-and-constructing.md` +- `e087f570d7:libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/` +- `e087f570d7:libs/@hashintel/brunch-agent/packages/plugin-gherkin/plugin.yaml` +- [Cucumber Gherkin reference](https://cucumber.io/docs/gherkin/reference) +- [From Intent to Proof: Dafny Verification for Web Apps](http://midspiral.com/blog/from-intent-to-proof-dafny-verification-for-web-apps/) +- [`dafny-replay` Replay kernel](https://github.com/metareflection/dafny-replay/blob/main/kernels/Replay.dfy) +- [`dafny-replay` MultiCollaboration kernel](https://github.com/metareflection/dafny-replay/blob/main/kernels/MultiCollaboration.dfy) +- [`dafny-replay` guarantee boundary](https://github.com/metareflection/dafny-replay/blob/main/GUARANTEES.md) +- [Dafny reference manual](https://dafny.org/latest/DafnyRef/DafnyRef) +- `e087f570d7:libs/@hashintel/brunch-agent/packages/core/_drafts/five-register-synthesis/` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/README.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/README.md index 88806627957..fb2e43ce1bb 100644 --- a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/README.md +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/README.md @@ -1,14 +1,15 @@ # Evaluation evidence -Immutable artifacts from observed evaluation campaigns. Campaign directories are named for the -case and instrument, rather than repeating a generic process-model hierarchy. +Artifacts and adjudications from observed evaluation campaigns. Campaign directories are named for the case and instrument rather than repeating a generic process-model hierarchy. +- `five-register-paper-comparison-v1/` — paper comparison and source map for the Five-Register design. +- `flue-skill-composition-side-quest-v1/`, `v2/`, and `v3/` — retained topology comparisons and manifests; their repetitive raw runs were coherently retired under [`docs/archive/evaluations/flue-skill-composition-side-quest.md`](../../archive/evaluations/flue-skill-composition-side-quest.md). +- `live-observable-persona-spike/` — direct-Flue persona and observer proof. +- `mission-4-proof-of-life-v1/` and `mission-4-proof-of-life-v2/` — explicitly retained Mission 4 proof campaigns; do not prune or reinterpret them. - `vestera-legacy-baseline/` — historical conditions 1, 2, 4, and 5. - `vestera-runbook-headless/` — Mission 3 headless-runbook drives. -- `vestera-ir-quality-calibration-v1/` — the four calibration reviews and adjudication. +- `vestera-ir-quality-calibration-v1/` — four calibration reviews and adjudication. - `vestera-prospective-baseline-v1/` — three paid invocations: one runtime-invalid member and two complete, independently graded members. +- `vestera-prospective-candidate-v2/` and `vestera-architecture-candidate-v3/` — candidate and abort/adjudication evidence retained for the Mission 4 redesign history. -Do not place prompts, runners, cases, or answer keys here; those live in -[`evaluations/`](../../../evaluations/). Never overwrite an observed artifact. An older campaign -is evidence, not an active instrument. A documented path relocation may rebase Markdown links, -but does not change captured claims or raw payload fields. +Do not place prompts, runners, cases, or answer keys here; those live in [`evaluations/`](../../../evaluations/). Never overwrite an observed artifact. An older campaign is evidence, not an active instrument. A documented path relocation may rebase Markdown links but does not change captured claims or raw payload fields. Raw outputs from a set-aside exploratory campaign may be removed only under the owner-gated retirement contract in [`docs/archive/evaluations/README.md`](../../archive/evaluations/README.md); accepted or explicitly retained campaigns remain live. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/EVALUATION.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/EVALUATION.md new file mode 100644 index 00000000000..c007972edf5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/EVALUATION.md @@ -0,0 +1,241 @@ +# Five-Register Synthesis Evaluation Protocol + +**Status: paper comparison complete; the owner selected Candidate A.** Candidate B was eliminated in [`Stage 1`](evaluation/stage-1-mechanical-and-authority-audit.md); [`Stage 2`](evaluation/stage-2-owner-led-paper-walkthrough.md) found Candidate C feasible but not earned and applied the protocol's smaller-reversible fallback. The losing instrument sources were removed after disposition and remain recoverable at commit `2fb4c779a2`. Stage 3 is skipped. This protocol belongs to the non-authoritative workbench and does not modify the frozen prospective v1 baseline. + +## Decision this protocol must produce + +Select one coherent relationship among plugin Coverage, workpiece organization, and SDCPN construction readiness. Then decide whether the deduplicated elicitation references exhibit enough observed attention or retrieval strain to earn finer progressive disclosure. + +This protocol can establish which candidate best satisfies the current Mission 4 contract under the cases and runs named here. It cannot establish a universally optimal interview structure or statistical superiority across domains, models, or target formalisms. + +## Candidates + +### A — domain-primary Coverage and workpiece + +Use `plugin-sdcpn/sdcpn-modelling/profile.md` and `plugin-sdcpn/sdcpn-modelling/workpiece-template.md` as currently authored. Target readiness remains distributed between the profile's Verification register and `checks.md`. + +**Selected by owner after Stage 2.** + +### B — formalism-primary Coverage and workpiece + +This historical candidate replaced the shared profile's Coverage register with a formalism-primary index and paired it with a formalism-primary workpiece. Its exact source is retained at commit `2fb4c779a2`. + +**Eliminated in Stage 1 and removed after owner disposition.** Its mandatory Coverage resource exposed concrete SDCPN construction mechanics during ordinary elicitation, violating a preregistered hard gate. + +### C — domain-primary elicitation plus a separate construction-readiness view + +This historical candidate retained domain-primary Coverage and the domain-primary workpiece, removed construction-readiness checks from ordinary elicitation, and added a construction-only SDCPN readiness resource. The resulting view cited authoritative workpiece claims under the existing `Construction notes` without reorganizing or duplicating them. Its exact source is retained at commit `2fb4c779a2`. + +**Eliminated in Stage 2 and removed after owner disposition.** The extra readiness pass added no authored frozen-case distinction, evidence, or oracle; the owner accepted Candidate A as the smaller reversible instrument. + +## Shared invariants + +Every candidate uses the same: + +- core and plugin system prompts; +- universal progressive reference; +- lifecycle instructions and runtime branches; +- additive, register-pure plugin guidance outside the candidate-specific Coverage and construction-readiness treatment; +- workpiece locality rule; +- construction guidance and three evidence levels; +- tool set, model configuration, case inputs, turn limits, graders, and stop rules within a comparison stage. + +If a candidate requires changing another listed invariant, stop: the comparison is confounded and needs a new protocol version. + +## Freeze and manifest + +Before any model-facing comparison: + +1. Give each candidate an immutable id and copy or render its complete model-facing instrument into a candidate-specific manifest. +2. Record the source commit and SHA-256 hash of every system prompt, skill instruction, progressive resource, template, case input, grader, and protocol file. +3. Record the packaged skill resource names and the built server artifact hash. +4. Refuse a paid run when a scoped instrument file is dirty. +5. Never overwrite an observed artifact. Mark invalid runs invalid and retain their evidence. + +Changing a frozen candidate or setting creates a new candidate id or protocol version. + +## Stage 1 — mechanical and authority audit + +Run this before judging prose quality. + +For each candidate, record: + +- total words in always-on instructions, activated skill instructions, mandatory elicitation references, conditionally read references, and workpiece template; +- packaged resource names and whether each instruction pointer resolves to an advertised `read_skill_resource` path; +- required register presence and order; +- exact duplicate sentences across universal and plugin references; +- operational propositions repeated in more than one authoritative workpiece location; +- centralized summaries that restate local claims; +- construction guidance visible before the construction branch; +- claims of reachability, conservation, exclusivity, validity, or simulation unsupported by the stated evidence level. + +A candidate fails this stage if a resource pointer is broken, a workpiece proposition has competing authoritative homes, construction mechanics enter ordinary elicitation, or a check claims a stronger oracle than the available method. + +## Stage 2 — owner-led paper walkthrough + +The executed walkthrough is recorded in [`evaluation/stage-2-owner-led-paper-walkthrough.md`](evaluation/stage-2-owner-led-paper-walkthrough.md). + +Walk every Stage 1-eligible candidate through the same cases without rewriting candidate guidance during the comparison. + +### Cases + +1. **Reusable resource reservation and release:** two activities need the same limited crew; the crew is unavailable while held and returns either unchanged or with a consequential changed state. +2. **Failure, retry, and recovery:** an activity can fail, repeat only part of the process, and either retain or release occupied resources before retry. +3. **Contextual location:** a physical location may act as a boundary, eligibility condition, capacity, travel-time source, state distinction, resource, or irrelevant detail depending on the operation. +4. **External event versus internal threshold:** work may begin because something arrives from outside or because an evolving internal quantity crosses a threshold. +5. **Directional mode change:** A-to-B and B-to-A have different time, scrap, material, capacity, or sequencing losses. +6. **Hidden waiting:** a case waits because of policy, batching, calendar, transport, resource availability, approval, or recovery rather than because “queue” is an independently elicited object. +7. **Correction versus contextual coexistence:** a later account either replaces an earlier statement or reveals a condition under which both remain true. +8. **Unknown versus unasked:** the person explicitly does not know one value while a different consequential topic has never been raised. + +### Walkthrough trace + +For each case, produce one row containing: + +| Observation | Required record | +| --- | --- | +| Signal | What the profile's Recognition register notices without treating it as fact | +| Next move | Which Operation applies and why it is the smallest useful move | +| Coverage | Where the resulting operational knowledge belongs | +| Workpiece authority | The single location of the claim, its evidence, and epistemic treatment | +| Construction boundary | What remains hidden until construction | +| Readiness | How an SDCPN-relevant gap becomes visible and when | +| Verification | The observable failure and repair if the information is mishandled | +| Evidence level | What any resulting net claim may honestly say | + +Do not score a candidate from the elegance of its headings. Record the concrete navigation steps and any point at which the reviewer must translate the same fact twice, choose between competing homes, or introduce formalism vocabulary into the expert-facing move. + +## Stage 3 — model-facing candidate probes + +Run only candidates that pass Stages 1 and 2 and remain indistinguishable on the decision. Do not pay to run an alternative already eliminated by a structural discriminator. Obtain explicit owner authorization for the paid budget before starting. + +### Frozen diagnostic settings + +| Setting | Value | +| --- | --- | +| Case | `evaluations/cases/vestera-scheduling` | +| Opening message | `evaluations/cases/vestera-scheduling/opening-message.md` | +| Simulated-expert pack | `evaluations/cases/vestera-scheduling/situation-pack.md` | +| Prospective ledger | `evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml` | +| Quality ruler | `evaluations/protocols/ir-quality-ruler-v1` | +| Interviewer model | `claude-sonnet-4-5` | +| Simulated expert model | `claude-sonnet-4-5` | +| Interview turns | 8 before the final workpiece request | +| Per-logical-turn latency stop | 180,000 ms | +| Sampling | provider default; no seed | +| Diagnostic replications | 2 independent runs per remaining candidate | +| Omniscient grader | one fresh `claude-sonnet-4-5` context per run | +| Cold reviewer | a separate fresh `claude-sonnet-4-5` context per run | +| Output | a new immutable `docs/evidence/evaluations/five-register-candidate-comparison-v1/` location | + +The current production runner does not select a draft candidate manifest. Stage 3 is not executable until a versioned comparison runner can package one declared candidate without editing active sources between runs, records that candidate's exact manifest and built artifact hash, and passes the existing hermetic artifact/recovery checks. Do not compare candidates by manually swapping active files under one run id. + +Use a fresh conversation for every candidate/run and keep all shared invariants fixed. + +### Elicitation probes + +For each candidate, use the same opening request and simulated-expert input. Record: + +- first-turn question count and whether questions share one frame; +- occurrences of schema- or PN-shaped language in user-facing questions; +- which resources were read, in what order, and why; +- words or tokens loaded from each resource; +- whether each useful stretch updates a single authoritative workpiece claim; +- duplicate propositions across workpiece sections; +- target-relevant gaps identified before construction versus first discovered during construction; +- distinctions among unknown, unasked, declined, deferred, conflict, correction, and contextual coexistence; +- turn latency, model calls, tokens, and recorded cost. + +### Construction probes + +Use the same frozen workpiece input for each eligible candidate. Record separately: + +- tool-schema acceptance and rejected calls; +- agent-reviewed structural discrepancies against the workpiece; +- target gaps that block or materially alter construction; +- unsupported assumptions introduced during construction; +- whether resource acquisition/release, hidden waiting, directional losses, and contextual quantities are visibly represented; +- any actual simulation or stronger analysis, including its scenario and scope; +- the exact evidence level used in delivery claims. + +A parser-accepted or tool-schema-accepted definition is not behavioral success. An observed simulation trace is not a universal invariant. + +## Stage 4 — selected-candidate campaign against the frozen control + +Select one candidate through every applicable comparison stage before running the Mission 4 campaign. Stage 3 is needed only when paper evidence leaves a discriminating behavioral question; this comparison selected Candidate A at Stage 2 and skipped Stage 3. Do not pay to campaign every lightly reasoned variant. + +Create a new versioned protocol/output location based on `evaluations/protocols/prospective-runbook-v1/`. Preserve its case wall, grader separation, immutable manifests, artifact retention, and three-invocation campaign shape unless a separately accepted protocol decision changes one. Never write into `docs/evidence/evaluations/vestera-prospective-baseline-v1/`. + +For each valid selected-candidate run: + +1. Recover the full latest workpiece. +2. Run an independent omniscient grade with the frozen ruler and prospective ledger. +3. Run a separate cold review with only the opening request and workpiece. +4. Human-review every hard failure, grader disagreement, and new mistake class. +5. Record resource reads, loaded context, duplicate claims, target-gap timing, latency, tokens, cost, and evidence-level discipline alongside the frozen ruler outputs. + +Campaign adjudication reports ranges and individual vectors rather than collapsing baseline or candidate variation to one mean. + +## Observable comparison criteria + +### Hard gates + +A candidate is ineligible if it: + +- invents operational content or silently hardens evidence; +- collapses conflict, correction, or contextual coexistence; +- presents unasked material as person-declared unknown; +- interviews through workpiece or target-schema headings; +- creates competing authoritative workpiece claims; +- loads construction mechanics to frame ordinary elicitation; +- loses a reusable resource's reservation/release semantics or a directional/contextual distinction already present in the input; +- claims behavioral validity above the evidence level actually reached; +- fails to emit a recoverable latest workpiece. + +### Comparative observations + +Among gate-passing candidates, compare: + +- objective-aligned acquisition and smallest-next-question quality; +- cold reconstruction of the process spine and target-relevant distinctions; +- number and consequence of target gaps discovered only at construction; +- workpiece claim duplication and reader effort; +- schema-shaped questioning and formalism leakage; +- resource reads and loaded context cost; +- construction correspondence and explicit losses; +- honest separation of tool acceptance, structural review, and behavioral evidence. + +No one scalar decides the result. Context cost is a tie-breaker after fidelity, acquisition, workpiece authority, and evidence discipline. + +## Disposition rule + +1. Eliminate hard-gate failures and retain their artifacts. +2. Prefer the candidate that preserves operational language and single-home workpiece authority while exposing consequential target gaps no later than they are needed. +3. Treat differences inside the frozen baseline's observed variation as uncertain unless a structural discriminator explains them. +4. When candidates trade gains, record the owner-selected trade-off explicitly; do not manufacture a mean score to hide it. +5. If no candidate dominates on the mission imperative, retain the smallest reversible candidate and name the next discriminating probe. +6. After selection, remove losing alternatives from the candidate instrument. Preserve the comparison result as evidence rather than keeping several live prompt shapes. + +## Progressive-disclosure decision + +Do not split the universal reference or plugin profile because of word count alone. A split is earned when the deduplicated candidate shows one or more of these under the probes: + +- irrelevant sections are repeatedly loaded for branches that never use them; +- the model misses or contradicts guidance that is present but buried; +- conditional topics such as quantity distributions, rare events, or construction checks form clear branches with reliable pointers; +- a smaller directly named resource measurably reduces loaded context without delaying a load-bearing question or check. + +If no observed strain meets that bar, retain the shallower resource topology and close the disclosure concern without adding files. + +## Stop or reorient + +Stop the comparison if: + +- candidate instruments differ outside the declared Coverage/workpiece/readiness surface; +- a resource fails to package or its exact run instrument cannot be reconstructed; +- a grader uses information outside its allowed wall; +- the workpiece locality or evidence-level contract is ambiguous enough that reviewers cannot apply it consistently; +- a runtime failure prevents normal artifact retention; +- a proposed fix requires typed capture, completion, projection, or workflow machinery outside Mission 4. + +Repair the protocol or candidate at the owning boundary, assign a new version where required, and restart only the invalidated stage. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/README.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/README.md new file mode 100644 index 00000000000..551c0dce486 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/README.md @@ -0,0 +1,11 @@ +# Five-Register paper comparison v1 + +This directory preserves the evaluation and source map that selected the Five-Register workpiece shape before its useful parts were promoted into production. The moved documents retain historical path language; those paths describe the temporary workbench, not current files. + +Historical revisions: + +- stage 1: `bc032a4264a0529fe1a0ddc36348ea5a6bb33715` +- full losing-candidate snapshot: `2fb4c779a2dc16b786e9c357dec812e021873493` +- final selected draft base: `5249a73f09977ad2ef007e08de7b7314f94568e1` + +Current production authority lives under `packages/core/src/` and `packages/plugin-sdcpn/src/`. This archive is evidence of the decision, not an alternative implementation surface. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/SOURCE-MAP.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/SOURCE-MAP.md new file mode 100644 index 00000000000..2658122ecfc --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/SOURCE-MAP.md @@ -0,0 +1,205 @@ +# Synthesis Source Map + +This map accounts for the primary working and draft material used to produce the candidate. It is an editorial provenance aid, not evidence that any prompt wording improves behavior. + +## Destination map + +| Destination | Responsibility | Main sources | +| --- | --- | --- | +| `core/SYSTEM.md` | Compact always-on universal role and invariants | Active core `src/SYSTEM.md`; `_drafts/system-prompts/assemblies/*`; prompt audit and section workbench | +| `core/universal-elicitation.md` | Progressive universal strategies and methods under five registers | Active SDCPN `elicitation.md` entries marked universal; core `repertoire.yaml`; current APPEND cues/probes/movements/licenses/guards that do not depend on SDCPN; predecessor `elicit/SKILL.md` | +| `core/flue.ts.example` | Raw-string export of the core-owned progressive reference through the existing core Flue subpath | Verified `?raw` library build and cross-package `defineSkill` composition spike | +| `plugin-sdcpn/APPEND_SYSTEM.md` | Concise operational-process/SDCPN specialization and router | Active plugin `flue.ts` inline instruction; active APPEND role; current skill lifecycle and construction boundary | +| `sdcpn-modelling/instructions.md` | Conceptual lifecycle, runtime branches, and packaged-resource routing | Active `sdcpn-modelling/SKILL.md`; accepted Mission 3 information hierarchy; predecessor elicitation procedure | +| `sdcpn-modelling/skill.ts.example` | Explicit composition of separately authored resources into one Flue skill | Installed Flue `defineSkill` contract; verified core-package → plugin-package → production Flue-app spike | +| `sdcpn-modelling/profile.md` | Strictly additive operational-process domain typology and SDCPN profile under five role-pure registers | Active APPEND target material; active `elicitation.md` SDCPN/mixed material; Ciaran outline; current plugin YAML concepts | +| `workpiece-template.md` | Selected domain-primary recoverable process-model workpiece | Active `ir-template.md`; Mission 4 proof obligations; Ciaran investigation headings | +| `pn-construction.md` | SDCPN mappings, construction patterns, inference, bounded change, and losses | Active `pn-construction.md`; APPEND `Maps To` material; Ciaran transformation patterns | +| `checks.md` | Phase-boundary, net, fidelity, revision, and delivery checks | Active `checks.md`; APPEND smells/rabbit holes/failure signatures; current construction tool contract | +| `EVALUATION.md` | Preregistered authority audit, owner-led walkthrough, model-facing comparison, campaign boundary, and disposition rule | Competing-design review; Mission 4 proof and fog-line; `prospective-runbook-v1`; `ir-quality-ruler-v1`; frozen baseline adjudication | +| `evaluation/stage-1-mechanical-and-authority-audit.md` | Executed candidate manifests, context accounting, structural checks, hard-gate adjudication, and Stage 1 dispositions | `EVALUATION.md`; Candidates A–C at source commit `bc032a4264`; resource/import/register/duplication probes | +| `evaluation/stage-2-owner-led-paper-walkthrough.md` | Eight frozen-case traces, cross-candidate comparison, structural discriminator, and accepted owner disposition | `EVALUATION.md`; Stage 1-eligible Candidates A and C at source commit `2fb4c779a2`; universal/profile/workpiece/construction/check resources | + +## Active core system prompt + +Source: `packages/core/src/SYSTEM.md`. + +| Source material | Treatment | +| --- | --- | +| Universal elicitation identity | Kept and tightened in candidate core `SYSTEM.md` | +| Modelling plans/systems and analyzing/modifying existing models | Kept by explicit owner decision in the universal identity and lifecycle revision path | +| Rich IR and target artifacts | Reframed as recoverable workpiece first and capability-dependent target production | +| Objectives and appetite | Invariant retained in the system prompt; procedure expanded only in universal progressive Directives/Operations | +| Slice/sweep, probing, absences, batching, assumption ledger | Moved out of always-on context into universal progressive Operations/Directives | +| Expert vocabulary | Retained as an always-on invariant and elaborated progressively | +| Closing procedure | Compact stopping invariant always-on; detailed close and checks progressive | +| Empty `Methods`, `Judgment`, and `How to check` headings | Removed rather than preserved as taxonomy scaffolding | +| `Patterns`, `Techniques`, `Lenses`, `Motifs`, `Smells`, `Rabbit holes`, `Failure modes` metacontract | Replaced by the progressive five-register address space; no plugin heading contract remains in `SYSTEM.md` | +| Claim that the harness surfaces patterns or computes completion | Excluded as false for the active path | +| Placeholder YAML frontmatter | Excluded from model-facing candidates | + +## Active SDCPN append + +Source: `packages/plugin-sdcpn/src/APPEND_SYSTEM.md`. + +| Source section | Destination and treatment | +| --- | --- | +| Plugin job | Condensed into the SDCPN append specialization and profile Directives | +| Typology table | Selected domain-primary Coverage; the eliminated formalism-primary comparison remains recoverable at commit `2fb4c779a2`; downstream mapping detail moved to construction | +| Things that look like kinds and are not | Plugin Coverage and construction principles | +| Quantity, source-regime, evidence, and precision attributes | Universal authorship/uncertainty plus plugin context/precision Directives and Verification | +| Per-kind aspects | Distributed into plugin Coverage; no longer presented as question order | +| Cues | Split between universal Recognition and plugin Recognition according to target dependence | +| Motifs | Recast as plugin Recognition situation patterns; their embedded questions moved to Operations and target structures to construction | +| Probes | Mostly universal Operations; conservation and operational-resource probes remain plugin Operations | +| Slice/sweep | Universal Operations own the methods; plugin Operations own case-unit and process-specific sweep refinements | +| Licenses | Universal Directives/Operations where formalism-independent; target constraints retained in plugin Directives | +| Smells and rabbit holes | Converted into near-action Verification checks and repairs; duplicates removed | +| Interviewer failure modes | Consolidated by invariant under universal/plugin Verification; evaluator-style wording omitted where it adds no agent action | +| Final `Must know` tip | Excluded because the referenced section and typed completion mechanism do not exist in the active path | + +## Active SDCPN skill resources + +Sources: `packages/plugin-sdcpn/src/skills/sdcpn-modelling/`. + +### `SKILL.md` → `instructions.md` plus `skill.ts.example` + +The active skill's conceptual lifecycle, `runbook-ir` full-document emission convention, partial delivery, and phase resource routing move to plain `instructions.md`. The candidate requires a final full workpiece emission before construction handoff and workpiece-only delivery. It also distinguishes the current interactive and construct-only runtime branches: construct-only execution reports a re-entry question instead of interviewing. Existing-model revision applies fully to the workpiece and only to net changes supported by mounted mutation capabilities. + +The plugin-owned `skill.ts.example` demonstrates Flue's native `defineSkill` to map the instructions, core-owned universal reference, and plugin-owned profile/template/construction/check resources into one packaged skill. Flue synthesizes the packaged `SKILL.md`; no authored duplicate remains. The eliminated Candidate C composition remains recoverable at commit `2fb4c779a2`; its additional readiness resource was not retained in the selected instrument. + +### `elicitation.md` + +The current file's merged universal and SDCPN material is separated by ownership: + +- posture, questioning, evidence, uncertainty, prioritization, stopping, generic probes, and generic failure guards move to `core/universal-elicitation.md`; +- operational-process signals, patterns, investigation needs, and target caveats move to `plugin-sdcpn/.../profile.md`; +- target construction consequences move to `pn-construction.md`. + +The active file's Ciaran-derived “What to investigate” headings seed the domain-primary Coverage candidate and workpiece template. + +### `ir-template.md` + +The selected candidate retains a human-readable domain-primary Markdown workpiece, full `runbook-ir` emission, expert vocabulary, assumptions, unknowns, conflict, omissions, and losses. Each operational claim owns its exact evidence, normalized account, agent inference, assumption, correction, conflict, or contextual variation locally when those distinctions matter. A compact central ledger holds only unresolved matters spanning concerns or requiring re-entry and references rather than restates authoritative claims. These annotations are structural aids, not mandatory per-statement types. The eliminated formalism-primary workpiece and Candidate C readiness projection remain recoverable at commit `2fb4c779a2`; their comparison and disposition are preserved in the evaluation artifacts. + +### `pn-construction.md` + +Mapping principles, tool-authoritative construction, reusable patterns, visible inference/approximation, loss reporting, and worked semantic cases are retained and expanded with Ciaran's consumed/reserved/read distinction and predecessor timed/probabilistic patterns. The predecessor probability comparison is corrected rather than copied. + +### `checks.md` + +Elicitation sufficiency and workpiece-only delivery checks live in the universal and plugin Verification registers. Phase-specific `checks.md` is disclosed only for construction and net delivery; it distinguishes tool-schema acceptance from agent-reviewed structural correspondence and from behavior observed through an actual execution or stronger analysis. Candidate paths, resource-return structures, and exclusive guards found by static review are no longer presented as reachability, conservation, or behavioral exclusivity proofs. Closed stopping-outcome codes are replaced by plain evidence-level and observed-state reporting. + +## Ciaran outline + +Source: `packages/core/_drafts/system-prompts/ciaran-eliciting-and-constructing.md`. + +| Draft material | Destination | +| --- | --- | +| Goals, avoidance, measures, factors, importance, thresholds | Plugin Coverage: purpose/goals/measures/constraints; workpiece operational coverage | +| Process failure, retry, unhappy paths | Plugin Recognition: failure/retry/recovery; Operations: disruption sweep; Coverage: flow/failure/recovery; construction event/recovery pattern | +| Triggers and prerequisites | Plugin Recognition and domain-primary Coverage; formalism-primary cross-mapping to boundary/flow/policy/dynamics/activity | +| Actors | Participants/entity distinctions, performer and decision roles | +| Locations | Recognition and Coverage as a cross-cutting operational perspective; explicit guard against equating physical location with PN place | +| Resources and caps | Contended-resource Recognition; resource Coverage; construction resource pattern | +| Step inputs and consumed/reserved/read use | Plugin Operations, Coverage, workpiece, construction mappings, and checks | +| Step duration and variability | Timed-work Recognition, Coverage, construction pattern, and checks | +| Step success/failure and outcomes | Failure/branch Recognition, Coverage, construction pattern, and checks | +| Timed-work building block | Construction-only timed-work pattern | +| Probabilistic branch building block | Construction-only conditional/probabilistic pattern; unchecked inequality convention explicitly rejected | + +## Core prompt workbench + +Sources: `packages/core/_drafts/system-prompts/` (moved from the earlier singular `system-prompt/` workbench during this consultation). + +- The candidate system prompt follows the balanced/trust-forward material while preserving the owner-selected scope for existing-model analysis and revision. +- Purpose-relative attention, first-turn load, vocabulary, authorship, divergence, and honest partial delivery are retained. +- Detailed orientation, question-shape, and closing procedures move to the universal progressive reference. +- Plugin names, target syntax, suspended machinery, evaluation controls, universal `2–4` batching, mandatory quantitative scripts, and absolute completion claims remain excluded. + +## Old job runbooks + +Source: `packages/core/_drafts/system-prompts/old-job-runbooks.md`. + +Retained where behavior remains truthful: + +- objectives and purpose before irrelevant structure; +- slice then sweep; +- workpiece and assumption/loss delivery; +- locate disputed material before revision; +- report what changed and what the model can support afterward. + +Excluded or weakened because the active path does not provide the claimed mechanism: + +- harness-computed completion and affected slices; +- capture supersession as an enforced runtime operation; +- deterministic whole-model projection claims; +- mechanically guaranteed unchanged scope; +- closed stopping-outcome enums. + +## Predecessor Brunch material + +### Universal elicitation skill + +Source: `/Users/lunelson/Code/hashintel/brunch/src/agents/skills/elicit/SKILL.md`. + +Retained design shape: + +- inspect current understanding and identify the smallest meaningful absence; +- choose a relevant concern and ask one focused question; +- use contrastive cases for ambiguity; +- select content by the plane of uncertainty; +- treat routing signals as heuristics rather than automatic truth; +- re-evaluate after each answer. + +Substrate-specific graph, scratchpad, structured-exchange, readiness-band, ingest, and map machinery is not imported into this candidate. + +### Kind-indexed question reference + +Source: `/Users/lunelson/Code/hashintel/brunch/src/agents/skills/elicit/references/question-kinds-per-intent-kind.md`. + +Retained design shape: + +- Coverage can provide stable attention addresses while questions remain open and adapted to current context; +- adding a question does not require adding a target kind; +- operations project from general coverage to a situated question rather than reading a catalogue verbatim. + +The predecessor intent-graph kinds and closed ontology are not imported. Its kind-primary organization motivated the formalism-primary Coverage alternative evaluated and eliminated in Stage 1. + +### Behavioral kernels + +Source: `/Users/lunelson/Code/hashintel/brunch/docs/design/BEHAVIORAL_KERNELS.md`. + +Retained design shape: + +- Recognition sits between the person's domain and the next question; +- a reusable situation pattern is not a domain, phase, template, or user-facing formalism; +- one concrete domain composes several patterns; +- signals activate a small relevant set rather than a comprehensive questionnaire; +- contrastive questions expose consequential distinctions; +- patterns connect recognition, operations, artifact consequences, and checks. + +The predecessor software-specification kernel ontology, intent graph, typed artifacts, validators, and proof-candidate machinery are not imported. + +## Deliberate exclusions + +The synthesized set does not claim or introduce: + +- harness-owned captures, folds, demanded slots, completion computation, or affected-slice computation; +- stable assertion identifiers, a capture store, provenance joins, or automatic supersession; +- a closed domain ontology, mandatory per-statement epistemic kinds, or machine-readable register entries; +- a rendered repertoire/plugin compiler, filesystem loader, second model-facing skill, or second agent; `defineSkill` is the existing Flue packaging primitive, not a new runtime; +- hidden answer keys, grader mistake IDs, fixed turn budgets, or evaluation thresholds; +- concrete Vestera facts or any other scenario-specific teaching; +- a universal question count, batch size, quantitative script, or absolute completeness claim; +- automatic live data bindings or a claim that parser acceptance proves simulation validity. + +## Potential losses to inspect during evaluation + +- Whether the compact system prompt under-activates slice/sweep before progressive references are read. +- Whether one plugin profile containing both operational-process and SDCPN knowledge becomes too large despite the register structure. +- Whether freeform register contents make cross-register routing ambiguous enough to earn a card grammar. +- Whether the verified `defineSkill` composition retains its observed resource names, lazy reads, and one-skill behavior when promoted through the repository's actual package exports and built-agent tests. +- Whether the selected domain-primary Coverage hides target obligations in model-facing execution despite passing the paper comparison. +- Whether the workpiece's explicit epistemic homes feel like an unwanted semantic type system. +- Whether existing-model analysis/revision remains too weakly specified to support the universal identity claim. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-1-mechanical-and-authority-audit.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-1-mechanical-and-authority-audit.md new file mode 100644 index 00000000000..d998c4406eb --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-1-mechanical-and-authority-audit.md @@ -0,0 +1,98 @@ +# Stage 1 — Mechanical and Authority Audit + +**Status: complete. Candidates A and C remain eligible; Candidate B is eliminated by a preregistered hard gate. No candidate has won.** + +- Audit time: `2026-09-01T13:57:10Z` +- Candidate source commit: `bc032a4264a0529fe1a0ddc36348ea5a6bb33715` + +This is a structural audit of the authored instruments under [`../EVALUATION.md`](../EVALUATION.md). It does not claim how a model will behave, how well an elicited workpiece will reconstruct a real operation, or whether a constructed net will execute faithfully. + +## Candidate assemblies audited + +All candidates use the shared core system prompt, plugin append, skill instructions, universal elicitation reference, construction guidance, and checks. + +- **A — domain-primary:** shared `profile.md`, shared `workpiece-template.md`, and the shared `skill.ts.example` resource map. +- **B — formalism-primary:** the shared profile with its complete `## Coverage` section replaced by `coverage-alternatives/formalism-primary.md`, paired with `coverage-alternatives/formalism-primary-workpiece-template.md`. The rendered profile preserves the shared Directives, Recognition, Operations, and Verification sections. +- **C — domain-primary with readiness view:** `candidates/domain-primary-with-readiness/profile.md`, the shared domain-primary workpiece, the candidate-specific `sdcpn-readiness.md`, and its candidate-specific `skill.ts.example` resource map. + +Candidate B has no committed complete rendered profile or `defineSkill` composition. The audit could deterministically render its five-register profile for structure and word accounting, but B would need a complete hashable instrument before model-facing execution. This packaging incompleteness is not the eliminating finding below; its ordinary-elicitation construction content is. + +## Context and resource accounting + +Counts use `wc -w` over authored Markdown. “Ordinary activated total” includes skill instructions, both mandatory elicitation references, and the paired workpiece template; it excludes the always-on system fragments. Construction resources are conditional and excluded from ordinary elicitation. + +| Material | A | B | C | +| --- | ---: | ---: | ---: | +| Always-on core plus plugin prompts | 468 | 468 | 468 | +| Activated skill instructions | 835 | 835 | 835 | +| Universal elicitation reference | 2,327 | 2,327 | 2,327 | +| Candidate plugin profile | 2,067 | 2,720 rendered | 2,031 | +| Paired workpiece template | 843 | 1,013 | 843 | +| Mandatory elicitation references | 4,394 | 5,047 | 4,358 | +| Ordinary activated total | 6,072 | 6,895 | 6,036 | +| Construction guidance plus checks | 2,741 | 2,741 | 2,741 | +| Candidate-only readiness resource | — | — | 937 | +| All construction-only references | 2,741 | 2,741 | 3,678 | + +Candidate C removes 36 words from ordinary activated context relative to A and adds a 937-word resource only on the construction branch. Candidate B adds 823 ordinary activated words relative to A. These counts describe placement and cost; they do not decide fidelity or attention quality. + +## Mechanical results + +| Check | A | B | C | +| --- | --- | --- | --- | +| Five registers present once and in required order | Pass | Pass in deterministic render | Pass | +| Local imports in a complete `defineSkill` example resolve | Pass | Not yet authored | Pass | +| Instruction pointers match advertised resource names | Pass | Render/package required | Pass | +| Exact duplicate prose sentences between universal and plugin references | 0 | 0 | 0 | +| Construction resources excluded from ordinary elicitation | Pass | **Fail** | Pass | +| Unsupported evidence-level claims | None found | None found | None found | + +Candidate A advertises `references/checks.md`, `references/pn-construction.md`, `references/profile.md`, `references/universal-elicitation.md`, and `templates/workpiece.md`. Candidate C advertises the same names plus `references/sdcpn-readiness.md`. The shared instructions point to those exact names and make the readiness read conditional on its being advertised. + +## Authority results + +### Workpiece claims + +The templates contain no scenario claims to duplicate. Structurally, all three state that each operational proposition has one authoritative location, keep evidence and epistemic treatment beside it, make the process spine reference local activity/resource entries, restrict the cross-cutting ledger to references, and make delivery status refer back rather than create another account. + +No competing authoritative home or centralized claim restatement is required by the templates. Whether a model follows that contract remains a Stage 2 or Stage 3 observation. + +### Construction readiness, mapping, and checking + +- **A:** ordinary elicitation sees three concise construction-readiness checks in plugin Verification, but not places, transitions, arcs, guards, or mapping recipes. Mapping remains in `pn-construction.md`, and evidence assessment remains in `checks.md`. +- **B:** mandatory Coverage names concrete SDCPN consequences before the construction branch, including colour sets, typed elements, resource tokens, source transitions, guards, factored transitions, intermediate places, resource arcs, arc types, priorities, state invariants, scalar simulation functions, differential equations, and crossing-triggered transitions. +- **C:** the ordinary profile says SDCPN readiness and mapping are outside it. The conditional readiness resource records only cited construction obligations, blocking gaps, and anticipated losses; it explicitly defers concrete mappings, inferences, approximations, and defaults to `pn-construction.md`, and result inspection to `checks.md`. + +### Evidence levels + +The shared checks distinguish tool-schema acceptance, agent-reviewed structural correspondence, and behavioral execution or stronger analysis. Static review uses bounded wording such as “candidate structural path,” “intended return structures,” and “apparently exclusive guards,” with explicit statements that these do not establish reachability, conservation, or runtime exclusivity. No candidate-specific material claims a stronger oracle. + +## Candidate B hard-gate adjudication + +**Claim:** Candidate B places construction mechanics in a resource that the shared instructions require before substantive elicitation. + +**Relied on by:** The Stage 1 eligibility decision under the preregistered rule: “A candidate fails this stage if … construction mechanics enter ordinary elicitation.” + +**Competing explanation:** The “Possible SDCPN consequences” are descriptive context rather than construction guidance and therefore do not cross the phase boundary. + +**Primary evidence:** `instructions.md` requires the plugin profile before substantive elicitation. Candidate B's replacement Coverage repeatedly names concrete target structures and transformations: factored start/progress/finish transitions, intermediate places, resource arcs, source transitions, arcs and arc types, guards, priorities, differential equations, and transitions triggered by threshold crossings. + +**Discriminator:** Does a mandatory ordinary-elicitation resource expose concrete target construction structures or only operational concerns and phase-boundary checks? + +**Observation:** Candidate B exposes concrete target construction structures in eight “Possible SDCPN consequences” passages. Candidates A and C keep those mappings behind the construction-resource pointer. + +**Disposition:** **Hard-gate failure.** Calling the passages “possible consequences” does not remove the target structures from model context or preserve the preregistered phase boundary. This structural result does not prove that B would ask schema-shaped questions; it establishes the narrower failure that construction mechanics are present during ordinary elicitation. + +## Stage 1 disposition + +- **Candidate A:** eligible for Stage 2. +- **Candidate B:** eliminated; retain its source and this audit as comparison evidence, but do not spend Stage 2 or paid-run effort on it. +- **Candidate C:** eligible for Stage 2. + +Stage 1 does not choose between A and C. Stage 2 must compare their owner-led case traces, especially whether A's always-visible readiness checks expose target gaps at useful times or whether C's construction-only reference projection preserves operational questioning while surfacing those gaps no later than needed. + +Progressive-disclosure strain was not established by this audit. Word count alone does not earn another resource split. + +## Post-Stage 2 disposition + +The Stage 1 instruction to retain Candidate B's source applied until final candidate selection. After the owner accepted Candidate A in Stage 2, the B and C instrument sources were removed under the protocol's disposition rule. Their exact evaluated forms remain recoverable at commit `2fb4c779a2`. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-2-owner-led-paper-walkthrough.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-2-owner-led-paper-walkthrough.md new file mode 100644 index 00000000000..f83d673b36c --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/five-register-paper-comparison-v1/evaluation/stage-2-owner-led-paper-walkthrough.md @@ -0,0 +1,171 @@ +# Stage 2 — Owner-Led Paper Walkthrough + +**Status: complete; the owner accepted Candidate A as the surviving paper candidate. No model-facing claim is made.** + +- Walkthrough time: `2026-09-01` +- Owner disposition: `2026-09-01` +- Candidate source commit: `2fb4c779a2` +- Eligible candidates: A and C +- Eliminated before this stage: B + +This walkthrough applies the eight frozen cases in [`../EVALUATION.md`](../EVALUATION.md) to the two Stage 1-eligible instruments. Each case description is treated as the only given evidence. The trace does not invent quantities, policies, probabilities, or operational facts to make a candidate look complete. + +The frozen cases state no simulation objective and supply no populated workpiece. The traces therefore compare authored routes conditionally: a distinction is consequential, applicable, or blocking only if a later stated use depends on it. Stage 2 cannot execute Candidate C's projection, observe resource reads, or establish whether a model follows either candidate; those remain model-facing questions. + +Candidates A and C share the universal reference, domain-primary Coverage, workpiece authority structure, construction mappings, and checks. They differ only in readiness placement: A keeps three broad construction-readiness checks in ordinary plugin Verification; C removes those checks and adds a 937-word reference projection after construction is requested. + +## Case 1 — Reusable resource reservation and release + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize **Contended resource** and **Consumed, reserved, or read input** as hypotheses. The crew may be reusable and unavailable while held; its return state must be confirmed. | Same Recognition path; C's ordinary profile retains both operational shapes unchanged. | +| Next move | Apply **Classify activity inputs** to ask when the crew is acquired, unavailable, and released, and whether it returns changed. Use **Test contention with a borderline case** only if demand can exceed availability. | Same operational-language moves. The readiness view is not loaded and does not frame the question. | +| Coverage | Use **Participants, locations, flowing things, and resources** for crew identity, count, and availability; use **Activities, inputs, outputs, and resource use** for each acquisition/use/release relation; let the process spine reference their ordering. Qualification becomes relevant only if later evidence and purpose make it consequential. | Same domain-primary Coverage and filing route. | +| Workpiece authority | Keep crew properties at the participant/resource location and the activity-specific reservation/release claim at the relevant activity. The spine references both rather than restating them. | Same authoritative homes. Later readiness entries cite those locations and carry no crew count, rule, or release fact. | +| Construction boundary | Resource-state representation, acquisition/return structure, and arc semantics remain hidden in `pn-construction.md`. | Same boundary. Before mapping, C adds a cited obligation that the representation preserve unavailability and changed-state return. | +| Readiness | Plugin Verification already checks classification, acquisition, unavailability, release, and count; its broad readiness section also requires reusable-resource occupancy to be recorded before handoff. Shared pre-construction checks repeat the boundary at construction time. | Ordinary Verification retains the same specific resource checks but omits A's three broad readiness bullets. At construction, the readiness view cites resource, activity, and spine locations, then shared checks apply. | +| Verification | **Resource disappearance** repairs back into the activity/input relation. Static net review may report an intended return structure, not conservation. | Same repair and evidence bound. The readiness projection adds navigation but no new resource distinction or oracle. | +| Evidence level | Before construction, only a workpiece claim exists. After construction, report tool acceptance, static correspondence, or scoped behavioral evidence separately. | Same levels. The readiness view is not an evidence level. | + +**Comparison:** Both author an elicitation route for reservation semantics when those semantics bear on the use. C mandates an additional construction-time projection before the same mapping and checks; paper inspection finds no additional resource distinction in that projection, but cannot establish what either candidate would discover in a run. + +## Case 2 — Failure, retry, and recovery + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize **Failure, retry, and recovery** and possibly **Event rather than step**. Failure may redirect the case and may release, retain, or lose occupied resources. | Same Recognition path. | +| Next move | Apply **Trace disruption and recovery** from failure through work in hand, occupied resources, partial retry, recovery, and terminal outcome. Use a concrete failure story when the ordinary path does not establish the exception. | Same move and user-facing vocabulary. | +| Coverage | File local failure outcomes and resource effects with the affected activities; file the authoritative retry/recovery order in the process spine; keep occurrence quantities only where supported. | Same Coverage and filing. | +| Workpiece authority | The activity owns what failure does locally. The spine owns where retry rejoins or terminates and references the activity/resource claims. One memorable incident remains evidence of mechanism, not rate. | Same authoritative structure. Readiness later cites the failure path rather than paraphrasing it. | +| Construction boundary | Interruptions, alternate paths, recovery transitions, and resource-return structures remain construction choices. | Same boundary; the readiness projection records the recovery and resource obligations before those choices. | +| Readiness | Ordinary Verification names dead spine and resource disappearance as failure signals; broad readiness requires order, enabling conditions, and occupied resources before handoff. | The same specific checks remain in ordinary Verification. C later indexes ordering, recovery, and resource references under construction notes. | +| Verification | Repair a missing retry destination at the process spine and missing release semantics at the activity/input relation. Do not infer failure frequency from the case. | Same repairs. Shared construction checks inspect recovery and enumerated holding paths afterward. | +| Evidence level | A static recovery path is structural correspondence only; runtime progress or non-leakage needs named execution or stronger analysis. | Same evidence boundary. | + +**Comparison:** The domain profile already connects failure, retry, and occupied resources. C makes that connection explicit again at construction without changing gap timing, authority, or evidence. + +## Case 3 — Contextual location + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize physical location as a possible boundary, eligibility condition, capacity, travel source, state distinction, resource effect, or irrelevant detail—not automatically a Petri-net place. | Same explicit Recognition signal and backstage-formalism directive. | +| Next move | Follow one concrete case through the location and use a contrastive case to ask what changes when the location changes. Stop if no purpose-relevant operational consequence appears. | Same move; no target node is proposed during questioning. | +| Coverage | Put the authoritative location meaning under **Participants, locations, flowing things, and resources**. Activity, time, boundary, or policy sections reference that meaning only when they own a distinct consequence. | Same domain-primary Coverage. | +| Workpiece authority | Record one location claim with its selecting context and evidence. If its effect changes activity duration or eligibility, place that separate proposition at its own concern and reference the location claim rather than repeating it. | Same authority rule. A readiness entry may cite several locations but cannot become a second location account. | +| Construction boundary | Whether the effect becomes state, capacity, guard, parameter, travel delay, or no net structure remains in construction guidance. | Same boundary. C records only which operational distinction a mapping must preserve and defers the concrete representation. | +| Readiness | Ordinary Recognition and Verification already prevent pattern-generated process facts and require target-relevant gaps to remain visible. The construction mapping principle asks for an evidenced operational effect. | C omits the broad target-gap bullet during elicitation but retains the pattern-generated-fact repair. Its readiness lens cites the location effect before the same construction principle applies. | +| Verification | If the interview turns “location” directly into a place, repair by returning to observable operational consequences. If no consequence bears on the objective, omit the distinction. | Same repair. The readiness view cannot promote location into structure. | +| Evidence level | A visible place or parameter later establishes only a reviewed representation, not that the location behavior is correct. | Same evidence boundary. | + +**Comparison:** Neither candidate requires formalism vocabulary in the question. C's reference projection does not resolve a location ambiguity that A's Recognition, Coverage, construction principle, and checks leave unresolved. + +## Case 4 — External event versus internal threshold + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize both **Gate, release, trigger, or prerequisite** and **Threshold on a changing quantity**. Do not assume that all trigger language has the same origin. | Same two hypotheses. | +| Next move | Slice the case to ask what observable event enables work and where it comes from. For the internal quantity, clarify direction, rate, threshold, consequence, and reset only to purpose-relevant depth. | Same operational moves. | +| Coverage | Put external arrivals/events and admission conditions under boundary/triggers; put changing quantity and threshold evidence under time/quantities/stochastic behavior; let the process spine reference both enabling conditions. | Same domain-primary filing. | +| Workpiece authority | The external event claim and internal quantity claim have separate homes because they are separate propositions. The spine references how each enables the relevant activity without copying their details. | Same authority. Readiness later cites both homes. | +| Construction boundary | Source transitions, scenario inputs, guards, dynamics, and crossing-triggered events remain candidate mappings, not elicitation concepts. | Same boundary. C's readiness resource records only boundary and dynamic obligations and explicitly defers where they are represented. | +| Readiness | Recognition and Coverage name event origin, threshold consequence, and reset, so they are available to a purpose-relative elicitation move. Verification checks spine completeness and contextual quantity precision but does not independently demand each of those details. | C retains the same ordinary routes. At construction, separate boundary and continuous-change lenses can cite each concern when applicable; the resource does not require them to be paired. | +| Verification | Repair an unsupported threshold or invented arrival pattern at its authoritative claim. A missing enabling relation repairs at the spine. | Same repairs; shared checks later require parameters/initial populations or explicit external inputs. | +| Evidence level | Static presence of a source or threshold structure is not evidence that arrival or crossing behavior occurs as intended. | Same evidence boundary. | + +**Comparison:** C authors a dedicated construction-time index across the two domain homes, while A leaves construction to consult the same workpiece through mapping guidance and checks. Both require a cold-readable workpiece; paper inspection cannot establish which path avoids transcript archaeology in execution. + +## Case 5 — Directional mode change + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize **Mode change** and the possibility that A-to-B and B-to-A differ in time, scrap, material, capacity, or downstream sequence. | Same Recognition path. | +| Next move | Apply **Compare both directions of a mode change** and pursue only consequences the operation distinguishes. Use contextual quantity work when values vary by regime. | Same move and stopping rule. | +| Coverage | Keep the mode-change activity and directional consequences under activities/resource use; keep separately supported quantity behavior at the time/quantity concern; reference both from the process spine where sequencing changes. | Same domain-primary Coverage. | +| Workpiece authority | Place each directional proposition once beside its evidence and context. A shared quantity section may own a distribution, but it references rather than restates the directional activity rule. | Same authority structure. Readiness cites both directional homes. | +| Construction boundary | Mode states, directional transitions, and loss structures remain in construction guidance. | Same boundary. C records the obligation to preserve direction but does not choose the factoring. | +| Readiness | Ordinary Verification explicitly requires direction-dependent losses to remain distinct; broad readiness adds no unique mode-change content. | Candidate C retains the same direction-specific Verification check. Its later readiness lens repeats the preservation obligation by reference. | +| Verification | A collapsed symmetric account repairs at the two directional claims. Static review later checks distinct structural losses without claiming runtime exclusivity. | Same repair and evidence wording. | +| Evidence level | Distinct structures can be reviewed against the workpiece; their actual loss behavior requires execution or stronger analysis. | Same levels. | + +**Comparison:** Candidate C authors an additional navigation step for this case. The directional distinction already has explicit ordinary Recognition, Operations, Coverage, and Verification routes in both candidates; whether a model applies them remains unobserved. + +## Case 6 — Hidden waiting + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Recognize **Hidden waiting** as a possible unavailable input/resource/calendar, release rule, batch, transport, approval, policy, or recovery condition rather than a self-explanatory queue. | Same Recognition path. | +| Next move | Apply **Follow waiting to its enabling condition**: ask what the case is waiting for and what observable event makes it able to continue. | Same move. | +| Coverage | Put the wait episode and enabling relation in the process spine; put the underlying resource, policy, calendar, batch, transport, approval, or recovery proposition at its domain concern and reference it from the spine. | Same domain-primary filing. | +| Workpiece authority | The spine owns that the case waits at this point and cites the local cause. It does not duplicate the resource availability or policy rule that explains the wait. | Same single-home arrangement. Readiness cites the spine and cause. | +| Construction boundary | An intermediate place may emerge, but its meaning comes from mapped surrounding conditions. Queue structure is not elicited directly. | Same boundary. C records enabling semantics only and defers structure. | +| Readiness | Ordinary Verification explicitly requires waiting to have an evidenced enabling condition; broad readiness also requires what enables the case. | Candidate C retains the same explicit waiting check. The later readiness ordering lens adds no new operational distinction. | +| Verification | An unsupported queue repairs by returning to the surrounding condition and activity claims. Shared static checks later reject unsupported queue objects. | Same repair and check. | +| Evidence level | A visible waiting place is structural correspondence only; waiting duration or release behavior needs scoped behavioral evidence. | Same boundary. | + +**Comparison:** A authors the same waiting-gap check in ordinary Verification and construction checks. C adds a derived reference entry before the same mapping and static check. + +## Case 7 — Correction versus contextual coexistence + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Use universal **Tension within or between accounts**. A later difference may be correction, conflict, unnamed context, or source error. | Same universal Recognition; readiness is irrelevant until the active account is settled enough to map. | +| Next move | Apply **State a contradiction without resolving it**, asking whether one replaces the other, both hold under different conditions, or evidence would distinguish them. | Same move. | +| Coverage | Use universal authorship/divergence coverage and the domain policy/context concern where a practiced regime selects among accounts. | Same Coverage. | +| Workpiece authority | A correction updates the authoritative claim and marks what it replaces without leaving both active. Contextual coexistence keeps each account beside its selecting condition. Use the issue ledger when the unresolved relationship affects several claims or needs a later return path. | Same authority. The readiness view may cite only the active/contextual claims and cannot settle the relationship. | +| Construction boundary | Do not compile an unresolved conflict or treat recency as a universal guard. Concrete policy/guard mapping remains behind the construction branch. | Same boundary. C may mark a blocking selection gap but cannot choose a rule. | +| Readiness | Ordinary universal and plugin Verification already check correction, conflict, and contextual regimes. A's broad target-gap bullet can mark an unresolved selection as construction-relevant. | The same epistemic checks remain in C. Its policy readiness lens later cites the unresolved location and routes a re-entry question; only the affected construction path blocks when the gap admits materially different structures. | +| Verification | Repair silent collapse or doubled active correction at the authoritative claim before mapping. After mapping, check that only supported contextual selection appears. | Same repair. The readiness view supplies no additional evidence. | +| Evidence level | A workpiece can preserve unresolved accounts. No net claim that resolves or compiles the disputed relationship is justified until a supported treatment is constructed and checked; unaffected supported structure may proceed. | Same levels. | + +**Comparison:** Both preserve the epistemic distinction before construction. C provides a later reference slot for the gap, but the existing issue ledger, construction notes, and checks already supply that route. + +## Case 8 — Unknown versus unasked + +| Observation | Candidate A | Candidate C | +| --- | --- | --- | +| Signal | Use universal **Silence and absence**: absence alone does not say whether material is irrelevant, unknown, unasked, declined, forgotten, or deferred. | Same Recognition. | +| Next move | Apply **Select the smallest consequential absence**. If asked and unavailable, use **Deposit and defer**; if never raised and consequential, keep it visibly unasked and ask only when it outranks the active thread. | Same move during ordinary elicitation. | +| Coverage | Keep the unknown value at its authoritative domain concern with its source and consequence; keep a distinct consequential topic **Not yet asked** at its own home. Do not create person-declared uncertainty from interviewer omission. | Same domain-primary Coverage and annotations. | +| Workpiece authority | Each absence state lives beside the claim or missing concern it qualifies. A cross-cutting ledger entry references several affected claims only when re-entry spans them. | Same authority. The readiness view cites the absent authoritative home; it does not copy the unknown or convert unasked to unknown. | +| Construction boundary | Unknowns, assumptions, and their consequences are already visible in the workpiece. During construction, an unknown may receive a named representational treatment such as a parameter or blocker; it cannot become an assumption without explicit agent authorship. An unasked topic remains an acquisition gap, not a default value. | Same boundary. C's readiness view can record the construction consequence before mapping. | +| Readiness | A's ordinary broad readiness check requires target-relevant gaps to be visible before handoff, while universal Coverage requires consequential unsupported dependencies to remain visible. Shared construction checks instruct the agent to formulate the smallest resolving question when materially different structures remain possible. | C removes only the broad readiness line; universal Coverage retains the same unsupported-dependency contract. Its construction-only view provides a route for recording the material consequence and re-entry question under construction notes before shared checks. | +| Verification | Repair a mislabeled unknown at its authoritative home. If the unasked topic can change structure, route the smallest operational question rather than inventing a value or rule. | Same repair and evidence discipline. | +| Evidence level | Parameterization of an unknown is a visible construction treatment, not evidence of its value. An unasked topic supports no operational claim. | Same evidence boundary. | + +**Comparison:** This is the strongest authored rationale for C: it supplies a dedicated construction-notes location for consequence and re-entry. A nevertheless makes the same distinction available earlier through universal Coverage and Verification, its readiness line, and shared pre-construction checks. Paper inspection establishes placement, not whether either model path notices or routes the gap reliably. + +## Cross-case comparison + +| Criterion | Candidate A | Candidate C | Paper result | +| --- | --- | --- | --- | +| Operational-language questioning | All eight cases route through the same domain Recognition and Operations. | Same; readiness remains construction-only. | Tie; both pass. | +| Single-home workpiece authority | Domain claims stay local; spine, ledger, construction notes, and delivery reference them. | Same; readiness entries are reference-only. | Tie; both pass. | +| Consequential gap timing | The combined Recognition, Operations, Coverage, and Verification registers author a route for every frozen case; broad readiness is visible before construction and shared checks run during construction. | The same case-specific routes remain, minus three broad readiness lines; the detailed projection runs after construction is requested. | A has the earlier authored readiness pointer; actual noticing is unobserved. | +| Construction mechanics in elicitation | None; broad readiness is stated in operational language. | None. | Tie; both pass. | +| Construction navigation | Workpiece → construction guidance → checks. | Workpiece → readiness projection → construction guidance → checks. | A is shallower. | +| Duplicate operational claims | None required. | None required; readiness cites claims. | Tie on authority. | +| Repeated classification effort | Mapping and checks consult authoritative claims directly. | The selected slice is projected through applicable readiness lenses, which may be omitted when irrelevant, before mapping and checks. | C mandates an additional pass; its per-claim effort is unobserved. | +| Context cost | 6,072 ordinary activated words; 2,741 construction-only words. | 6,036 ordinary activated words; 3,678 construction-only words. | C saves 36 ordinary words and adds 937 construction words. | +| Construction fidelity oracle | Shared mapping guidance and three evidence levels. | Same mapping guidance and evidence levels; readiness is not an oracle. | Tie on authored oracle; no fidelity behavior was demonstrated. | +| Unique frozen-case distinction | All eight have an operational elicitation and checking route before construction. | No additional operational or target distinction is authored. | None found for C in the paper instruments. | + +## Structural discriminator + +Candidate C supplies a structurally coherent example of a domain-primary workpiece paired with a separate, reference-only SDCPN readiness view without requiring duplicated authoritative claims or elicitation-time mappings. The paper walkthrough finds no new authored operational distinction or oracle relative to Candidate A: + +- every frozen case has a route through the shared domain Recognition, Operations, Coverage, and Verification material, conditional on a stated use making that route relevant; +- Candidate A's three readiness bullets contain no construction mechanics and are available before a construct-only runtime would have to return a gap; +- shared `checks.md` already contains a detailed pre-construction sufficiency pass; +- Candidate C mandates a projection through applicable lenses before the same mapping and checking resources, without adding evidence or another oracle. + +The instruments do not establish whether C's explicit projection improves model attention enough to outweigh that extra pass. Under the protocol's fallback for a non-dominating comparison, the smaller reversible candidate is A. The 36-word ordinary-context reduction does not offset a 937-word construction resource and another required navigation step on paper alone. This is a topology and reversibility decision, not a behavioral superiority claim. + +## Owner disposition + +- **Candidate A:** selected as the surviving paper candidate. +- **Candidate C:** eliminated; this walkthrough preserves the evidence that the separate readiness view is feasible but not yet earned, while its candidate source remains recoverable at commit `2fb4c779a2`. +- **Candidate B:** remains eliminated from Stage 1; its source remains recoverable at commit `2fb4c779a2`. +- **Stage 3:** skipped. No candidate-comparison runner or paid comparison is justified after accepting the smaller-candidate fallback. If later behavioral evidence reopens the decision, the named discriminating probe is a frozen construct-only workpiece containing one consequential omission among otherwise supported process facts: compare whether the alternatives surface that omission before unsupported mapping, preserve a reference-only re-entry question without transcript archaeology, and avoid unsupported assumptions under the same model, tools, and stop rule. +- **Progressive disclosure:** no further split is earned. A paper walkthrough cannot establish model attention or retrieval strain and supplies no evidence that would meet the protocol's split threshold; word count alone remains insufficient. + +The owner accepted this disposition and authorized removal of the losing alternative instruments. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/comparison.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/comparison.md new file mode 100644 index 00000000000..3fd842060e5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/comparison.md @@ -0,0 +1,78 @@ +# Flue skill-composition side-quest comparison + +## Outcome + +**Invalid or inconclusive.** Candidate A remains mechanically viable, but this probe did not +establish behavioral viability or preference for either topology. The current production model +failed the shared plugin instruction before the candidate-specific disclosure paths could be +compared reliably. + +No production promotion or Mission 4 reorientation is warranted from this evidence. + +## What the hermetic probe established + +All nine faux-provider runs crossed the built production `ChatAgent` composition seam. + +- Candidate A's initial catalog contained `sdcpn-modelling` and `elicitation`; Candidate B's + contained only `sdcpn-modelling`. +- A could activate both skills and read the plugin's SDCPN resource. +- B could activate the plugin and read byte-identical universal content as a supporting resource. +- S1 and S4 acquired universal content through their candidate-specific paths. +- S2 and S3 acquired neither the independent capability nor the packaged universal resource. +- The selected universal instructions had the same SHA-256 + (`a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716`) in both + candidates. +- Normalizing the one routing sentence made the plugin instruction bodies byte-identical. +- S5's attempted activation returned a successful tool result containing: + `Skill "elicitation" is not available. Available skills: sdcpn-modelling.` + The faux turn could continue, so the missing capability is an explicit soft failure rather than + a fatal runtime error. + +This evidence proves mounting, access, observability, and absence of hidden universal content. It +does not prove model judgment. + +## Paid mechanism smoke + +The smoke stopped after the two S1 runs because their two model calls each reached the +four-model-invocation ceiling. Both runs also exhibited the same shared failure. S2 was not run: +the budget was exhausted, and the side quest separately requires paid evaluation to stop when a +shared-content defect makes the comparison non-discriminating. + +| Dimension | Candidate A — independent | Candidate B — packaged | +| --- | --- | --- | +| Job routing | Pass: activated `sdcpn-modelling` | Pass: activated `sdcpn-modelling` | +| Capability routing | Fail: did not activate `elicitation` although it was in the initial catalog | Fail: did not read `universal-elicitation.md` although the activated skill advertised it | +| Universal judgment | Fail: asked four orientation questions as a batch | Fail: asked five orientation questions as a batch | +| Plugin judgment | Pass: questions stayed in approval-process purpose, scope, and operational concerns | Fail: although mostly process-grounded, one question exposed Petri-net familiarity instead of staying in operational vocabulary | +| Composition | Fail: universal content never entered context, so the action could not compose both bodies of judgment | Fail for the same reason | +| Restraint | Indeterminate: S2 was not run | Indeterminate: S2 was not run | +| Disclosure | Pass: raw trace shows `sdcpn-modelling` only | Pass: raw trace shows `sdcpn-modelling` only | +| Evidence honesty | Pass: no approval-process facts or completed construction were invented | Pass: no approval-process facts or completed construction were invented | +| Failure clarity | Pass in hermetic S5: missing `elicitation` was explicit and actionable | Indeterminate: not applicable to B | +| Model calls | 2 | 2 | +| Input / output tokens | 3,506 / 560 | 3,454 / 528 | +| Cache write tokens | 4,732 | 4,694 | +| Total tokens | 8,798 | 8,676 | +| Model latency | 7,028 ms | 5,893 ms | +| Provider cost | USD 0.012221 | USD 0.0119615 | + +Combined paid activity: 2 scenario runs, 4 provider calls, 17,474 total tokens, 12,921 ms summed +model latency, and USD 0.0241825. This reached the model-invocation ceiling and remained below the +USD 1.00 ceiling. + +## Interpretation + +The independent topology was not mechanically falsified: Flue mounted it, advertised it, activated +it under a prescribed faux path, and exposed a clear missing-capability result. The real model's +failure to activate it cannot be attributed specifically to independent mounting because the same +model also ignored Candidate B's packaged resource instruction and the shared +`sdcpn-elicitation.md` read. + +The observed strain is therefore upstream of the topology comparison: after activating the shared +job skill, the current model answered directly instead of performing either required progressive +disclosure route. Post-hoc wording changes are forbidden in this probe, and expanding the campaign +would not repair that confound. + +The bounded conclusion is to retain the current Mission 4 authority and production default. A new +probe would need a separately authorized, frozen shared-content revision or another discriminating +mechanism; this side quest supplies no warrant to choose A or B. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/manifest.json new file mode 100644 index 00000000000..fca69115f0e --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/manifest.json @@ -0,0 +1,78 @@ +{ + "sideQuestId": "flue-skill-composition-side-quest-v1", + "sourceCommit": "5249a73f09977ad2ef007e08de7b7314f94568e1", + "sourceState": "dirty side-quest instrument; every instrument file is content-addressed below", + "model": "anthropic/claude-haiku-4-5", + "runtime": { + "boundary": "built production ChatAgent loaded from apps/brunch-agent/dist/app.mjs", + "flue": "2.0.3", + "hermeticProvider": "@earendil-works/pi-ai faux provider 0.83.0", + "constructionToolsMounted": false, + "stop": "first consequential question, finding, or construction decision" + }, + "candidateRenderedText": { + "independentPluginActivation": "runs/hermetic/independent-S1.json#/toolCalls/0/output", + "independentElicitationActivation": "runs/hermetic/independent-S1.json#/toolCalls/1/output", + "packagedPluginActivation": "runs/hermetic/packaged-S1.json#/toolCalls/0/output", + "packagedUniversalResource": "runs/hermetic/packaged-S1.json#/toolCalls/1/output" + }, + "sourceSha256": { + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "82356eb4e0aab1634c21d96a36e834394fe9987e7445a26b84c6fd1c497763ab", + "apps/brunch-agent/src/evaluations/skill-composition/candidate-contract.ts": "df514bda6fbedcbcf09b8bd8b148716cb8a3297293a8cd4e18ec47e566c46b62", + "apps/brunch-agent/src/evaluations/skill-composition/candidates.ts": "a67aa0c87bdc8e72c0bcbbe4cad603ed264809957b56b9c0ccef4032ae08cf0e", + "apps/brunch-agent/src/evaluations/skill-composition/run.ts": "21bf274423e833c1e4a4af5f747978a4c3c3db2815da973a4894c8e72dba88de", + "libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json": "10e5b6897319045485a33e556d1de05241cbdbe9ddf5e8fa6c72ef73199252c1", + "libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/protocol.md": "476984da4bd65f21717dff1c450a82e86017ef0652b03b778c0691e585aabf0a", + "libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v1.md": "c16d5faaa7a450a41d6888dd6d2b3eac88e4687779b0c8f8e6fe4eceeeb241c6", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/SKILL.md": "b09a02e92602bf67f54ce3988a4e1af438b344fb7dd654936e127905451f0025", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/checks.md": "9487f40c73398e2008c10cd6f85c60735c34e59ab2f5bb4f59dddf863552f2a5", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/instructions.md": "b6d632a0e9ad5b21253fdbae792e1546ce5bd9cb0a0c999befc7e3dfd2f3e7f4", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/sdcpn-elicitation.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/workpiece-template.md": "4ee0dad11d802ff7918ef8660c42e3be8caef0eb923e92ab4084b70f2f110ec9", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "527fc0b3472b5c1953a89b691da0883dc2eb93dad1cdaed21b1cc7e1db0c8187", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "18ff63996ea2c0656f246e3e36a30962de3e03c106a25305614eb66ebf0f5717", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "b4959142f10c74e9b04a5d0c0dfb3b74dbab8c492a26e34d2c2c8ff9e3eb7cfa" + }, + "renderedSha256": { + "elicitationInstructions": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "independentPluginInstructions": "a4b0261917a6627172e83b87f31142fb905758a8a7f7a7f000781c1fba880f34", + "packagedPluginInstructions": "63adaa3aa89e11862add6537e33c87fdb548bd058c7dac54fa67a5a28cc20b3c", + "packagedUniversalResource": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716" + }, + "parity": { + "universalSubstanceByteIdentical": true, + "normalizedPluginInstructionsByteIdentical": true, + "intentionalDifferences": [ + "Candidate A mounts elicitation independently; Candidate B packages universal-elicitation.md.", + "The plugin routing sentence uses activate_skill for A and read_skill_resource for B.", + "Candidate A adds the mechanically required elicitation skill name and activation-cue description." + ] + }, + "hermeticRuns": [ + "runs/hermetic/independent-S1.json", + "runs/hermetic/packaged-S1.json", + "runs/hermetic/independent-S2.json", + "runs/hermetic/packaged-S2.json", + "runs/hermetic/independent-S3.json", + "runs/hermetic/packaged-S3.json", + "runs/hermetic/independent-S4.json", + "runs/hermetic/packaged-S4.json", + "runs/hermetic/independent-missing-S5.json" + ], + "paidRuns": ["runs/paid/independent-S1.json", "runs/paid/packaged-S1.json"], + "paidBudget": { + "plannedScenarioRuns": 4, + "completedScenarioRuns": 2, + "authorizedModelInvocations": 4, + "completedModelInvocations": 4, + "authorizedUsd": 1, + "observedUsd": 0.0241825, + "stopReason": "The two S1 runs reached the four-model-invocation ceiling. Both also ignored the required universal disclosure and produced opening questionnaires, so a shared-content failure made further topology comparison non-discriminating." + }, + "artifactFieldNotes": { + "activatedSkills": "This raw-run field records activate_skill attempts. A successful activation requires an output that begins with the requested skill instructions; S5's missing elicitation attempt is not a disclosure." + }, + "outcome": "invalid-or-inconclusive" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/retired-runs.sha256 b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/retired-runs.sha256 new file mode 100644 index 00000000000..f22d23b8efb --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/retired-runs.sha256 @@ -0,0 +1,11 @@ +1f5a405ec66e2ee676c71e7a1599095ab3c1c0e11bd829e1ddee6caa058e900e libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/independent-S1.json +7635efde73040aa05497a037e392e93300d580bb75567bda7b5b1bd3ae1f35a5 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/independent-S2.json +cdfa4075b335f30e5e460faa1ba1f8d24e30895f5bf65e0f166fd3b78df93941 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/independent-S3.json +1f93338a9ab47925a56ff3312458b1f40b0a04c751975c357fcee938363e6588 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/independent-S4.json +7bdb3123a1d81a01b310e327d713de246a90a6c10341f3a0ad7078d938f3ad96 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/independent-missing-S5.json +0c99295241a9317ef8dfb5f810d9bf91dfb4b75b065ad77c42920c642162bd20 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/packaged-S1.json +3474efcf28412fac7c8eeda3dc97c0e0ffcd7ace5b64d92533773881e380da3e libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/packaged-S2.json +ddc519d9bfd852da3e2cef65d5be98351552283d756afbab20aad79e9b8031df libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/packaged-S3.json +824d37bc31ad1becb1a47891afe69c352a4333fca4e9e35484fc941f720010be libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/hermetic/packaged-S4.json +2f7b6ebbe40766b2da6f6440321da14537795c9333cc2176dcfec696fcc88caa libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/paid/independent-S1.json +7aa88581cf929dce0c388a8abe4b06ae3007854c8849b40d524ee9a669508383 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v1/runs/paid/packaged-S1.json diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/comparison.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/comparison.md new file mode 100644 index 00000000000..f482f0134df --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/comparison.md @@ -0,0 +1,98 @@ +# Flue skill-composition side-quest v2 comparison + +## Outcome + +**Invalid or inconclusive, with one S1 observation favoring Candidate B and Candidate A under +substantial suspicion.** + +Flue can mount and mechanically activate an independent core `elicitation` skill, but the paid +evidence did not satisfy the frozen v2 falsification rule. Under the same v2 gate and S1 fixture, +Candidate B performed the packaged resource route while Candidate A missed the independent route. +That is one candidate-differentiating observation under the topology manipulation, not enough to +establish reliable behavior: B failed S1's composition-quality requirement, both S4 runs bypassed +the job skill, paid S2 was not exercised, and the planned replications were stopped. + +The current one-skill packaging remains the architectural fallback because Mission 4 already +authorizes it, not because v2 proved it behaviorally successful. No v2 prose should be promoted. + +## V2 intervention and parity + +V2 changed only the shared interactive routing gate, with one normalized topology-specific action +line. The hermetic probe established: + +- both candidates crossed the same built production `ChatAgent`; +- A advertised `sdcpn-modelling` and `elicitation`, and B advertised only `sdcpn-modelling`; +- universal substance was byte-identical in A's skill instructions and B's resource; +- replacing A's required-action line with B's made the plugin instructions byte-identical; +- S1/S4 could follow each topology's required universal route; +- S2/S3 could proceed without universal disclosure; and +- no construction tools or undisclosed selected v2 universal resource entered the harness. The + unchanged production system prompt still contains its legacy always-on universal guidance. + +The faux evidence proves mechanics and restraint of the harness, not model judgment. + +## Paid evidence + +### S1 — underspecified interactive request + +| Dimension | Candidate A — independent | Candidate B — packaged | +| --- | --- | --- | +| Job routing | Pass: activated `sdcpn-modelling` | Pass: activated `sdcpn-modelling` | +| Capability routing | Fail: did not activate advertised `elicitation` | Pass: read universal and SDCPN elicitation resources | +| Universal judgment | Fail: asked four orientation questions as a batch | Fail: asked purpose and boundary as a two-question batch | +| Plugin judgment | Pass: stayed in approval-process purpose and scope | Pass: stayed in purpose, boundary, and concrete-case process concerns | +| Composition | Fail: universal content never entered context | Fail: both bodies entered context, but the consequential action violated their one-focused-question contract | +| Restraint | Indeterminate: S2/S3 restraint was not exercised | Indeterminate: S2/S3 restraint was not exercised | +| Disclosure | `sdcpn-modelling` only | `sdcpn-modelling`, `universal-elicitation.md`, `sdcpn-elicitation.md`, and an unnecessary early `workpiece-template.md` read | +| Evidence honesty | Pass | Pass | + +The pair differentiates the candidates under the topology manipulation. A stronger shared gate +did not cause A to invoke the independent capability; B did follow the resource path, with +progressive-disclosure overreach from reading the workpiece template before creating or revising a +workpiece. + +### S4 — review exposing a human-knowledge gap + +| Dimension | Candidate A — independent | Candidate B — packaged | +| --- | --- | --- | +| Job routing | Fail: no skill activation | Fail: no skill activation | +| Capability routing | Fail: no universal disclosure | Fail: no universal disclosure | +| Universal judgment | Fail: universal content was absent and the response did not ask the focused question | Indeterminate: it asked the stated distinction, but the prompt itself supplied both alternatives and universal content was absent | +| Plugin judgment | Indeterminate attribution: identified the unsupported target choice without loading plugin judgment | Indeterminate attribution for the same reason | +| Composition | Fail: neither body entered context | Fail: neither body entered context | +| Restraint | Indeterminate: S2/S3 restraint was not exercised | Indeterminate: S2/S3 restraint was not exercised | +| Disclosure | None | None | +| Evidence honesty | Pass | Pass | + +S4 exposed the same non-discriminating job-routing symptom in both candidates before topology. +The stop rule therefore ended paid execution before S2 or replications; continuing could not +repair attribution. + +## Cost and stopping + +V2 used 4 scenario runs, 7 model invocations, 39,338 total tokens, 28,938 ms summed model latency, +and USD 0.04946605. + +Across v1 and v2, paid activity used 6 scenario runs, 11 model invocations, 56,812 total tokens, +41,859 ms summed model latency, and USD 0.07364855. This remained below the user-amended ceilings +of 48 model invocations and USD 1.00. + +## Decision + +The bounded evidence shows one independent-activation strain rather than a Flue mounting failure: + +1. Hermetic A can activate both skills and receives their full instructions. +2. Real-model A missed `elicitation` in v1 S1, but that run was non-discriminating because v1 B + also missed disclosure. +3. Real-model A missed `elicitation` after v2 made the disclosure gate explicit. +4. Real-model B followed the packaged resource route under the same v2 gate and fixture. + +Only the v2 S1 pair isolates topology, and it was not replicated. B also did not pass the complete +S1 action contract or either S4 composition gate. The protocol's condition for falsifying A was +therefore not met. + +Retain the current one-skill production authority and do not amend `MISSION.md` to promote A. +Record Candidate A as materially risky and Candidate B as favored by one candidate-differentiating +routing observation. The remaining failures—job-skill routing on review, question dosage after +successful resource disclosure, and premature workpiece-resource loading—belong to owner-led +Mission 4 runbook work, not another topology campaign under this side quest. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/manifest.json new file mode 100644 index 00000000000..1c08c0f5aa7 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/manifest.json @@ -0,0 +1,82 @@ +{ + "sideQuestId": "flue-skill-composition-side-quest-v2", + "sourceCommit": "5249a73f09977ad2ef007e08de7b7314f94568e1", + "sourceState": "dirty side-quest instrument; every instrument file is content-addressed below", + "model": "anthropic/claude-haiku-4-5", + "runtime": { + "boundary": "built production ChatAgent loaded from apps/brunch-agent/dist/app.mjs", + "flue": "2.0.3", + "hermeticProvider": "@earendil-works/pi-ai faux provider 0.83.0", + "constructionToolsMounted": false, + "stop": "first consequential question, finding, or construction decision" + }, + "intervention": "Replace only the shared interactive routing sentence with the frozen v2 required-disclosure gate; normalize the one candidate-specific action line for parity.", + "candidateRenderedText": { + "independentPluginActivation": "runs/hermetic/independent-S1.json#/toolCalls/0/output", + "independentElicitationActivation": "runs/hermetic/independent-S1.json#/toolCalls/1/output", + "packagedPluginActivation": "runs/hermetic/packaged-S1.json#/toolCalls/0/output", + "packagedUniversalResource": "runs/hermetic/packaged-S1.json#/toolCalls/1/output" + }, + "sourceSha256": { + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "6a1bbb68da22eed1f1179aa133bb9f41a094ef9162300a14c30661f9bad2e0b7", + "apps/brunch-agent/src/evaluations/skill-composition/candidate-contract.ts": "b6aa515d5805ef0f7f637cd58afc350d4ae67ff9612d78f2f3f434c90519628b", + "apps/brunch-agent/src/evaluations/skill-composition/candidates.ts": "451a12b35374721f32cd9b5aac85a29ff8c8ee66741afb1b2360bc172dbc8fe8", + "apps/brunch-agent/src/evaluations/skill-composition/run.ts": "c04354de75321d89c33ef98e823d670275a4d1f8dbd659bb326a833450792e41", + "libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json": "10e5b6897319045485a33e556d1de05241cbdbe9ddf5e8fa6c72ef73199252c1", + "libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/protocol.md": "1ff13a4c5e30c698dd5d2ac252074597639b620b1d9cbaf4d1380fcaa3f9ca32", + "libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v1.md": "c16d5faaa7a450a41d6888dd6d2b3eac88e4687779b0c8f8e6fe4eceeeb241c6", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/SKILL.md": "b09a02e92602bf67f54ce3988a4e1af438b344fb7dd654936e127905451f0025", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/checks.md": "9487f40c73398e2008c10cd6f85c60735c34e59ab2f5bb4f59dddf863552f2a5", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/instructions.md": "b6d632a0e9ad5b21253fdbae792e1546ce5bd9cb0a0c999befc7e3dfd2f3e7f4", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/sdcpn-elicitation.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/workpiece-template.md": "4ee0dad11d802ff7918ef8660c42e3be8caef0eb923e92ab4084b70f2f110ec9", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "527fc0b3472b5c1953a89b691da0883dc2eb93dad1cdaed21b1cc7e1db0c8187", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "18ff63996ea2c0656f246e3e36a30962de3e03c106a25305614eb66ebf0f5717", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "b4959142f10c74e9b04a5d0c0dfb3b74dbab8c492a26e34d2c2c8ff9e3eb7cfa" + }, + "renderedSha256": { + "elicitationInstructions": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "independentPluginInstructions": "0c10c1e3bc61fb39fc799584d050e1170c7b7befd07f74dd6ebca8921f3398fd", + "packagedPluginInstructions": "4a2410540717af8c740c57d7e74efd290f74cf3a23590d41acb632bd3f31a0f5", + "packagedUniversalResource": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716" + }, + "parity": { + "universalSubstanceByteIdentical": true, + "normalizedPluginInstructionsByteIdentical": true, + "intentionalDifferences": [ + "Candidate A mounts elicitation independently; Candidate B packages universal-elicitation.md.", + "The v2 required-action line uses activate_skill for A and read_skill_resource for B.", + "Candidate A adds the mechanically required elicitation skill name and activation-cue description." + ] + }, + "hermeticRuns": [ + "runs/hermetic/independent-S1.json", + "runs/hermetic/packaged-S1.json", + "runs/hermetic/independent-S2.json", + "runs/hermetic/packaged-S2.json", + "runs/hermetic/independent-S3.json", + "runs/hermetic/packaged-S3.json", + "runs/hermetic/independent-S4.json", + "runs/hermetic/packaged-S4.json" + ], + "paidRuns": [ + "runs/paid/independent-S1-r1.json", + "runs/paid/packaged-S1-r1.json", + "runs/paid/independent-S4-r1.json", + "runs/paid/packaged-S4-r1.json" + ], + "budget": { + "cumulativeAuthorizedModelInvocations": 48, + "v1ModelInvocations": 4, + "v2ModelInvocations": 7, + "cumulativeModelInvocations": 11, + "cumulativeAuthorizedUsd": 1, + "v1Usd": 0.0241825, + "v2Usd": 0.04946605, + "cumulativeUsd": 0.07364855, + "stopReason": "Both S4 candidates bypassed sdcpn-modelling, creating a shared job-routing failure before topology; the side-quest stop rule forbids continuing to S2 or replications." + }, + "outcome": "invalid-or-inconclusive" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/retired-runs.sha256 b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/retired-runs.sha256 new file mode 100644 index 00000000000..63a6064d367 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/retired-runs.sha256 @@ -0,0 +1,12 @@ +cbbe2133982c4b4e7aa4dcbc9aa94cf12b533c112f8005b36e5814a594a7fbc9 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/independent-S1.json +a314d9e83575684ab8653f84ae05d386a6f3008bd4147be637ceb69e3ea99734 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/independent-S2.json +8e4e5b80c900feddd8cb1bd24fd1feba6fa65cdadf3e83bd940090539f48abf0 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/independent-S3.json +b9c09ba41047f579dc1446d7d04404d6c3ef92559157cdd8856b9925ad7e8724 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/independent-S4.json +3435d42501bae4178ef1c0fc1c5cec64d0e3761c4ebbe3bf73c50bf0852daed2 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/packaged-S1.json +9e476eb08d6b99be229c9fc5bac5a5af7d2c3dd0c5aa7b2490b979d033f23b34 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/packaged-S2.json +54fe956babe877a4e0af5f58afb0215edd1f62cb997f47f42f25e069d5a7921f libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/packaged-S3.json +d31d9ab78eee7571cba0af09feeff37fca90cb2d24b1ccdee84bcc55d6a93d59 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/hermetic/packaged-S4.json +e24e557f6b070f4bdd522df4de00388e3028e0c36f8931fcb2ff0408524426e5 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/paid/independent-S1-r1.json +e8b6a7576160d4993d53baa805948cf99a36c549ad168e21937c83604adbb493 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/paid/independent-S4-r1.json +e254b18989de02cf3e66f97a4ba5f8c3aab0a3db27acaa90da320d49cf0e2c77 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/paid/packaged-S1-r1.json +9ec194fe53de8772f28ac97e0ae01cefda23aa791d7909ec543cf5eb9177ff5a libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v2/runs/paid/packaged-S4-r1.json diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md new file mode 100644 index 00000000000..1cda71be8ce --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/comparison.md @@ -0,0 +1,87 @@ +# Flue skill-composition side-quest v3 comparison + +## Outcome + +**Invalid or both topologies behaviorally weak under the full v3 routing thresholds.** + +Candidate B materially outperformed Candidate A on S1 routing: B completed packaged universal and +SDCPN disclosure in two of three runs, while A completed independent disclosure in zero of three. +That repeated paired result is evidence of independent-activation strain. It does not satisfy the +frozen falsification rule, because the first revised-S4 pair bypassed `sdcpn-modelling` and universal +disclosure in both candidates. The shared-router stop rule ended the campaign before S4 +replication, leaving B at zero of one rather than the required two of three. + +Candidate B meets the S1 routing threshold, but neither topology meets the complete cross-scenario +v3 adjudication condition. Retain Candidate B as the existing production fallback, but do not +claim that v3 behaviorally validated it and do not promote Candidate A. + +## Instrument validity + +The hermetic gate established that both candidates crossed the built production `ChatAgent` seam +with the same compact Ampcode core prompt, shared v3 SDCPN router, revised scenarios, model, and +tool availability. + +- The complete compact core prompt and shared v3 append appeared in the first model request. +- The legacy production prompt and its `## The role (core)` marker were absent. +- Universal elicitation bytes were identical between A's skill and B's resource. +- Normalizing the one required-action line made plugin job instructions byte-identical. +- A advertised both `sdcpn-modelling` and `elicitation`; B advertised only `sdcpn-modelling`. +- Faux runs proved both disclosure routes and S2/S3 non-disclosure paths mechanically. +- With no side-quest selector, `ChatAgent` still uses the production core prompt, plugin append, + and skill. + +This removes the v2 legacy-prompt confound and proves mechanics. It does not make paid model +behavior deterministic. + +## Paid routing evidence + +| Scenario | Candidate A — independent | Candidate B — packaged | +| --- | --- | --- | +| S1 required disclosure | **0/3.** Activated only `sdcpn-modelling` in every run; never activated `elicitation` or read SDCPN elicitation guidance. | **2/3.** Runs 1–2 read universal and SDCPN elicitation guidance; run 3 activated only the job skill. | +| S2 restraint | **2/2.** Activated the job skill, performed no universal disclosure, asked no question, and stated supported construction decisions. | Universal restraint **2/2** and job activation **2/2**. Run 1 stated a supported decision; run 2 ended with an avoidable representation question. | +| S3 restraint | Universal restraint **2/2**, job activation **2/2**, supported defect **2/2**. | Universal restraint **2/2**, supported defect **2/2**, but job activation only **1/2**. | +| Revised S4 required disclosure | **0/1.** No skill activation; asked a weak question already answered by the account rather than exposing reviewer availability during appeal. | **0/1.** No skill activation; described the unsupported reviewer-association choice but did not ask the required focused question. | + +### S1 integrated judgment + +Both successful B disclosure runs asked one purpose-focused question in operational vocabulary. +Run 1 also read `workpiece-template.md` prematurely; that is progressive-disclosure overreach but +does not erase the completed topology route. A's first run asked a three-part opening battery. +A's other two outputs asked one purpose question, but universal and SDCPN elicitation judgment had +not entered context, so they fail composition regardless of fluency. + +### Revised S4 shared failure + +The revised prompt no longer announced the missing distinction or supplied the expected question. +Nevertheless, both candidates answered directly from the visible account and target. Neither +obeyed the shared router requiring `sdcpn-modelling` activation for review. Because this failure +occurred before the A/B disclosure action, the pair cannot discriminate topology and triggered the +mandatory stop. + +## Cost and stopping + +V3 completed 16 scenario runs and 37 provider invocations, using 175,762 total tokens, 228,135 ms +summed model latency, and USD 0.2399051. It remained below the separately authorized ceilings of +60 invocations and USD 1.00. + +The campaign completed all S1, S2, and S3 pairs. It stopped after the first S4 pair; four planned +S4 runs were not dispatched. Stopping preserved paired evidence and followed the frozen +shared-failure rule. + +## Decision + +Candidate A is not behaviorally viable under v3: it missed independent disclosure in all three S1 +runs, despite successful job-skill activation and a direct required-action instruction. Candidate +B shows stronger S1 routing, but cannot be declared the behaviorally validated fallback because it +also missed one S1 disclosure and the only S4 disclosure opportunity. + +The bounded result is: + +1. independent skill mounting is mechanically sound but unreliable for the current production + model in this instrument; +2. packaged resource disclosure is more reliable for underspecified opening elicitation; +3. the shared review router remains behaviorally weak; and +4. question dosage, premature workpiece loading, and review routing return to owner-led Mission 4 + runbook work. + +Do not create a v4 by momentum or revise candidate content from these results. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/manifest.json new file mode 100644 index 00000000000..62f2d877d20 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/manifest.json @@ -0,0 +1,149 @@ +{ + "sideQuestId": "flue-skill-composition-side-quest-v3", + "sourceCommit": "5249a73f09977ad2ef007e08de7b7314f94568e1", + "sourceState": "dirty side-quest instrument; every instrument file is content-addressed below", + "model": "anthropic/claude-haiku-4-5", + "runtime": { + "boundary": "built production ChatAgent loaded from apps/brunch-agent/dist/app.mjs", + "flue": "2.0.3", + "hermeticProvider": "@earendil-works/pi-ai faux provider 0.83.0", + "constructionToolsMounted": false, + "stop": "first consequential question, finding, or construction decision" + }, + "candidateRenderedText": { + "corePrompt": "runs/hermetic/independent-S1.json#/instrument/renderedSha256/candidateCorePrompt", + "sharedSdcpnAppend": "runs/hermetic/independent-S1.json#/instrument/renderedSha256/v3SdcpnAppend", + "independentPluginActivation": "runs/hermetic/independent-S1.json#/toolCalls/0/output", + "independentElicitationActivation": "runs/hermetic/independent-S1.json#/toolCalls/1/output", + "packagedPluginActivation": "runs/hermetic/packaged-S1.json#/toolCalls/0/output", + "packagedUniversalResource": "runs/hermetic/packaged-S1.json#/toolCalls/1/output" + }, + "sourceSha256": { + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "d2d42344c40ddeb2471eeeba437032079bd2c7083afdbc2f9de5e823f841d5b8", + "apps/brunch-agent/src/evaluations/skill-composition/candidate-contract.ts": "ebec2ac5faab6cbbbe26eeeebc2d7ac0ba7dc637fd43ea9b5917f8e0277f5515", + "apps/brunch-agent/src/evaluations/skill-composition/candidates.ts": "c7cb5b2222b0d1b5aff861c4056f41ba8825aca284ec774d8e4f0fbf12807314", + "apps/brunch-agent/src/evaluations/skill-composition/run.ts": "321f8405c12374315d61caf466001d6b4b5cf68b04325df77a7948daaa680de1", + "libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json": "1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb", + "libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/protocol.md": "ef1443bb02c6c26d4ded6edf9e023579bc32cdc394f90490edfb34eaf32b8ed2", + "libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v3.md": "8cd2749c8292f01045ab4282fc7248c07046cecaf14a586a9ef286e2876cf8bf", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "527fc0b3472b5c1953a89b691da0883dc2eb93dad1cdaed21b1cc7e1db0c8187", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/APPEND_SYSTEM.md": "8826b85d32d6c24fbc8f678ec394600676ec61c4a0b83d0a9cfa16be87fc5d76", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/SKILL.md": "b09a02e92602bf67f54ce3988a4e1af438b344fb7dd654936e127905451f0025", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/checks.md": "9487f40c73398e2008c10cd6f85c60735c34e59ab2f5bb4f59dddf863552f2a5", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/instructions.md": "b6d632a0e9ad5b21253fdbae792e1546ce5bd9cb0a0c999befc7e3dfd2f3e7f4", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/sdcpn-elicitation.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/workpiece-template.md": "4ee0dad11d802ff7918ef8660c42e3be8caef0eb923e92ab4084b70f2f110ec9", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "18ff63996ea2c0656f246e3e36a30962de3e03c106a25305614eb66ebf0f5717", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "1a2417cc5f9649d459c5efbc87cd3242db99a9717622a6d86073c72835818865" + }, + "renderedSha256": { + "candidateCorePrompt": "6a458c0bbbc9dee5b4454a54b032818227ab681ba9f7beaa60d4fe9628f6eb41", + "v3SdcpnAppend": "73cac46487d33051a90cc487ea7abfc11fec4b049af11a108396ec08d44569fb", + "elicitationInstructions": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "independentPluginInstructions": "0c10c1e3bc61fb39fc799584d050e1170c7b7befd07f74dd6ebca8921f3398fd", + "packagedPluginInstructions": "4a2410540717af8c740c57d7e74efd290f74cf3a23590d41acb632bd3f31a0f5", + "packagedUniversalResource": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716" + }, + "promptSelection": { + "candidateCorePromptIncluded": true, + "v3SdcpnAppendIncluded": true, + "legacyCorePromptIncluded": false, + "legacyRoleMarkerIncluded": false + }, + "parity": { + "universalSubstanceByteIdentical": true, + "normalizedPluginInstructionsByteIdentical": true, + "promptAppendScenarioByteIdenticalBetweenCandidates": true, + "intentionalDifferences": [ + "Candidate A mounts elicitation independently; Candidate B packages universal-elicitation.md.", + "The required-action line uses activate_skill for A and read_skill_resource for B.", + "Candidate A adds the mechanically required elicitation skill name and activation-cue description." + ] + }, + "plannedPaidRunOrder": [ + "S1-independent-r1", + "S1-packaged-r1", + "S1-packaged-r2", + "S1-independent-r2", + "S1-independent-r3", + "S1-packaged-r3", + "S2-packaged-r1", + "S2-independent-r1", + "S2-independent-r2", + "S2-packaged-r2", + "S3-packaged-r1", + "S3-independent-r1", + "S3-independent-r2", + "S3-packaged-r2", + "S4-packaged-r1", + "S4-independent-r1", + "S4-independent-r2", + "S4-packaged-r2", + "S4-packaged-r3", + "S4-independent-r3" + ], + "budget": { + "authorizedAdditionalModelInvocations": 60, + "authorizedAdditionalUsd": 1, + "usedModelInvocations": 37, + "usedUsd": 0.2399051, + "stopReason": "The first revised-S4 pair bypassed sdcpn-modelling and universal disclosure in both candidates. This symmetric shared-router failure triggered the frozen stop rule before the remaining S4 pairs." + }, + "routingCounts": { + "S1": { + "independent": "0/3", + "packaged": "2/3" + }, + "S4": { + "independent": "0/1", + "packaged": "0/1" + }, + "restraint": { + "independentUniversalDisclosure": "0/4", + "packagedUniversalDisclosure": "0/4" + } + }, + "hermeticRuns": [ + "runs/hermetic/independent-S1.json", + "runs/hermetic/packaged-S1.json", + "runs/hermetic/independent-S2.json", + "runs/hermetic/packaged-S2.json", + "runs/hermetic/independent-S3.json", + "runs/hermetic/packaged-S3.json", + "runs/hermetic/independent-S4.json", + "runs/hermetic/packaged-S4.json" + ], + "paidRuns": [ + "runs/paid/S1-independent-r1.json", + "runs/paid/S1-packaged-r1.json", + "runs/paid/S1-packaged-r2.json", + "runs/paid/S1-independent-r2.json", + "runs/paid/S1-independent-r3.json", + "runs/paid/S1-packaged-r3.json", + "runs/paid/S2-packaged-r1.json", + "runs/paid/S2-independent-r1.json", + "runs/paid/S2-independent-r2.json", + "runs/paid/S2-packaged-r2.json", + "runs/paid/S3-packaged-r1.json", + "runs/paid/S3-independent-r1.json", + "runs/paid/S3-independent-r2.json", + "runs/paid/S3-packaged-r2.json", + "runs/paid/S4-packaged-r1.json", + "runs/paid/S4-independent-r1.json" + ], + "paidTotals": { + "scenarioRuns": 16, + "modelInvocations": 37, + "inputTokens": 63542, + "outputTokens": 18373, + "cacheReadTokens": 28531, + "cacheWriteTokens": 65316, + "totalTokens": 175762, + "summedModelLatencyMs": 228135, + "providerCostUsd": 0.2399051 + }, + "outcome": "invalid-or-both-behaviorally-weak" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/retired-runs.sha256 b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/retired-runs.sha256 new file mode 100644 index 00000000000..ea011f458da --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/retired-runs.sha256 @@ -0,0 +1,24 @@ +8b6850bc4415bb034fbd1e8dae1b4a10d1dcf73edaf2c8913f370ecfcb2189bb libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/independent-S1.json +2806b807a246cbae27a594d90cace76c0550100e3e443c3bc35cd6e037b738b1 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/independent-S2.json +46ecdaa61ac6a4bf4120d3c97561bdb16eaeca528861f7a42de1e3a8c8c3cb7a libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/independent-S3.json +b023190478ffca254c11d2a5ab6794c0071c7b05bf6f71af329332526f0abe67 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/independent-S4.json +f00dcde4b5d61de5a546ec77fc724200b6b2c2dcaa7f2279d282ee19bcada334 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/packaged-S1.json +dc68261fdd0a03f9b0ddf9f08b97a66bf7d30036b8293ca27352e22b562b4c2f libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/packaged-S2.json +302e475471a8322d2ab00875f30381a6f3da06315817a14cf4829dc99fee8f7f libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/packaged-S3.json +68eb6220e20d371766bea03941b017ab61cdd5e4abdfcfd3a611cf5ea1a00b41 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/hermetic/packaged-S4.json +96601c7f7ad955901ed0431bf67382c092ff3ae4bba076ebd79f046708c84cee libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-independent-r1.json +91ba4787c29c1b16b0240fd47513e10203b93da0d43b06c78bbb4125c65ca8a2 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-independent-r2.json +0ab764d9d7c93725e38a049575a664a50ab3832e047f359609e8fa59ef34874c libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-independent-r3.json +42759c6c3910c8a987fcb7550b2eed932174124a5c7f848427a427e177f6766d libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-packaged-r1.json +c39aaf0c4943c861c74c393b2909f5ae58cd552478efac5c1b5b496e8260cfba libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-packaged-r2.json +dc3ae3906fe21af8f629c07093aeaf6aa40de3d46730adbbc0d519b9f64c900f libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S1-packaged-r3.json +077527ed0861c5fb63ffd51f3da55b124b2eb337673da4509501536f38538def libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S2-independent-r1.json +6d20316ffc5289b0e15f1ad8e2eea435658a2767367379c5aefdbd02350ca75b libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S2-independent-r2.json +5d7a60dcf2fec7b26b9405c2b9b19eea74fb4052e709af0bd25cfc11794a9e62 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S2-packaged-r1.json +30da42f5f2538090d0f9c940b4f7a5b7025210c766c98320e3fce693b80016d4 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S2-packaged-r2.json +364dede2519de270b011e32859625595327e2998b82fa4da232e2659aef021ee libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S3-independent-r1.json +248a0367f36872880527af6c2daab70182ff0dbae0ae64f4e9b9d0d08c0f17ed libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S3-independent-r2.json +04f6b2c78d72f64a536f6e7d587bacf933c36c66c221e44ea1cde556ec2cc19e libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S3-packaged-r1.json +10230ca9885ca6961978783aa6856af073263038b8edf224789162daec0fc392 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S3-packaged-r2.json +556cfbdea3355682cf7610121cb5a5ae33e754816cefdbb1cf5255353e3ce0a0 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S4-independent-r1.json +127669acc1cb56ee876034b6d8dfd8c3372028073a1e372b35f27de4001ad9d3 libs/@hashintel/brunch-agent/docs/evidence/evaluations/flue-skill-composition-side-quest-v3/runs/paid/S4-packaged-r1.json diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/live-observable-persona-spike/README.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/live-observable-persona-spike/README.md new file mode 100644 index 00000000000..4740466e5a6 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/live-observable-persona-spike/README.md @@ -0,0 +1,157 @@ +# Live-observable persona spike + +## Disposition + +Completed on 2026-09-02. The spike established a local working line in which one project-defined +Pi persona sent three sequential user turns through `brunch_turn` to the production Brunch Flue +`ChatAgent`, while the existing browser chat and transcript CLI read the same canonical +conversation. + +This is mechanism evidence only. It does not establish persona fidelity, elicitation quality, +full-run completion, workpiece quality, repeatability, crash recovery, remote operation, or +production readiness. + +The harness is useful for the future `Observability and simulation viewing` planning cluster, but +it is not currently load-bearing for Mission 4 proof item 5. Mission 4 already has a production +headless candidate path, and no candidate attempt has failed because this harness was absent. + +## Identity and topology + +| Field | Observed value | +| --- | --- | +| Origin | `http://127.0.0.1:4321` | +| Principal | `local` | +| Persona / conversation id | `brunch-persona-spike-20260902a` | +| Flue instance id | `d7878c473bbbf00dc51ff35a9d7caa997d31873d615c4ad688d4eef00c1df35d` | +| Elicitor | production `brunch-chat-agent` | +| Persona model | `anthropic/claude-sonnet-4-6`, medium thinking | +| Elicitor model | production default `anthropic/claude-haiku-4-5` | + +Herdr's project-agent discovery resolved `.pi/subagents/brunch-persona.md` from the Brunch context +root as a project definition. Its declared extension resolved to +`.pi/extensions/brunch-turn.ts`. The visible child command used `--no-extensions`, explicitly +loaded the Herdr companion/state extensions and `brunch-turn.ts`, disabled skills, and selected +exactly: + +```text +--tools brunch_turn,ask_parent +``` + +It exposed no `subagent`, shell, file, browser, or web tool. + +The actor received a bounded subset of the existing Vestera interviewee pack and a three-turn +mechanism objective. No oracle, target model, repository-reading tool, or scenario fact was added +to the reusable persona definition. + +## Commands + +The application and static proof used: + +```sh +yarn workspace @apps/brunch-agent lint:tsc +yarn workspace @apps/brunch-agent lint:eslint +yarn workspace @apps/brunch-agent test:unit +yarn workspace @apps/brunch-agent build +``` + +The normal server was started from `apps/brunch-agent` with its Vite `dev` command. The observer +URL was: + +```text +http://127.0.0.1:4321/?mode=observe&principal=local&id=brunch-persona-spike-20260902a +``` + +Canonical history was printed with: + +```sh +yarn workspace @apps/brunch-agent transcript -- \ + --principal local \ + --id brunch-persona-spike-20260902a +``` + +## Turn and submission comparison + +| Turn | Persona message | Submission id | Pi reply, observer, and CLI | +| --- | --- | --- | --- | +| 1 | Introduced the scheduling-model objective and requested an interview. | `sub_01M1GSDR0M6F5MDA0H5NSH9FJ5` | Exact elicitor scope questions matched. | +| 2 | Named late orders, changeover hours, hold-versus-washdown, weekly horizon, and users. | `sub_01M1GSE5JCB4FHAHFD11HYGGBJ` | Exact request for a concrete order-transition scenario matched. | +| 3 | Described the three lines, product families, asymmetric changeovers, and a Tuesday example. | `sub_01M1GSEMTX9GYCM2BBCZTA83EK` | Exact follow-up on the waiting decision and order timing matched. | + +All submission ids were distinct. Each Pi result rendered the persona message, the settled +submission id, and the exact elicitor text. The browser showed the same three visible user and +assistant message pairs in the same order. The transcript CLI showed that same visible sequence +and additionally retained the canonical diagnostic `activate_skill` tool part; it did not invent a +second chat transcript. + +## Live observer and reload + +The observer displayed `READ-ONLY` and exposed no textbox, button, composer, or send action. A +normal visit to `/` still displayed the writable textbox and Send button. An observer URL with a +non-`local` principal displayed an explicit identity error and no composer. + +Opening the observer before the first admission produced an idle view that did not discover the +subsequently created instance. Reloading after the instance existed reconstructed the first two +settled pairs and established the live subscription. The third user message appeared there and +the observer reported `STREAMING` while its assistant response was in progress. Reloading again +after settlement reconstructed all three pairs in the same order and returned to `IDLE`. + +The resulting operational rule is concrete: admit the first turn, then attach the observer. +Pre-creation observation is not a substitute for that ordering. No retry layer or parallel +observer protocol was added. + +## Failures and paid activity + +The initial actor process used an `@file` reference for the launch-supplied pack. The child did not +receive expanded file contents and correctly raised one `ask_parent` orchestration blocker. After +the bounded pack was supplied through that channel, Pi crashed before any `brunch_turn` call while +rendering the long parent answer: one rendered line exceeded the pane width. Canonical browser +history was inspected and was empty, so there was no admitted or indeterminate Flue submission. +The same conversation identity was safely restarted with the bounded pack inline. A controlled +regression now also proves the `brunch_turn` custom renderer stays within its supplied width. + +Paid activity remained within the side-quest cap: + +- Persona: five completed model continuations across the pre-admission startup and successful + three-turn path, plus one possibly in-flight continuation at the renderer crash; conservatively + counted as six. Pi reported approximately USD 0.062 total. +- Production elicitor: three admitted submissions. Canonical history showed three assistant + replies and one `activate_skill` continuation, for four observed provider model calls and no + more than the 25-call cap. +- Paid graders, judges, external-provider auxiliary research calls, and additional Flue runs: + zero. Two repository-only Cursor planning scouts ran before the paid path; neither called the + persona or elicitor provider. + +There were no failed or aborted Flue settlements, duplicate visible user messages, incarnation +conflicts, empty assistant replies, client-executed Petrinaut tool requests, or replies selected +from latest history. + +## Proof disposition + +1. Controlled tool boundary: passed. Eight tests cover exact `send`/`read`, submission ids, + incarnation uid conditioning, concurrency, no resend, empty replies, Flue failures, and bounded + rendering. +2. Project persona discovery and restricted loadout: passed. +3. Three sequential real turns with exact submission-scoped replies: passed. +4. Independently attached read-only observer with no composer: passed after the first conversation + admission, as required by the connected-line ordering. +5. Reload reconstruction and resumed live observation: passed. +6. Transcript visible-message order comparison: passed; canonical tool diagnostics were also + present. +7. Normal writable chat preservation: passed. +8. App type-check, lint, unit tests, and build: passed. + +## Fog-line answers and re-entry + +- The topology is usable for a bounded local mechanism proof. This run does not show that it + scales to a full elicitation or final workpiece/model. +- Generic persona instructions were sufficient for this bounded run once the situation pack was + actually supplied inline. A reusable situation-pack transport or schema is not earned. +- The observer is useful evaluation infrastructure, but this run does not justify productizing or + remotely exposing it. +- No client-executed Petrinaut tools were needed in three turns. Longer runs remain unproven. +- Pending-admission persistence was not needed and remains deferred. +- Canonical history plus the existing transcript CLI were sufficient; no convenience history or + status tool is warranted. + +Re-enter only for a named consumer that needs a longer run, pre-creation observer discovery, +client-tool execution, crash recovery, remote access, or broader conversation-part rendering. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/README.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/README.md new file mode 100644 index 00000000000..d4161bf039d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/README.md @@ -0,0 +1,14 @@ +# Mission 4 proof-of-life v1 result + +Status: **retired after an instrument failure; do not resume or rerun.** + +The frozen protocol's embedded status still says “not frozen and not authorized” because `protocol.md` was committed as the instrument candidate before the separate manifest and owner-authorization commits. That historical text is deliberately immutable. [`mission-4-proof-of-life-freeze-acceptance-2026-09-03.md`](../../decisions/mission-4-proof-of-life-freeze-acceptance-2026-09-03.md) supersedes it and records the actual v1 freeze and paid authorization. + +V1 admitted only the Vestera primary and its sole permitted replacement. Both attempts were technically valid and showed the required mechanism order—`sdcpn-modelling`, `elicitation`, then the SDCPN profile read—but the persona stopped after one Orientation question because its private objective asked it to recognize an evaluator-owned “Substantive” category without receiving the ruler that defined it. Fresh adjudication therefore recorded both attempts as valid with no Substantive text, not 4a-gradable, and not floor-satisfying. + +The frozen v1 serial stop rule ended execution before Data Centre. The retained runs are: + +- [`m4-pol-v1-vestera-p1`](runs/m4-pol-v1-vestera-p1/) +- [`m4-pol-v1-vestera-r1`](runs/m4-pol-v1-vestera-r1/) + +The causal record and v2 repair decision are in [`mission-4-retire-v1-and-cut-v2-2026-09-03.md`](../../decisions/mission-4-retire-v1-and-cut-v2-2026-09-03.md). V1 remains evidence of the instrument behavior; it is not evidence that Brunch failed activation or restraint. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/adjudication.md new file mode 100644 index 00000000000..0fcd640e343 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/adjudication.md @@ -0,0 +1,89 @@ +# Adjudication: m4-pol-v1-vestera-p1 + +| Field | Value | +| --- | --- | +| Adjudicator model | anthropic/claude-opus-4-6 | +| Thinking | high | +| Ruler | mission-4-activation-and-restraint-ruler-v1 | +| Run id | m4-pol-v1-vestera-p1 | +| Run kind | Interactive entry (first-Substantive probe) | +| Conversation id | conv_01M1K5N2JXXQ93286TMVXBMXMD | + +## 1. Visible assistant text classification + +The run contains exactly one assistant text, in turn 1. + +### Turn 1 — text (sequence 5) + +> Great — thank you for the context. Before I start asking about the plant itself, I want to make sure the model we build is shaped around what it actually needs to answer. +> +> **What kind of scheduling decisions are you hoping to test with the simulation?** For example: whether a particular run sequence causes bottlenecks, how a new order fits into an existing week's plan, whether adding capacity somewhere would help — or something else entirely? + +**Classification: Orientation.** + +Deciding quote: *"What kind of scheduling decisions are you hoping to test with the simulation?"* + +This asks the person to clarify the **purpose and intended decision** the model must serve. The illustrative examples (bottlenecks, fitting a new order, adding capacity) are offered as the adjudicator's contrastive alternatives for the person to select among — they do not ask the person to supply operational knowledge of how the coatings plant works, who does what, when, how often, or under what condition. The text satisfies the Orientation definition ("asks or confirms purpose, intended decision, audience, boundary, horizon, accuracy need, or available time, or clarifies the person's own request. Asks for no operational fact about how the domain works") and does not satisfy the Substantive definition. + +**Dosage (Orientation text): Deepening.** One coherent question pursuing one thread (what the simulation needs to answer), with illustrative alternatives that share a single frame (kinds of scheduling decision). Not a Battery. + +## 2. First Substantive text (T_sub) + +**No Substantive text exists in this run.** The sole assistant text is Orientation. The run is recorded as `no substantive question`. + +## 3. Item 4a — Activation before substance + +**Not applicable.** Per the ruler: "A run with no Substantive text within its budget is recorded as `no substantive question` and excluded from item 4a … proportions but reported." There is no T_sub against which to measure activation ordering. + +## 4. Item 5a — Conditional profile read before reliance + +**Not applicable.** Same exclusion as 4a: no Substantive text exists, so there is no reliance event to precede. + +## 5. Item 5d — Opening dosage + +**Not applicable.** The ruler states: "The first Substantive text of every interactive-entry run must not be a Battery." No first Substantive text exists, so the opening-Battery prohibition cannot be evaluated. + +## 6. Trace verification (mechanism presence, not converted to pass) + +The derived trace from `trace.json` shows: + +| Sequence | Event | Outcome | +| --- | --- | --- | +| 1 | `user(1)` | — | +| 2 | `activate(sdcpn-modelling, ok)` | ok | +| 3 | `activate(elicitation, ok)` | ok | +| 4 | `read(sdcpn-modelling/references/profile.md, ok)` | ok | +| 5 | `text(1, hasWorkpiece=false)` | — | + +**All three mechanism events are present and correctly ordered:** +- `activate(sdcpn-modelling, ok)` at sequence 2 precedes `activate(elicitation, ok)` at sequence 3. ✓ +- Both activations precede the profile read at sequence 4. ✓ +- The profile read precedes the sole text at sequence 5. ✓ + +**Per the ruler's instruction, this correct mechanism ordering is not converted into a 4a or 5a pass because no Substantive text exists.** The mechanism fired correctly but did not reach the point where its constraint becomes testable. + +## 7. Run validity + +The run is **valid**: the Flue snapshot shows `outcome: "completed"` with no runtime or transport error, no unresolved client-tool suspension, no elicitor response with no text and no tool call, no persona refusal signal. The first user message matches the expected interactive-entry opening. There is nothing in the transcript indicating the persona mentioned its budget, instructions, or the evaluation. + +## 8. Floor qualification (item 4b) + +**This run does not satisfy the 3/3 floor.** The ruler states: "Invalid members and valid members with no Substantive text are retained and reported but do not satisfy the floor." This run is a valid member with no Substantive text. It is retained and reported but cannot count as one of the three required 4a-passing runs. + +## 9. Fresh-id replacement + +**The frozen protocol permits one fresh-id replacement attempt.** The ruler states that valid members with no Substantive text "do not satisfy the floor; the frozen protocol must bound replacement attempts under fresh run ids and stop when the floor cannot be reached within its authorized ceiling." This run may be replaced by a new attempt under a fresh run id for the same case family (vestera). This run (`m4-pol-v1-vestera-p1`) is retained with its full evidence regardless. + +## Summary + +| Check | Result | Reason | +| --- | --- | --- | +| Turn 1 text classification | **Orientation** | Asks about purpose/intended decision; no operational-domain question | +| T_sub | **None** | No Substantive text in run | +| 4a (activation before substance) | **Not applicable** | No T_sub | +| 5a (profile read before reliance) | **Not applicable** | No T_sub | +| 5d opening (first Substantive not Battery) | **Not applicable** | No T_sub | +| Trace mechanism order | **Correct** (not converted to pass) | sdcpn-modelling → elicitation → profile read, all before text | +| Run validity | **Valid** | No disqualifying condition | +| Satisfies 3/3 floor | **No** | Valid, no Substantive text | +| Fresh-id replacement permitted | **Yes** | Protocol allows bounded replacement under new run id | diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/manifest.json new file mode 100644 index 00000000000..cfa11ac3b8a --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "e9a57dcb47b1186ff387910b5bb4d10d8a72b07b1aa03e8c59f316e53a0f0940" + }, + { + "path": "run.json", + "sha256": "a91cc60aba5af658780de4a616b6c4e4ccfe2db5f3db1c54d61b47d861af9fde" + }, + { + "path": "snapshot.json", + "sha256": "ae29ceea231590465ee8d774db29cbc24f3a9f5e52886b451dc6e16e5e44b61e" + }, + { + "path": "trace.json", + "sha256": "f95283c9b478749c985064be552b1e9c7a2905076b99106e8c4e319b8ac36db4" + }, + { + "path": "trace.md", + "sha256": "9fbe1a7910e8be1a2838f609794d5b629096d59a797843d3460080a6b962ddfb" + }, + { + "path": "transcript.md", + "sha256": "fda3d432fb5f7f1406b75fe8688217ecf24193b7cb516ba30b4d1dd3cc71c750" + }, + { + "path": "usage.json", + "sha256": "151e8457af1f3228d746ea3477624847c31f43808a9ee566db74547c90d724a2" + }, + { + "path": "validity.json", + "sha256": "10c4ad8e3dc9126a43aba2b1ca61d6c2d161e6e97921845751de5bdbb8fee660" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/run.json new file mode 100644 index 00000000000..b81a8e525f3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/run.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "campaign": "mission-4-proof-of-life-v1", + "attemptId": "m4-pol-v1-vestera-p1", + "slot": "vestera-probe", + "runKind": "interactive-probe", + "case": "vestera-scheduling", + "instrumentCommit": "ce2fbde9d96faaaf52ecf532e1071d3d9e952f1a", + "freezeCommit": "cc9a68497d", + "executionHead": "297161ac0d", + "models": { + "elicitor": { + "provider": "anthropic", + "requested": "claude-sonnet-4-6" + }, + "persona": { + "provider": "openai", + "requested": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "maximum": 3, + "stopAfter": "first Substantive elicitor text" + }, + "startedAt": "2026-09-03T08:18:35.436926+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/snapshot.json new file mode 100644 index 00000000000..69c8799265d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/snapshot.json @@ -0,0 +1,87 @@ +{ + "v": 1, + "conversationId": "conv_01M1K5N2JXXQ93286TMVXBMXMD", + "offset": "0000000000000000_0000000000000030", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLNU4ySlY3MlZTOTEyQ0NQMEhCNERW", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K5N2JV72VS912CCP0HB4DV", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K5N41KAKWCST5RCXB0HFZS", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K5N2JV72VS912CCP0HB4DV", + "turnId": "turn_01M1K5N2KPD0S27W6NYCS8MP91", + "parts": [ + { + "type": "reasoning", + "text": "The user wants me to help them model their coatings plant scheduling operation as an SDCPN in Petrinaut. I need to activate the sdcpn-modelling skill first before substantive interviewing.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01RKrtgKfgE4hSwiphzSYAWW", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 4 + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01B3dT3mAokwc9ijrj55fLdu", + "state": "output-available", + "input": { + "name": "elicitation" + }, + "output": "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_011oDMarGpEBMVhcNUirUNb6", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Now I have the elicitation guidance and the profile. Let me also read the workpiece template before I start interviewing.\n\nLet me start the interview. I should ask one focused opening question to start the elicitation. I want to understand the purpose first - what the simulation model needs to help them decide or answer.", + "state": "done" + }, + { + "type": "text", + "text": "Great — thank you for the context. Before I start asking about the plant itself, I want to make sure the model we build is shaped around what it actually needs to answer.\n\n**What kind of scheduling decisions are you hoping to test with the simulation?** For example: whether a particular run sequence causes bottlenecks, how a new order fits into an existing week's plan, whether adding capacity somewhere would help — or something else entirely?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K5N2JV72VS912CCP0HB4DV", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K5N2JV72VS912CCP0HB4DV" + } + ], + "incarnation": "inc_01M1K5N2JV7DT4CH1T60V8XBGW" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.json new file mode 100644 index 00000000000..25e1b2aca35 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.json @@ -0,0 +1,47 @@ +{ + "conversationId": "conv_01M1K5N2JXXQ93286TMVXBMXMD", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLNU4ySlY3MlZTOTEyQ0NQMEhCNERW", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K5N41KAKWCST5RCXB0HFZS", + "toolCallId": "toolu_01RKrtgKfgE4hSwiphzSYAWW", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K5N41KAKWCST5RCXB0HFZS", + "toolCallId": "toolu_01B3dT3mAokwc9ijrj55fLdu", + "name": "elicitation", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K5N41KAKWCST5RCXB0HFZS", + "toolCallId": "toolu_011oDMarGpEBMVhcNUirUNb6", + "path": "sdcpn-modelling/references/profile.md", + "outcome": "ok", + "sequence": 4 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K5N41KAKWCST5RCXB0HFZS", + "text": "Great — thank you for the context. Before I start asking about the plant itself, I want to make sure the model we build is shaped around what it actually needs to answer.\n\n**What kind of scheduling decisions are you hoping to test with the simulation?** For example: whether a particular run sequence causes bottlenecks, how a new order fits into an existing week's plan, whether adding capacity somewhere would help — or something else entirely?", + "hasWorkpiece": false, + "sequence": 5 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.md new file mode 100644 index 00000000000..cfc125173d1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/trace.md @@ -0,0 +1,9 @@ +# Canonical proof trace + +Conversation: `conv_01M1K5N2JXXQ93286TMVXBMXMD` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLNU4ySlY3MlZTOTEyQ0NQMEhCNERW` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_01RKrtgKfgE4hSwiphzSYAWW` +3. turn 1: `activate(elicitation, ok)` — call `toolu_01B3dT3mAokwc9ijrj55fLdu` +4. turn 1: `read(sdcpn-modelling/references/profile.md, ok)` — call `toolu_011oDMarGpEBMVhcNUirUNb6` +5. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K5N41KAKWCST5RCXB0HFZS` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/transcript.md new file mode 100644 index 00000000000..4c14bccf707 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/transcript.md @@ -0,0 +1,12 @@ +## User +I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person. + +Please interview me about how our operation works, and then produce the model. + +## Assistant +Great — thank you for the context. Before I start asking about the plant itself, I want to make sure the model we build is shaped around what it actually needs to answer. + +**What kind of scheduling decisions are you hoping to test with the simulation?** For example: whether a particular run sequence causes bottlenecks, how a new order fits into an existing week's plan, whether adding capacity somewhere would help — or something else entirely? +- tool activate_skill (toolu_01RKrtgKfgE4hSwiphzSYAWW): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool activate_skill (toolu_01B3dT3mAokwc9ijrj55fLdu): "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>" +- tool read_skill_resource (toolu_011oDMarGpEBMVhcNUirUNb6): "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n" diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/usage.json new file mode 100644 index 00000000000..2b6717e2bb7 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/usage.json @@ -0,0 +1,19 @@ +{ + "currency": "USD", + "persona": { + "model": "openai/gpt-5.6-sol", + "cost": 0.019, + "source": "Pi terminal usage display; rounded" + }, + "elicitor": { + "model": "anthropic/claude-sonnet-4-6", + "cost": null, + "source": "Flue canonical history does not expose provider usage; reconcile from Anthropic billing before close" + }, + "adjudicator": { + "model": "anthropic/claude-opus-4-6", + "cost": 0.279, + "source": "Pi terminal usage display; rounded" + }, + "knownRoundedTotal": 0.298 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/validity.json new file mode 100644 index 00000000000..e6bae7d30df --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-p1/validity.json @@ -0,0 +1,9 @@ +{ + "status": "valid-no-substantive", + "technicallyValid": true, + "substantiveTextObserved": false, + "qualifiesForFloor": false, + "replacementPermitted": true, + "stopReason": "Persona stopped after the first elicitor question, which the fresh-context adjudicator classified as Orientation rather than Substantive.", + "adjudication": "adjudication.md" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/adjudication.md new file mode 100644 index 00000000000..9591075c578 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/adjudication.md @@ -0,0 +1,125 @@ +# Adjudication — m4-pol-v1-vestera-r1 + +| Field | Value | +| --- | --- | +| Adjudicator model | `anthropic/claude-opus-4-6` | +| Thinking mode | high | +| Ruler | `mission-4-activation-and-restraint-ruler-v1.md` | +| Run kind | Interactive entry (first-Substantive probe) | +| Conversation | `conv_01M1K6FYRXN4GGXX8N90M9XSX4` | +| Visible user turns | 1 | + +--- + +## 1 — Turn-by-turn text classification + +The run contains exactly one assistant text, produced in turn 1. + +### Turn 1 — sole assistant text + +> Great — a scheduling simulation for a coatings plant is exactly the kind of problem this process is designed for. Before we get into how the operation works, let me ask one orienting question, because the answer will shape everything we spend time on: +> +> **What's the scheduling decision — or type of decision — that your boss most wants to be able to test?** For example, is it something like "should we run this job now or hold it for a better batch?", "how many lines should we staff on a given day?", "what sequence minimises changeovers?" — or something else entirely? +> +> Once I know what the model needs to help you decide or compare, I'll know where to focus the detail. + +**Classification: Orientation.** + +Deciding quote: *"What's the scheduling decision — or type of decision — that your boss most wants to be able to test?"* + +This asks about the intended decision and purpose of the simulation — what the model must help compare or decide. The three bracketed examples are the elicitor's proposed illustrations of purpose, not requests for operational facts. The text asks for no operational knowledge of how the domain works (no "how something works, who does it, when, how often, how much, under what condition, or what happens when"). It falls squarely under the Orientation definition: "asks or confirms purpose, intended decision, audience, boundary, horizon, accuracy need, or available time, or clarifies the person's own request." + +**Dosage (Orientation):** Deepening — pursues one answerable thread (what decision the model must support), with illustrative alternatives that share one frame. + +### T_sub determination + +There is no Substantive text in this run. The sole assistant text is Orientation. **T_sub = no substantive question.** + +Note: the validity record reports that the persona declared this text Substantive and stopped. Per the ruler, "The persona is a model output and is not the oracle" and "Pi tool details, the browser observer, and the persona's own summary are projections and never the evidence." The fresh-context adjudicator classifies independently from the visible text. The text is Orientation. + +--- + +## 2 — Trace mechanism verification + +The derived trace contains the following ordered events: + +| Sequence | Event | Detail | +| --- | --- | --- | +| 1 | `user(1)` | Opening message | +| 2 | `activate(sdcpn-modelling, ok)` | `toolu_01GuX1xn4LAFAuVeh4xsxhcH` | +| 3 | `activate(elicitation, ok)` | `toolu_01C75HCHqtDz5FEXHyqzJdgk` | +| 4 | `read(sdcpn-modelling/references/profile.md, ok)` | `toolu_015x9fCZVzwtZCux6yNnEoPF` | +| 5 | `text(1, hasWorkpiece=false)` | Sole assistant text | + +**All three prerequisites are present and correctly ordered:** + +1. `activate(sdcpn-modelling, ok)` at seq 2 precedes `activate(elicitation, ok)` at seq 3. ✓ +2. Both activations precede the profile read at seq 4. ✓ +3. `read(sdcpn-modelling/references/profile.md, ok)` at seq 4 precedes the sole text at seq 5. ✓ + +However, because no Substantive text exists, mechanism presence does not convert into a pass for items 4a or 5a. The mechanisms fired correctly but there is nothing to anchor them to. + +--- + +## 3 — Item results + +### Item 4a — Activation before substance + +**Not applicable.** T_sub = no substantive question. Per the ruler: "A run with no Substantive text within its budget is recorded as `no substantive question` and excluded from item 4a and 5a proportions but reported." This run is not 4a-gradable. + +### Item 5a — Conditional profile read before reliance + +**Not applicable.** Same basis as 4a: no Substantive text exists to constitute "reliance." The profile read did occur (seq 4) and preceded the sole text (seq 5), but there is no Substantive text to satisfy the check's precondition. + +### Item 5d — Opening dosage + +**Not applicable.** The ruler states: "The first Substantive text of every interactive-entry run must not be a Battery." There is no Substantive text in this run; the opening-Battery prohibition has no text to evaluate. + +The sole Orientation text is classified as Deepening for the record. + +### Item 5c — Resource restraint (observation only) + +No `read(sdcpn-modelling/references/pn-construction.md, *)` or `read(sdcpn-modelling/references/checks.md, *)` appears anywhere in the trace. No reads of resources belonging to a non-activated skill appear. No finding. + +--- + +## 4 — Floor qualification + +The accepted floor requires, per elicitor model, three valid 4a-gradable runs over three distinct case families, all passing items 4a and 5a. The ruler further states: + +> "Invalid members and valid members with no Substantive text are retained and reported but do not satisfy the floor." + +This run is technically valid (per the validity record: `technicallyValid: true`, no mechanical check failures). It is not invalid. However, it has no Substantive text and is therefore not 4a-gradable. + +**This attempt does not qualify for the 3/3 floor.** It is retained and reported but does not count as a floor-satisfying member. + +--- + +## 5 — Qualifying-member determination + +Given the validity record and ruler only: + +- The validity record confirms `technicallyValid: true` with all mechanical checks passing (no runtime error, no unresolved suspension, no empty response, no persona refusal, opening message matched). +- The run is therefore a **valid member** of the campaign evidence set — it is not invalid and is not excluded. +- It is **not a floor-satisfying member** because it has no Substantive text and is not 4a-gradable. + +The run is a qualifying member of the campaign (retained, reported, legitimate evidence) but does not contribute toward the 3/3 floor requirement. No replacement recommendation or later campaign execution recommendation is made. + +--- + +## Summary + +| Dimension | Result | +| --- | --- | +| Texts classified | 1 | +| Orientation | 1 (turn 1: purpose/decision question) | +| Substantive | 0 | +| T_sub | no substantive question | +| Mechanism order correct | Yes (sdcpn-modelling → elicitation → profile read → text) | +| 4a | Not applicable (no Substantive text) | +| 5a | Not applicable (no Substantive text) | +| 5d opening | Not applicable (no Substantive text) | +| 5c findings | None | +| Technically valid | Yes | +| Floor-satisfying | **No** | +| Campaign-qualifying member | Yes (retained and reported) | diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/manifest.json new file mode 100644 index 00000000000..0c19b7fc671 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "78b3397534fbe6297f535c819d2fea91a0f10f23622e398f936c07668c487253" + }, + { + "path": "run.json", + "sha256": "0a6f97f71c2289b210e1cdf567e13e7f93a8bf1da9ad35491381820a0ab520ce" + }, + { + "path": "snapshot.json", + "sha256": "0f52d2588553e0a6c0e4a22d89deeca6ca060ef2722b8f24d0fa78af1ff0eb3f" + }, + { + "path": "trace.json", + "sha256": "822ccb490a69396c852bb1f7aa62d71275af9ddbe7312f8b94752f8b3ba827d7" + }, + { + "path": "trace.md", + "sha256": "8773571dfbc05daf469971cc109cb1ba13670207a96d343f7688999b7d84c6de" + }, + { + "path": "transcript.md", + "sha256": "7cda86ed457a40eceb2822a5fa633730ff7ddd8e3cec4e6d4e5b05c4d121ad60" + }, + { + "path": "usage.json", + "sha256": "0c357b13fcc84e93c996bed81654956a0c03047dad3673dc91012c4db8d3ce68" + }, + { + "path": "validity.json", + "sha256": "f17de5a77e09f1c6178d95fd6306a6317d542ced8677c87f9dc975cf1f6dd1f7" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/run.json new file mode 100644 index 00000000000..4af5367c4b8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/run.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "campaign": "mission-4-proof-of-life-v1", + "attemptId": "m4-pol-v1-vestera-r1", + "slot": "vestera-probe", + "runKind": "interactive-probe", + "case": "vestera-scheduling", + "replacementFor": "m4-pol-v1-vestera-p1", + "replacementReason": "Primary was technically valid but reached no Substantive text within budget.", + "instrumentCommit": "ce2fbde9d96faaaf52ecf532e1071d3d9e952f1a", + "freezeCommit": "cc9a68497d", + "executionHead": "3b8b90476ae4a2aef8a18632f0cde17253708609", + "models": { + "elicitor": { + "provider": "anthropic", + "requested": "claude-sonnet-4-6" + }, + "persona": { + "provider": "openai", + "requested": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "maximum": 3, + "stopAfter": "first Substantive elicitor text" + }, + "startedAt": "2026-09-03T08:33:33.745901+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/snapshot.json new file mode 100644 index 00000000000..247635824b8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/snapshot.json @@ -0,0 +1,87 @@ +{ + "v": 1, + "conversationId": "conv_01M1K6FYRXN4GGXX8N90M9XSX4", + "offset": "0000000000000000_0000000000000034", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLNkZZUlRaMDBEUjIxMjUwN1NQNlIz", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K6FYRTZ00DR212507SP6R3", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K6G3HF7PVMARTQ36C1QTR7", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K6FYRTZ00DR212507SP6R3", + "turnId": "turn_01M1K6FYSZ7B5QHKV5S8FJ3NQ7", + "parts": [ + { + "type": "reasoning", + "text": "The user wants to build a simulation model of their coatings plant scheduling operation as a Petri net (SDCPN). I need to activate the sdcpn-modelling skill before substantive interviewing.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01GuX1xn4LAFAuVeh4xsxhcH", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 3 + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01C75HCHqtDz5FEXHyqzJdgk", + "state": "output-available", + "input": { + "name": "elicitation" + }, + "output": "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_015x9fCZVzwtZCux6yNnEoPF", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Now I have the elicitation and sdcpn-modelling skill guidance loaded. I should also read the workpiece template to understand what structure I need to build toward. But first, let me start the interview - I don't need to read the template yet, I can do that when I start building the workpiece.\n\nLet me start with an oriented, focused opening question. The person is a master scheduler at a coatings plant, wants a simulation model for testing scheduling decisions. I need to establish:\n1. What decisions/questions the model needs to answer\n2. The process itself\n\nI should follow the elicitation guidance: one focused opening question, follow the person's thread, use their vocabulary. \n\nI'll start by asking what specific scheduling decisions they want to test, since that purpose will shape everything else about the model.", + "state": "done" + }, + { + "type": "text", + "text": "Great — a scheduling simulation for a coatings plant is exactly the kind of problem this process is designed for. Before we get into how the operation works, let me ask one orienting question, because the answer will shape everything we spend time on:\n\n**What's the scheduling decision — or type of decision — that your boss most wants to be able to test?** For example, is it something like \"should we run this job now or hold it for a better batch?\", \"how many lines should we staff on a given day?\", \"what sequence minimises changeovers?\" — or something else entirely?\n\nOnce I know what the model needs to help you decide or compare, I'll know where to focus the detail.", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K6FYRTZ00DR212507SP6R3", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K6FYRTZ00DR212507SP6R3" + } + ], + "incarnation": "inc_01M1K6FYRVMHJRD75R6R1N1X8H" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.json new file mode 100644 index 00000000000..b28aa833230 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.json @@ -0,0 +1,47 @@ +{ + "conversationId": "conv_01M1K6FYRXN4GGXX8N90M9XSX4", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLNkZZUlRaMDBEUjIxMjUwN1NQNlIz", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K6G3HF7PVMARTQ36C1QTR7", + "toolCallId": "toolu_01GuX1xn4LAFAuVeh4xsxhcH", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K6G3HF7PVMARTQ36C1QTR7", + "toolCallId": "toolu_01C75HCHqtDz5FEXHyqzJdgk", + "name": "elicitation", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K6G3HF7PVMARTQ36C1QTR7", + "toolCallId": "toolu_015x9fCZVzwtZCux6yNnEoPF", + "path": "sdcpn-modelling/references/profile.md", + "outcome": "ok", + "sequence": 4 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K6G3HF7PVMARTQ36C1QTR7", + "text": "Great — a scheduling simulation for a coatings plant is exactly the kind of problem this process is designed for. Before we get into how the operation works, let me ask one orienting question, because the answer will shape everything we spend time on:\n\n**What's the scheduling decision — or type of decision — that your boss most wants to be able to test?** For example, is it something like \"should we run this job now or hold it for a better batch?\", \"how many lines should we staff on a given day?\", \"what sequence minimises changeovers?\" — or something else entirely?\n\nOnce I know what the model needs to help you decide or compare, I'll know where to focus the detail.", + "hasWorkpiece": false, + "sequence": 5 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.md new file mode 100644 index 00000000000..0ece1e2cc37 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/trace.md @@ -0,0 +1,9 @@ +# Canonical proof trace + +Conversation: `conv_01M1K6FYRXN4GGXX8N90M9XSX4` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLNkZZUlRaMDBEUjIxMjUwN1NQNlIz` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_01GuX1xn4LAFAuVeh4xsxhcH` +3. turn 1: `activate(elicitation, ok)` — call `toolu_01C75HCHqtDz5FEXHyqzJdgk` +4. turn 1: `read(sdcpn-modelling/references/profile.md, ok)` — call `toolu_015x9fCZVzwtZCux6yNnEoPF` +5. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K6G3HF7PVMARTQ36C1QTR7` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/transcript.md new file mode 100644 index 00000000000..86a073752a2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/transcript.md @@ -0,0 +1,14 @@ +## User +I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we schedule production, so we can test scheduling decisions before making them. We have tooling that runs Petri-net-style process models, so the end product should be a model I can hand to that — but I can't tell you much about the format, I'm not the modelling person. + +Please interview me about how our operation works, and then produce the model. + +## Assistant +Great — a scheduling simulation for a coatings plant is exactly the kind of problem this process is designed for. Before we get into how the operation works, let me ask one orienting question, because the answer will shape everything we spend time on: + +**What's the scheduling decision — or type of decision — that your boss most wants to be able to test?** For example, is it something like "should we run this job now or hold it for a better batch?", "how many lines should we staff on a given day?", "what sequence minimises changeovers?" — or something else entirely? + +Once I know what the model needs to help you decide or compare, I'll know where to focus the detail. +- tool activate_skill (toolu_01GuX1xn4LAFAuVeh4xsxhcH): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool activate_skill (toolu_01C75HCHqtDz5FEXHyqzJdgk): "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>" +- tool read_skill_resource (toolu_015x9fCZVzwtZCux6yNnEoPF): "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n" diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/usage.json new file mode 100644 index 00000000000..0392d10020a --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/usage.json @@ -0,0 +1,19 @@ +{ + "currency": "USD", + "persona": { + "model": "openai/gpt-5.6-sol", + "cost": 0.025, + "source": "Pi terminal usage display; rounded" + }, + "elicitor": { + "model": "anthropic/claude-sonnet-4-6", + "cost": null, + "source": "Flue canonical history does not expose provider usage; reconcile from Anthropic billing before close" + }, + "adjudicator": { + "model": "anthropic/claude-opus-4-6", + "cost": 0.313, + "source": "Pi terminal usage display; rounded" + }, + "knownRoundedTotal": 0.338 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/validity.json new file mode 100644 index 00000000000..29d13a637fc --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/m4-pol-v1-vestera-r1/validity.json @@ -0,0 +1,19 @@ +{ + "status": "valid-no-substantive", + "technicallyValid": true, + "mechanicalChecks": { + "settledOutcome": "completed", + "runtimeOrTransportError": false, + "unresolvedClientToolSuspension": false, + "emptyElicitorResponse": false, + "personaRefusalSignal": false, + "openingMessageMatched": true + }, + "visibleUserTurns": 1, + "personaStopReason": "Persona declared the first elicitor text Substantive and stopped after one visible user turn.", + "semanticClassification": "Orientation", + "substantiveTextObserved": false, + "qualifiesForFloor": false, + "replacementPermitted": false, + "campaignConsequence": "The sole permitted Vestera replacement is exhausted without reaching Substantive text; stop for owner adjudication." +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/attempt-ledger.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/attempt-ledger.md new file mode 100644 index 00000000000..f61fe39be5c --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/attempt-ledger.md @@ -0,0 +1,19 @@ +# Mission 4 proof-of-life v2 attempt ledger + +Campaign stopped on S4 under the frozen rule. The owner later accepted Mission 4 closure with that failure deferred as non-blocking; no replacement or full run was admitted. + +Instrument commit: `95954b494308fbba384cc4ce169a813916f164f9` + +Manifest commit: `d9ca2fe1498f6484746b2abaaf18973e7abcbeaa` + +Frozen manifest SHA-256: `91bc02e59dad3ed2d7791e3e1b095435c18fca8c78b4302e9e3bb43872e727a9` + +| Order | Slot | Attempt | Technical validity | Ruler result | Campaign disposition | +| ---: | --- | --- | --- | --- | --- | +| 1 | Vestera probe | [`m4-pol-v2-vestera-p1`](runs/m4-pol-v2-vestera-p1/) | Valid | 4a pass; 5a pass; opening 5d pass | Floor-satisfying probe | +| 2 | Data Centre probe | [`m4-pol-v2-data-centre-p1`](runs/m4-pol-v2-data-centre-p1/) | Valid | 4a pass; 5a pass; opening 5d pass | Floor-satisfying probe | +| 3 | S3 resolvable review | [`m4-pol-v2-s3-p1`](runs/m4-pol-v2-s3-p1/) | Valid | 4d pass | Review restraint satisfied | +| 4 | S4 knowledge-gap review | [`m4-pol-v2-s4-p1`](runs/m4-pol-v2-s4-p1/) | Valid | 4e fail: no `activate(elicitation, ok)` | Valid behavioral failure; campaign stopped | +| 5 | Industrial Gas full | Not admitted | — | — | Prohibited after S4 failure | + +No replacement is permitted for S4 because the primary is technically valid. The reserved S4 replacement and both Industrial Gas ids remain unused. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/final-adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/final-adjudication.md new file mode 100644 index 00000000000..0e559592b55 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/final-adjudication.md @@ -0,0 +1,17 @@ +# Mission 4 proof-of-life v2 campaign adjudication + +Status: **frozen campaign stopped on a valid behavioral failure; owner accepted Mission 4 closure with S4 deferred as non-blocking on 2026-09-03.** + +The Vestera and Data Centre fixed three-submission probes are technically valid, 4a-gradable, and floor-satisfying. In both, `sdcpn-modelling` then `elicitation` activation and the conditional SDCPN profile read preceded the first Substantive text at turn 2; neither opening was a Battery. The exact S3 resolvable-review entry is technically valid and passes item 4d without elicitation activation. + +The exact S4 knowledge-gap entry is technically valid and fails item 4e. Its trace contains `activate(sdcpn-modelling, ok)`, a read of `references/checks.md`, and then Substantive text identifying the unresolved reviewer-release rule. It contains no `activate(elicitation, *)`. The fresh adjudicator therefore applied the ruler's explicit “asks without activation” failure condition. The response did not invent a resolution, but activation before the knowledge request is independently required. + +Because S4 is a valid behavioral failure, the protocol forbids its replacement and requires stopping before Industrial Gas. Consequently: + +- the controlled positive-complement claim is falsified for this frozen run; +- the required interactive `3/3` floor cannot be completed because the Industrial Gas full run was not admitted; +- no workpiece handoff candidate exists; +- Mission 4's bounded proof-of-life claim is **not established** by v2; +- the two passing probes and S3 restraint remain attributable evidence but cannot be averaged over the S4 failure. + +Every admitted attempt is retained with canonical snapshot, transcript, trace, validity, adjudication, usage, and SHA-256 manifest. The owner subsequently accepted branch closure on the narrower implementation evidence, judged immediate review-to-elicitation switching a nice-to-have at this mission boundary, and deferred it to the optional Mission 4 addendum or the first downstream mission that makes it load-bearing. This does not change the frozen failure or authorize a production repair or v3 campaign. See [`mission-4-closure-and-deferral-2026-09-03.md`](../../decisions/mission-4-closure-and-deferral-2026-09-03.md). diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/adjudication.md new file mode 100644 index 00000000000..1f66467b1fb --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/adjudication.md @@ -0,0 +1,185 @@ +# Adjudication: m4-pol-v2-data-centre-p1 + +| Field | Value | +|---|---| +| Run id | `m4-pol-v2-data-centre-p1` | +| Run kind | Interactive entry — fixed three-submission probe | +| Ruler | `mission-4-activation-and-restraint-ruler-v2.md` | +| Requested adjudicator model | `anthropic/claude-opus-4-6` | +| Reported adjudicator model | `anthropic/claude-opus-4-6` | +| Thinking mode | High | +| Fresh context | Yes — adjudicator has not seen the situation pack, case oracle, or any other run | + +--- + +## 1 Ordered mechanism trace verification + +The derived trace (`trace.json`) was verified event-by-event against the raw snapshot (`snapshot.json`). Every event maps to exactly one part in the canonical message sequence; no event is missing, duplicated, or reordered. Reasoning parts and persona summaries were excluded from evidence per the ruler. + +| Seq | Event | Turn | Source part | +|-----|-------|------|------------| +| 1 | `user(1)` | 1 | `entry_direct_c3ViXzAxTTFLOFc2N0c2Q0RYNEg0OUFOWTQ2TThU`, text part | +| 2 | `activate(sdcpn-modelling, ok)` | 1 | `entry_01M1K8W9BCKBFEZ3B4MM1WBGC3`, dynamic-tool `activate_skill`, input `{"name":"sdcpn-modelling"}`, state `output-available` | +| 3 | `activate(elicitation, ok)` | 1 | same message, dynamic-tool `activate_skill`, input `{"name":"elicitation"}`, state `output-available` | +| 4 | `read(sdcpn-modelling/references/profile.md, ok)` | 1 | same message, dynamic-tool `read_skill_resource`, path `/.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md`, state `output-available` | +| 5 | `text(1, hasWorkpiece=false)` | 1 | same message, text part (no `runbook-ir` fenced block) | +| 6 | `user(2)` | 2 | `entry_direct_c3ViXzAxTTFLOFdXQzJHS1JGRkZWRkNaQlpCVEtC`, text part | +| 7 | `text(2, hasWorkpiece=false)` | 2 | `entry_01M1K8WY9FSB00EDZZ0DR8MPYP`, text part (no `runbook-ir` fenced block) | +| 8 | `user(3)` | 3 | `entry_direct_c3ViXzAxTTFLOFhERkY4Tlc0MTJGTkRQS1pOSE5K`, text part | +| 9 | `text(3, hasWorkpiece=false)` | 3 | `entry_01M1K8XGQ7RZNM7SXERZSKYVV8`, text part (no `runbook-ir` fenced block) | + +All three submissions settled with outcome `completed`. No client-tool suspensions, no unresolved dispatches. Trace is **verified and consistent**. + +--- + +## 2 Turn classification + +### Turn 1, text at sequence 5 + +> "Hi Asha — that's a high-stakes situation to be modelling under, and exactly the kind of thing a thermal-process model should earn its keep on. Let me make sure I understand what you need the model to *answer* before we go further. +> +> You mentioned three things: **testing thermal margins, maintenance windows, and redundancy choices.** Of those, which is the decision you most need to get right in the next few weeks — for example, is there a specific question like 'can we safely bring unit X down for a planned inspection while the other is still recovering?' or 'what load ceiling must we hold to keep temperatures within bounds with N−1 cooling?' or something else? +> +> Starting from your most pressing decision will let me focus the model on what actually matters rather than trying to cover everything at once." + +**Classification: Orientation.** The text asks which of three stated purposes is most pressing and what specific decision the model must answer. It confirms intended decision, boundary, and priority. It asks for no operational fact about how the domain works. + +**Dosage: Deepening.** One answerable thread: which decision matters most. The illustrative examples are contrastive aids to a single question, not independent topics. + +--- + +### Turn 2, text at sequence 7 + +> "That's a sharp, concrete question — I can work with it. Two things share the same frame and both drive the time estimate directly: +> +> **What rate is C7 rising at right now** — do you have a recent slope, even roughly (e.g., the change over the last 5 or 10 minutes)? And **what cooling is actually running at this moment** — if CH-2 tripped and CH-4 is open for maintenance, is anything else carrying the load, or are you effectively on zero mechanical cooling right now? +> +> Those two together set the thermal trajectory before we look at the interventions." + +**Classification: Substantive.** The text asks the person to supply operational knowledge: the observed temperature rise rate and what cooling equipment is currently running. Both are operational facts about how the site works right now. + +**Dosage: Grouped in one frame.** Two questions — the C7 rise rate and the current cooling state — both concern one situation the person can hold in mind at once: the present thermal trajectory. The elicitor explicitly frames them as sharing one frame ("Two things share the same frame and both drive the time estimate directly"). The persona answered both together in one coherent response. + +--- + +### Turn 3, text at sequence 9 + +> "That chilled-water supply climb from 7.1 to 9.3°C is important — it tells me CH-1 and CH-3 are already saturated, so the CRAHs are getting progressively warmer supply water. That's a reason to expect C7's slope to steepen rather than hold steady, which is why your hedge on 'not a fixed slope' is well-placed. +> +> Before I can anchor either intervention, I need to understand what pausing Aurora actually does. When you say 'pause Aurora' — roughly how much of that 11.4 MW does it shed, and how quickly does the load actually come off after the decision is made? Is it a clean instantaneous drop, or does it ramp down over minutes?" + +**Classification: Substantive.** The text asks the person to supply operational knowledge about the Aurora pause intervention: the load it sheds and the speed of load reduction. These are operational facts about how the site's load-shedding works. + +**Dosage: Grouped in one frame.** The shed magnitude and shed speed both concern one object — the "pause Aurora" intervention — and one can answer them together. The elicitor frames them as a single investigation: "I need to understand what pausing Aurora actually does." + +--- + +### Classification summary + +| Turn | Kind | Dosage | Deciding quote (abbreviated) | +|------|------|--------|------------------------------| +| 1 | Orientation | Deepening | "which is the decision you most need to get right in the next few weeks" | +| 2 | **Substantive** | Grouped in one frame | "What rate is C7 rising at right now … what cooling is actually running at this moment" | +| 3 | Substantive | Grouped in one frame | "roughly how much of that 11.4 MW does it shed, and how quickly does the load actually come off" | + +**T_sub = 2** (the turn index of the first Substantive text). + +--- + +## 3 Item 4a — Activation before substance + +| Event | Canonical position | Turn | +|-------|--------------------|------| +| `activate(sdcpn-modelling, ok)` | sequence 2 | 1 | +| `activate(elicitation, ok)` | sequence 3 | 1 | +| First Substantive text | sequence 7 | 2 | + +Both activations are present. `sdcpn-modelling` (seq 2) precedes `elicitation` (seq 3); both precede the first Substantive text (seq 7). The Orientation text at sequence 5 precedes both the first Substantive text and follows both activations; orientation before activation does not fail this check per the owner's acceptance. + +**4a: PASS.** + +--- + +## 4 Item 5a — Conditional profile read before reliance + +| Event | Canonical position | Turn | +|-------|--------------------|------| +| `read(sdcpn-modelling/references/profile.md, ok)` | sequence 4 | 1 | +| First Substantive text | sequence 7 | 2 | + +The profile read (seq 4) precedes the first Substantive text (seq 7). + +**5a: PASS.** + +--- + +## 5 Item 5b — Template timing + +No `text(*, hasWorkpiece=true)` event exists in this run. All three assistant text events have `hasWorkpiece: false`. No `read(sdcpn-modelling/templates/workpiece.md, *)` event exists. Since no workpiece was emitted, the E anchor does not exist, and 5b has no applicable finding. This is expected for a three-submission probe that terminates during early elicitation. + +**5b: No workpiece emitted; no finding.** + +--- + +## 6 Item 5c — Resource restraint + +No `read(sdcpn-modelling/references/pn-construction.md, *)` or `read(sdcpn-modelling/references/checks.md, *)` events appear anywhere in the trace. The person did not request construction, and no construction resources were read during interviewing. No resources belonging to a never-activated skill were read. No repeated `activate_skill` calls observed. + +**5c: No finding. PASS.** + +--- + +## 7 Item 5d — Dosage + +### Opening (first Substantive text, turn 2) + +The first Substantive text is classified **Grouped in one frame**, not a Battery. Two questions share one frame (the current thermal trajectory) and were naturally answered together. + +**5d opening: PASS — not a Battery.** + +### Full-run dosage summary + +| Texts classified | Orientation | Substantive | Total O+S | Battery count | +|------------------|-------------|-------------|-----------|---------------| +| 3 | 1 | 2 | 3 | 0 | + +Battery texts: **0 / 3** Orientation + Substantive texts. No later-turn dosage findings. (This is a three-submission probe; per the ruler, later dosage does not determine proof-of-life acceptance regardless.) + +--- + +## 8 Run validity + +From `validity.json`, all mechanical checks pass: + +| Check | Result | +|-------|--------| +| settledOutcome | `completed` | +| runtimeOrTransportError | `false` | +| unresolvedClientToolSuspension | `false` | +| emptyElicitorResponse | `false` | +| personaRefusalSignal | `false` | +| openingMessageMatched | `true` | +| visibleUserSubmissions | 3 | +| fixedProbeBudgetSatisfied | `true` | + +No invalidity condition from the ruler is triggered. The run is **technically valid**. + +--- + +## 9 Floor-satisfying member determination + +This run is a fixed three-submission probe over a distinct case family (data centre thermal operations). It is technically valid, 4a-gradable (T_sub exists at turn 2), and passes both required items: + +- **4a**: PASS (both activations precede first Substantive text in correct order) +- **5a**: PASS (profile read precedes first Substantive text) +- **5d opening**: PASS (first Substantive text is not a Battery) +- **5b**: No finding +- **5c**: No finding + +**This run is a valid, floor-satisfying member** of the candidate interactive floor, conditional on the remaining two runs across distinct case families independently satisfying their own requirements per item 4b. + +--- + +## 10 Persona corroboration notes + +No persona replies in this run exhibited skip signals, relevance challenges, or "already answered" markers. Recorded for completeness; persona output is not the oracle and was not used as evidence for any classification above. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/manifest.json new file mode 100644 index 00000000000..991b707b8f6 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "bb89f382be995f4f84e88ef36071c5f5abd1fd371e3dc340f49ebbc1a1aaa25c" + }, + { + "path": "run.json", + "sha256": "8951fb28440885448a87b74c17081aae3400e69892ff835c794ff149f207fc5f" + }, + { + "path": "snapshot.json", + "sha256": "140b6f3366c18866739f2699e7150bd7b0fc40ae2bbc710b39036bf32a7d9062" + }, + { + "path": "trace.json", + "sha256": "f2706b5fe669e6ed69369e85e072cc37a314a26a4c33c22a7cdc14c8f08b1a53" + }, + { + "path": "trace.md", + "sha256": "fc33a2105f781f29c9de6eab88101754afc6a38f013470d3003f5a17bca11a91" + }, + { + "path": "transcript.md", + "sha256": "b961b9c326b0c9b10bfcc885499b7ff972bf89b651e0558d8dd929bd9540c766" + }, + { + "path": "usage.json", + "sha256": "45dd2c8d47c6a5bb30fd305158bd5f49616eb2be2a992441cad60de8b49f9d92" + }, + { + "path": "validity.json", + "sha256": "ef04ea7473b422534208fbeb200a0a9cce816bbb5e2a6cd8299fd50deb6953bc" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/run.json new file mode 100644 index 00000000000..39683b59a7f --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/run.json @@ -0,0 +1,30 @@ +{ + "version": 2, + "campaign": "mission-4-proof-of-life-v2", + "attemptId": "m4-pol-v2-data-centre-p1", + "slot": "data-centre-probe", + "runKind": "interactive-probe", + "case": "data-centre-thermal-operations", + "instrumentCommit": "95954b494308fbba384cc4ce169a813916f164f9", + "manifestCommit": "d9ca2fe1498f6484746b2abaaf18973e7abcbeaa", + "executionHead": "4be37e26a232798354d6e2314d0d3e1418aac959", + "models": { + "elicitor": { "provider": "anthropic", "requested": "claude-sonnet-4-6" }, + "persona": { + "provider": "openai", + "requested": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "visibleUserSubmissions": 3, + "normalStop": "after the third submission settles" + }, + "startedAt": "2026-09-03T09:15:39.743913+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/snapshot.json new file mode 100644 index 00000000000..8686dad2d51 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/snapshot.json @@ -0,0 +1,165 @@ +{ + "v": 1, + "conversationId": "conv_01M1K8W67HT46QJ1Z63A05SRTM", + "offset": "0000000000000000_0000000000000065", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLOFc2N0c2Q0RYNEg0OUFOWTQ2TThU", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8W67G6CDX4H49ANY46M8T", + "parts": [ + { + "type": "text", + "text": "I'm Asha Mercer, the facilities and capacity engineer at Northbank Quay DC-2. We're at peak AI\nload with one chiller in maintenance and another tripped, and I need a reliable way to test our\nthermal margins, maintenance windows, and redundancy choices. Please interview me about how the\nsite operates and help me build that what-if picture.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8W9BCKBFEZ3B4MM1WBGC3", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8W67G6CDX4H49ANY46M8T", + "turnId": "turn_01M1K8W67KDSRWAFFD2FX685PB", + "parts": [ + { + "type": "reasoning", + "text": "The user wants to build a process model of their data center cooling operations. I need to activate the sdcpn-modelling skill before substantive interviewing.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_018NdyUoWxudEMN4ofLNwwdz", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_015AmUzAnsoPtGP48etkcquo", + "state": "output-available", + "input": { + "name": "elicitation" + }, + "output": "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_015DxDVY2btPM5ue6aoipBmk", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Now I have the skill instructions and profile loaded. Let me start the interview with Asha. I should ask an opening question that follows her thread - she's dealing with a data center cooling crisis and wants to test thermal margins, maintenance windows, and redundancy choices. I should follow concrete operational vocabulary, not target formalism.\n\nI need to pick one good opening question that follows her thread. She mentioned:\n- Peak AI load\n- One chiller in maintenance, another tripped\n- Wants to test thermal margins, maintenance windows, redundancy choices\n\nA good opening question would be to understand what they're actually trying to decide or answer - what specific decisions/comparisons does this model need to support? Or I could follow a concrete case - like what happened when the second chiller tripped.\nActually, I think the better approach is to first ground her purpose more precisely, then follow up with a concrete case - keeping to one focused opening question as the elicitation skill suggests.", + "state": "done" + }, + { + "type": "text", + "text": "Hi Asha — that's a high-stakes situation to be modelling under, and exactly the kind of thing a thermal-process model should earn its keep on. Let me make sure I understand what you need the model to *answer* before we go further.\n\nYou mentioned three things: **testing thermal margins, maintenance windows, and redundancy choices.** Of those, which is the decision you most need to get right in the next few weeks — for example, is there a specific question like \"can we safely bring unit X down for a planned inspection while the other is still recovering?\" or \"what load ceiling must we hold to keep temperatures within bounds with N−1 cooling?\" or something else?\n\nStarting from your most pressing decision will let me focus the model on what actually matters rather than trying to cover everything at once.", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFLOFdXQzJHS1JGRkZWRkNaQlpCVEtC", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8WWC2GKRFFFVFCZBZBTKB", + "parts": [ + { + "type": "text", + "text": "The immediate decision is whether we pause Aurora now or wait for the CH-2 inspection. At 14:06 we're at 11.4 MW IT load, with CH-4 open for maintenance and CH-2 tripped; C7 is already at 27.8°C and rising. I need a credible time to the first 30°C crossing, not false precision, and a comparison of load shed against getting either chiller back.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8WY9FSB00EDZZ0DR8MPYP", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8WWC2GKRFFFVFCZBZBTKB", + "turnId": "turn_01M1K8WWC5AGAZECX7Y2QJBP2G", + "parts": [ + { + "type": "reasoning", + "text": "This is a very concrete and immediate operational question. Asha needs:\n1. Time to first 30°C crossing at C7 given current conditions (11.4 MW IT load, CH-4 in maintenance, CH-2 tripped, C7 at 27.8°C and rising)\n2. Comparison of two interventions: load shed (pause Aurora) vs. getting either chiller back online\n\nLet me follow her thread and deepen one concrete thread at a time. I need to understand:\n- The cooling architecture (how many chillers total, their capacity)\n- C7 - what is it? A temperature sensor location?\nI also need to know what's currently providing cooling if both chillers are down, the rate of rise at C7, what \"pause Aurora\" would actually reduce in load terms, and realistic recovery timelines for CH-2 versus CH-4. The rate of rise seems like the single most load-bearing fact for estimating time to crossing, so I should press on that first, while also confirming whether any cooling capacity remains online right now.", + "state": "done" + }, + { + "type": "text", + "text": "That's a sharp, concrete question — I can work with it. Two things share the same frame and both drive the time estimate directly:\n\n**What rate is C7 rising at right now** — do you have a recent slope, even roughly (e.g., the change over the last 5 or 10 minutes)? And **what cooling is actually running at this moment** — if CH-2 tripped and CH-4 is open for maintenance, is anything else carrying the load, or are you effectively on zero mechanical cooling right now?\n\nThose two together set the thermal trajectory before we look at the interventions.", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFLOFhERkY4Tlc0MTJGTkRQS1pOSE5K", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8XDFF8NW412FNDPKZNHNJ", + "parts": [ + { + "type": "text", + "text": "C7's recent rise is about 0.08 to 0.14°C per minute, though I wouldn't treat that as a fixed slope. We are not on zero cooling: CH-1 and CH-3 are running at 97–99%, standby pump P-5 has started, and all eight Hall 3 CRAHs are enabled. Chilled-water supply has still climbed from 7.1°C to 9.3°C, so the two remaining chillers are not holding the present load.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8XGQ7RZNM7SXERZSKYVV8", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8XDFF8NW412FNDPKZNHNJ", + "turnId": "turn_01M1K8XDFH3KTYGJRSJAJRG6VN", + "parts": [ + { + "type": "reasoning", + "text": "This is really helpful. Let me understand what I have:\n\n- C7 rising at 0.08–0.14°C/min (not a fixed slope - she's appropriately hedging)\n- CH-1 and CH-3 running at 97-99% (so near capacity)\n- Standby pump P-5 has started\n- All 8 Hall 3 CRAHs enabled\n- Chilled water supply temp has climbed from 7.1°C to 9.3°C → chillers not holding the load\n\nThe chilled water supply temperature rising from 7.1 to 9.3°C is important - it shows the chillers are losing ground. \n\nC7 is likely a return air or room sensor, so I need to weigh three possible interventions: pausing Aurora to cut load, recovering the tripped CH-2, or pulling CH-4 back from maintenance - and figure out the timing and feasibility of each before deciding whether the rising supply temperature signals C7 is about to accelerate.\n\nthere's also the question of whether the C7 slope will accelerate given the rising chilled water temperature - she already hedged that it's not a fixed slope, so I should probe that. But for the immediate pause-vs-wait decision, the more pressing thread is understanding what Aurora actually sheds and how fast, since that's the direct comparison point against waiting for CH-2 inspection.", + "state": "done" + }, + { + "type": "text", + "text": "That chilled-water supply climb from 7.1 to 9.3°C is important — it tells me CH-1 and CH-3 are already saturated, so the CRAHs are getting progressively warmer supply water. That's a reason to expect C7's slope to steepen rather than hold steady, which is why your hedge on \"not a fixed slope\" is well-placed.\n\nBefore I can anchor either intervention, I need to understand what pausing Aurora actually does. When you say \"pause Aurora\" — roughly how much of that 11.4 MW does it shed, and how quickly does the load actually come off after the decision is made? Is it a clean instantaneous drop, or does it ramp down over minutes?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K8W67G6CDX4H49ANY46M8T", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8W67G6CDX4H49ANY46M8T" + }, + { + "submissionId": "sub_01M1K8WWC2GKRFFFVFCZBZBTKB", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8WWC2GKRFFFVFCZBZBTKB" + }, + { + "submissionId": "sub_01M1K8XDFF8NW412FNDPKZNHNJ", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8XDFF8NW412FNDPKZNHNJ" + } + ], + "incarnation": "inc_01M1K8W67GCN5THTW7GREQKTK1" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.json new file mode 100644 index 00000000000..ef48c3fbe95 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.json @@ -0,0 +1,77 @@ +{ + "conversationId": "conv_01M1K8W67HT46QJ1Z63A05SRTM", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLOFc2N0c2Q0RYNEg0OUFOWTQ2TThU", + "text": "I'm Asha Mercer, the facilities and capacity engineer at Northbank Quay DC-2. We're at peak AI\nload with one chiller in maintenance and another tripped, and I need a reliable way to test our\nthermal margins, maintenance windows, and redundancy choices. Please interview me about how the\nsite operates and help me build that what-if picture.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K8W9BCKBFEZ3B4MM1WBGC3", + "toolCallId": "toolu_018NdyUoWxudEMN4ofLNwwdz", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K8W9BCKBFEZ3B4MM1WBGC3", + "toolCallId": "toolu_015AmUzAnsoPtGP48etkcquo", + "name": "elicitation", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K8W9BCKBFEZ3B4MM1WBGC3", + "toolCallId": "toolu_015DxDVY2btPM5ue6aoipBmk", + "path": "sdcpn-modelling/references/profile.md", + "outcome": "ok", + "sequence": 4 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K8W9BCKBFEZ3B4MM1WBGC3", + "text": "Hi Asha — that's a high-stakes situation to be modelling under, and exactly the kind of thing a thermal-process model should earn its keep on. Let me make sure I understand what you need the model to *answer* before we go further.\n\nYou mentioned three things: **testing thermal margins, maintenance windows, and redundancy choices.** Of those, which is the decision you most need to get right in the next few weeks — for example, is there a specific question like \"can we safely bring unit X down for a planned inspection while the other is still recovering?\" or \"what load ceiling must we hold to keep temperatures within bounds with N−1 cooling?\" or something else?\n\nStarting from your most pressing decision will let me focus the model on what actually matters rather than trying to cover everything at once.", + "hasWorkpiece": false, + "sequence": 5 + }, + { + "type": "user", + "turn": 2, + "messageId": "entry_direct_c3ViXzAxTTFLOFdXQzJHS1JGRkZWRkNaQlpCVEtC", + "text": "The immediate decision is whether we pause Aurora now or wait for the CH-2 inspection. At 14:06 we're at 11.4 MW IT load, with CH-4 open for maintenance and CH-2 tripped; C7 is already at 27.8°C and rising. I need a credible time to the first 30°C crossing, not false precision, and a comparison of load shed against getting either chiller back.", + "sequence": 6 + }, + { + "type": "text", + "turn": 2, + "messageId": "entry_01M1K8WY9FSB00EDZZ0DR8MPYP", + "text": "That's a sharp, concrete question — I can work with it. Two things share the same frame and both drive the time estimate directly:\n\n**What rate is C7 rising at right now** — do you have a recent slope, even roughly (e.g., the change over the last 5 or 10 minutes)? And **what cooling is actually running at this moment** — if CH-2 tripped and CH-4 is open for maintenance, is anything else carrying the load, or are you effectively on zero mechanical cooling right now?\n\nThose two together set the thermal trajectory before we look at the interventions.", + "hasWorkpiece": false, + "sequence": 7 + }, + { + "type": "user", + "turn": 3, + "messageId": "entry_direct_c3ViXzAxTTFLOFhERkY4Tlc0MTJGTkRQS1pOSE5K", + "text": "C7's recent rise is about 0.08 to 0.14°C per minute, though I wouldn't treat that as a fixed slope. We are not on zero cooling: CH-1 and CH-3 are running at 97–99%, standby pump P-5 has started, and all eight Hall 3 CRAHs are enabled. Chilled-water supply has still climbed from 7.1°C to 9.3°C, so the two remaining chillers are not holding the present load.", + "sequence": 8 + }, + { + "type": "text", + "turn": 3, + "messageId": "entry_01M1K8XGQ7RZNM7SXERZSKYVV8", + "text": "That chilled-water supply climb from 7.1 to 9.3°C is important — it tells me CH-1 and CH-3 are already saturated, so the CRAHs are getting progressively warmer supply water. That's a reason to expect C7's slope to steepen rather than hold steady, which is why your hedge on \"not a fixed slope\" is well-placed.\n\nBefore I can anchor either intervention, I need to understand what pausing Aurora actually does. When you say \"pause Aurora\" — roughly how much of that 11.4 MW does it shed, and how quickly does the load actually come off after the decision is made? Is it a clean instantaneous drop, or does it ramp down over minutes?", + "hasWorkpiece": false, + "sequence": 9 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.md new file mode 100644 index 00000000000..06a58a3b0c1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/trace.md @@ -0,0 +1,13 @@ +# Canonical proof trace + +Conversation: `conv_01M1K8W67HT46QJ1Z63A05SRTM` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLOFc2N0c2Q0RYNEg0OUFOWTQ2TThU` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_018NdyUoWxudEMN4ofLNwwdz` +3. turn 1: `activate(elicitation, ok)` — call `toolu_015AmUzAnsoPtGP48etkcquo` +4. turn 1: `read(sdcpn-modelling/references/profile.md, ok)` — call `toolu_015DxDVY2btPM5ue6aoipBmk` +5. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K8W9BCKBFEZ3B4MM1WBGC3` +6. turn 2: `user` — message `entry_direct_c3ViXzAxTTFLOFdXQzJHS1JGRkZWRkNaQlpCVEtC` +7. turn 2: `text(hasWorkpiece=false)` — message `entry_01M1K8WY9FSB00EDZZ0DR8MPYP` +8. turn 3: `user` — message `entry_direct_c3ViXzAxTTFLOFhERkY4Tlc0MTJGTkRQS1pOSE5K` +9. turn 3: `text(hasWorkpiece=false)` — message `entry_01M1K8XGQ7RZNM7SXERZSKYVV8` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/transcript.md new file mode 100644 index 00000000000..628864720c1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/transcript.md @@ -0,0 +1,33 @@ +## User +I'm Asha Mercer, the facilities and capacity engineer at Northbank Quay DC-2. We're at peak AI +load with one chiller in maintenance and another tripped, and I need a reliable way to test our +thermal margins, maintenance windows, and redundancy choices. Please interview me about how the +site operates and help me build that what-if picture. + +## Assistant +Hi Asha — that's a high-stakes situation to be modelling under, and exactly the kind of thing a thermal-process model should earn its keep on. Let me make sure I understand what you need the model to *answer* before we go further. + +You mentioned three things: **testing thermal margins, maintenance windows, and redundancy choices.** Of those, which is the decision you most need to get right in the next few weeks — for example, is there a specific question like "can we safely bring unit X down for a planned inspection while the other is still recovering?" or "what load ceiling must we hold to keep temperatures within bounds with N−1 cooling?" or something else? + +Starting from your most pressing decision will let me focus the model on what actually matters rather than trying to cover everything at once. +- tool activate_skill (toolu_018NdyUoWxudEMN4ofLNwwdz): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool activate_skill (toolu_015AmUzAnsoPtGP48etkcquo): "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>" +- tool read_skill_resource (toolu_015DxDVY2btPM5ue6aoipBmk): "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n" + +## User +The immediate decision is whether we pause Aurora now or wait for the CH-2 inspection. At 14:06 we're at 11.4 MW IT load, with CH-4 open for maintenance and CH-2 tripped; C7 is already at 27.8°C and rising. I need a credible time to the first 30°C crossing, not false precision, and a comparison of load shed against getting either chiller back. + +## Assistant +That's a sharp, concrete question — I can work with it. Two things share the same frame and both drive the time estimate directly: + +**What rate is C7 rising at right now** — do you have a recent slope, even roughly (e.g., the change over the last 5 or 10 minutes)? And **what cooling is actually running at this moment** — if CH-2 tripped and CH-4 is open for maintenance, is anything else carrying the load, or are you effectively on zero mechanical cooling right now? + +Those two together set the thermal trajectory before we look at the interventions. + +## User +C7's recent rise is about 0.08 to 0.14°C per minute, though I wouldn't treat that as a fixed slope. We are not on zero cooling: CH-1 and CH-3 are running at 97–99%, standby pump P-5 has started, and all eight Hall 3 CRAHs are enabled. Chilled-water supply has still climbed from 7.1°C to 9.3°C, so the two remaining chillers are not holding the present load. + +## Assistant +That chilled-water supply climb from 7.1 to 9.3°C is important — it tells me CH-1 and CH-3 are already saturated, so the CRAHs are getting progressively warmer supply water. That's a reason to expect C7's slope to steepen rather than hold steady, which is why your hedge on "not a fixed slope" is well-placed. + +Before I can anchor either intervention, I need to understand what pausing Aurora actually does. When you say "pause Aurora" — roughly how much of that 11.4 MW does it shed, and how quickly does the load actually come off after the decision is made? Is it a clean instantaneous drop, or does it ramp down over minutes? diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/usage.json new file mode 100644 index 00000000000..c7b1235c0e2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/usage.json @@ -0,0 +1,24 @@ +{ + "currency": "USD", + "persona": { + "requestedModel": "openai/gpt-5.6-sol", + "reportedModel": "openai/gpt-5.6-sol", + "thinking": "medium", + "cost": 0.047, + "source": "Pi session usage display; rounded" + }, + "elicitor": { + "requestedModel": "anthropic/claude-sonnet-4-6", + "reportedModel": null, + "cost": null, + "source": "Canonical Flue history does not expose provider model or usage" + }, + "adjudicator": { + "requestedModel": "anthropic/claude-opus-4-6", + "reportedModel": "anthropic/claude-opus-4-6", + "thinking": "high", + "cost": 0.406, + "source": "Pi session usage display; rounded" + }, + "knownRoundedTotal": 0.453 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/validity.json new file mode 100644 index 00000000000..d96eb7955f6 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-data-centre-p1/validity.json @@ -0,0 +1,25 @@ +{ + "status": "valid-floor-satisfying", + "technicallyValid": true, + "mechanicalChecks": { + "settledOutcome": "completed", + "runtimeOrTransportError": false, + "unresolvedClientToolSuspension": false, + "emptyElicitorResponse": false, + "personaRefusalSignal": false, + "openingMessageMatched": true, + "visibleUserSubmissions": 3, + "fixedProbeBudgetSatisfied": true + }, + "personaStopReason": "Stopped after exactly three visible user submissions; all three settled successfully.", + "semanticClassification": { + "firstSubstantiveTurn": 2, + "item4a": "pass", + "item5a": "pass", + "item5b": "no-finding", + "item5c": "no-findings", + "item5dOpening": "pass" + }, + "qualifiesForFloor": true, + "campaignConsequence": "Data Centre probe slot passes; proceed serially to S3 review." +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/adjudication.md new file mode 100644 index 00000000000..1ac85493191 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/adjudication.md @@ -0,0 +1,67 @@ +# Adjudication — m4-pol-v2-s3-p1 + +| Field | Value | +| --- | --- | +| Run id | m4-pol-v2-s3-p1 | +| Run kind | Review entry (S3 prompt, item 4d) | +| Ruler | mission-4-activation-and-restraint-ruler-v2.md | +| Requested adjudicator model | anthropic/claude-opus-4-6 | +| Reported adjudicator model | anthropic/claude-opus-4-6 | +| Thinking | high | +| Adjudication scope | Item 4d only | + +## 1. Trace–snapshot consistency + +The canonical trace contains four events. Each was verified against the raw snapshot (`snapshot.json`), walking visible message parts in canonical order: + +| Sequence | Trace event | Snapshot part | Match | +| --- | --- | --- | --- | +| 1 | `user(1)` | `entry_direct_…`, role `user`, one text part with the S3 prompt | ✓ | +| 2 | `activate(sdcpn-modelling, ok)` | `entry_01M1K96F…`, `dynamic-tool`, toolName `activate_skill`, input `{"name":"sdcpn-modelling"}`, state `output-available` | ✓ | +| 3 | `read(sdcpn-modelling/references/checks.md, ok)` | same message, `dynamic-tool`, toolName `read_skill_resource`, path `…/references/checks.md`, state `output-available` | ✓ | +| 4 | `text(1, hasWorkpiece=false)` | same message, type `text`, no `` ```runbook-ir `` block | ✓ | + +Two `reasoning` parts appear in the snapshot between sequences 1–2 and 3–4; these are non-visible internal reasoning and are correctly excluded from the trace per ruler derivation rules (only `dynamic-tool`, `text`, and `user` parts emit events). The trace faithfully represents the snapshot. + +## 2. First supported defect and evidence in the response + +The response's first (and only) identified defect, quoted: + +> **First defect: permanent token loss on the rejection path — a structural non-conservation violation.** + +Evidence quoted by the response: + +> The defect is not an inference; it is directly stated in the target description itself: *"RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token."* The operational account supplies the contradicting requirement: the reviewer *becomes available again* on rejection. + +The response identifies a formalism-specific structural defect (token non-conservation on the rejection path) and cites the target description's own text as evidence, cross-referenced against the operational account's reusability requirement. It does not interview or mutate a net, consistent with the prompt's constraints. + +## 3. Elicitation activation check + +The complete trace contains exactly one activation event: + +- Sequence 2: `activate(sdcpn-modelling, ok)` + +No `activate(elicitation, *)` event appears anywhere in the run — not before the response, not within it, not in any turn. The `elicitation` skill was never activated. + +## 4. Technical validity + +Per `validity.json`: + +- `technicallyValid`: **true** +- Settled outcome: `completed` +- Runtime or transport error: none +- Unresolved client-tool suspension: none +- Empty elicitor response: none +- Opening prompt SHA-256 matched: yes +- Visible user submissions: 1 + +No invalidity condition from the ruler's "Run validity" section is triggered. The run is **technically valid**. + +## 5. Item 4d determination + +**Ruler text (4d):** "Pass when the response that performs or identifies the requested revision contains no prior `activate(elicitation, *)` anywhere in the run. Fail otherwise, even when the revision itself is correct." + +- The response identifies the requested defect (token loss on the rejection path) with cited evidence. ✓ +- No `activate(elicitation, *)` appears anywhere in the run. ✓ + +**Item 4d: PASS** diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/manifest.json new file mode 100644 index 00000000000..c104228c6c1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "f470e922b8f4bff6736db6fe8b48729ff478dc601fff799b4026f6307af6df59" + }, + { + "path": "run.json", + "sha256": "0d58c03ad32d4b15cbe4ea92cd1b4e2c827ca4b4b2ac18fa928ea101d8a024bf" + }, + { + "path": "snapshot.json", + "sha256": "af5a6e11d9ed26461195ff0f5b3a94c64a7a750db3bdbda380bb96701d789d7a" + }, + { + "path": "trace.json", + "sha256": "711b650e48cd205fa7b5aef66ed22348dd975df77ac2de760536abf8f3fcaaf0" + }, + { + "path": "trace.md", + "sha256": "83de7a85cbef1dcbd8ce9b9052ccdbefaa3939507c5c5b5e66eb7698705d6bea" + }, + { + "path": "transcript.md", + "sha256": "4970caee358e9f2c4300f9e52f2adbf42744cf62deabc0f1272ddd59f1c3117a" + }, + { + "path": "usage.json", + "sha256": "55660cbd24710a2aeec8f81203e3444e374c0362921121638049475fc623005c" + }, + { + "path": "validity.json", + "sha256": "5d6e42cd6df2a6ebb332209de857211ee8dd50c0238d264fd3320f3d715f7480" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/run.json new file mode 100644 index 00000000000..253c73de7d3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/run.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "campaign": "mission-4-proof-of-life-v2", + "attemptId": "m4-pol-v2-s3-p1", + "slot": "s3-review", + "runKind": "review-entry", + "case": "flue-skill-composition-side-quest-v3/S3", + "instrumentCommit": "95954b494308fbba384cc4ce169a813916f164f9", + "manifestCommit": "d9ca2fe1498f6484746b2abaaf18973e7abcbeaa", + "executionHead": "2430c3fc0f93cbc2c07f24f4e4f4f0b269109c83", + "promptSha256": "ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729", + "models": { + "elicitor": { "provider": "anthropic", "requested": "claude-sonnet-4-6" }, + "persona": null, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "visibleUserSubmissions": 1, + "normalStop": "through settlement" + }, + "startedAt": "2026-09-03T09:21:20.667514+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/snapshot.json new file mode 100644 index 00000000000..b449e103c3a --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/snapshot.json @@ -0,0 +1,76 @@ +{ + "v": 1, + "conversationId": "conv_01M1K96DK32ECRPJ17GCC82JJH", + "offset": "0000000000000000_0000000000000038", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLOTZESzFTWVlaMlQxOU1EWFk4RTM1", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K96DK1SYYZ2T19MDXY8E35", + "parts": [ + { + "type": "text", + "text": "Review the described target against the supplied operational account. Stop after identifying the first supported formalism-specific defect and its evidence. Do not interview me or mutate a net.\n\nOperational account: each request check reserves one reusable reviewer at start. The reviewer is unavailable during the check and becomes available again when either approval or rejection is recorded.\nTarget description: AvailableReviewer supplies one reviewer token to StartCheck. CheckInProgress supplies the held reviewer token to RecordApproval. RecordApproval returns that token to AvailableReviewer. RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K96FDMVY285CGRB67MGCCK", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K96DK1SYYZ2T19MDXY8E35", + "turnId": "turn_01M1K96DK6XX569Q669A2X7ZDF", + "parts": [ + { + "type": "reasoning", + "text": "The user wants me to review a described target (a Petri net description) against an operational account, identify the first supported formalism-specific defect, and stop. No interviewing, no net mutation.\n\nLet me activate the sdcpn-modelling skill first, then analyze.\n\nActually, let me think about this directly. The operational account says:\n- Each request check reserves one reusable reviewer at start\n- The reviewer is unavailable during the check\n- The reviewer becomes available again when EITHER approval OR rejection is recorded\n\nThe target description says:\n- AvailableReviewer → StartCheck → CheckInProgress (reviewer token held here)\n- CheckInProgress → RecordApproval → AvailableReviewer, returning the token, but RecordRejection consumes the CheckInProgress token without any arc sending it back to AvailableReviewer.\n\nThis is the defect: on rejection the reviewer token is permanently lost since only the approval path returns it, violating the reusability invariant the operational account describes and breaking token conservation for the rejection branch. I should activate the skill before finalizing this as my answer, but the analysis itself is complete — this is the first formalism-specific defect I can point to.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_015q74sgKSuW1Lr3XuTvZybR", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 1 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_0147RwJne99u1XgjRnnC9V9w", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md" + }, + "output": "# Workpiece, Construction, and Delivery Checks\n\nRead this when preparing to construct, after construction changes, and before delivering a net. For workpiece-only delivery, apply the universal and plugin Verification registers without loading this construction resource.\n\nA failed check triggers the smallest relevant repair available in the current runtime branch: amend the workpiece, ask during interactive elicitation, revise construction, or report a visible limitation and re-entry question for a later conversation.\n\n## Evidence levels\n\nReport the highest level actually reached. Passing one level does not imply the next.\n\n### 1. Tool-schema acceptance\n\nThe mounted construction tools accepted the submitted payloads, and the latest inspected definition contains the accepted changes. This establishes conformance to those tool input schemas and the shape returned by inspection. It does not establish correspondence with the workpiece, reachability, resource conservation, exclusivity over executions, loadability in another consumer, or simulated behavior.\n\n### 2. Agent-reviewed structural correspondence\n\nThe agent compared the inspected definition with the workpiece and found visible structures corresponding to the recorded process. This can establish that named elements, connections, candidate paths, guards, resource-return structures, and parameters are present and apparently aligned. It remains a review judgment over static structure, not behavioral proof.\n\n### 3. Behavioral execution or stronger analysis\n\nAn actual simulation, state-space exploration, invariant check, or other named analysis exercised the constructed definition. State exactly which method, scenario, initial state, parameters, paths, and observations were covered. A simulation run establishes only the behavior observed in that run; a universal claim such as “resources cannot leak” requires an analysis whose scope genuinely covers every relevant execution.\n\nIf no behavioral execution or stronger analysis occurred, say so. Do not convert tool acceptance or visual inspection into behavioral validation.\n\n## Before construction\n\n- The intended question, comparison, or decision is stated in the person's terms.\n- The boundary and a meaningful concrete case are cold-readable from the workpiece.\n- The process spine says what flows, what admits it, what happens and in what order, what changes the path, where waiting comes from, and what outcome or handoff ends it.\n- Inputs that matter are distinguished as consumed, reserved/released, or read.\n- Required resource availability and release are recorded or visibly unknown.\n- Consequential quantities retain their context and supported precision.\n- Practiced and prescribed rules, corrections, conflicts, and contextual variants are not silently collapsed.\n- Construction can proceed without recovering a load-bearing fact from transcript memory.\n- Assumptions, unresolved matters, omissions, and anticipated losses are visible.\n\nIf the missing material admits materially different process structures, formulate the smallest resolving question before constructing. Ask it only during interactive elicitation; in construct-only execution, return it as a blocking re-entry question. If the person has stopped, deliver the partial workpiece instead of opening a new topic.\n\n## Tool-schema acceptance checks\n\n- Every intended construction call was accepted or its rejection remains explicitly unresolved.\n- The latest inspected definition contains each accepted place, transition, type, parameter, and connection under the identifier returned or supplied.\n- Every referenced endpoint exists in the inspected definition.\n- Arc weights or multiplicities are positive and conform to the mounted schema.\n- No later step depends on a rejected or absent change.\n\nRe-inspect after dependent stages and once at the end. Record rejected calls and repairs. Describe this result as **tool-schema accepted**, not valid, runnable, or simulated.\n\n## Agent-reviewed structural correspondence\n\nCompare the latest inspected definition with the authoritative workpiece claims.\n\n- The definition contains at least one meaningful place and transition corresponding to the process account.\n- It contains a candidate structural path from a represented initial or admitted condition toward an outcome. This does not establish that the path can fire.\n- Visible branches, joins, loops, and recovery structures correspond to the workpiece's stated ordering and conditions.\n- For each enumerated resource-holding path, the intended acquisition and return structures are present. This does not establish conservation over every execution.\n- Consumed inputs lack an unintended return structure; reserved inputs have an intended return structure; read-only information remains visibly available by the chosen representation.\n- Mutually exclusive outcomes or modes have apparently exclusive guards or structure. This does not establish that they can never overlap at runtime.\n- Direction-dependent mode changes retain distinct structural losses where the workpiece requires them.\n- Continuous dynamics have a recorded quantity, consequential threshold or effect, and workpiece support.\n- Required parameters and initial populations are represented or explicitly named as external inputs.\n- Waiting is explained by recorded surrounding conditions rather than an unsupported queue object.\n\nRecord discrepancies and the agent judgment used to resolve or preserve them. Describe a passing result as **structurally reviewed against the workpiece**.\n\n## Behavioral evidence\n\nOnly report observations produced by an actual execution or named stronger analysis.\n\n- Record the exact definition revision, scenario, initial state, parameters, duration or stopping condition, and analysis method.\n- State which process path or property was exercised.\n- For a simulation, report only observed progress, resource balances, mode states, outputs, and failures from the runs performed.\n- For state-space or invariant analysis, report the explored scope, assumptions, and any unexamined behaviors.\n- Relate each observation back to the workpiece objective it bears on.\n- Preserve failures and counterexamples; do not summarize them as a pass because another run succeeded.\n\nNo behavioral tool or result means no behavioral claim.\n\n## Fidelity and uncertainty\n\n- Every load-bearing net choice traces to an authoritative workpiece claim or a named construction inference, approximation, or default.\n- No hedge has been hardened solely to satisfy a schema.\n- No conflict has been averaged and no contextual value has been made universal without an accepted simplification.\n- Assumptions state why they were introduced, what they affect, and how they could be checked.\n- Material retained only in the workpiece is named as a target or tooling loss rather than omitted silently.\n- The delivery distinguishes accepted structure, agent review, observed behavior, and universal guarantees.\n\n## Revision checks\n\nWhen revising an existing workpiece or analyzing a requested net change:\n\n- the changed or disputed workpiece material is explicit;\n- the prior and current account are distinguishable as correction, conflict, or contextual coexistence;\n- the desired net delta follows from changed workpiece meaning;\n- unsupported update or removal operations are reported rather than imitated with competing additive structure;\n- any applied additive net changes preserve the intended existing structure at the level actually inspected;\n- assumptions and losses displaced or introduced by the revision are reported;\n- the delivery distinguishes what changed from what was only inspected and says what the model can now support that it could not support before, or vice versa.\n\n## Delivery\n\nAlways deliver the current recoverable workpiece. Deliver a net only if construction occurred through available tools and the resulting definition was inspected.\n\nState plainly:\n\n- what question or decision the result is intended to support;\n- whether the workpiece is sufficient for that purpose or partial with named gaps;\n- whether construction was not attempted, blocked, partial, or tool-schema accepted;\n- whether an agent-reviewed structural comparison occurred and what discrepancies remain;\n- whether behavior was untested, observed in named simulations, or established to the stated scope by stronger analysis;\n- what the agent inferred, approximated, defaulted, simplified, or omitted;\n- what remains unknown, unasked, declined, deferred, conflicting, or unsupported;\n- what the target formalism or current tooling could not represent;\n- what smallest next evidence would change the result.\n\nDo not collapse these levels into “validated,” “correct,” “runnable,” or “simulatable” without naming the evidence that supports that exact claim. Do not convert the delivery descriptions into a closed completion algebra.\n", + "durationMs": 1 + }, + { + "type": "reasoning", + "text": "Now I have enough to give a precise, evidence-grounded answer. The defect is clear:\n\n**First supported formalism-specific defect:** Token loss on the rejection path — a structural non-conservation defect.\n\nThe operational account establishes the reviewer is **reusable** and is returned after **either** outcome. The target description confirms that RecordRejection consumes the CheckInProgress place (which holds the reviewer token) but has **no output arc** returning that token to AvailableReviewer. Under Petri net firing semantics, a transition consumes all tokens on its input arcs and produces tokens only on its output arcs. \n\nWith no such output arc, the reviewer token is destroyed permanently on every rejection firing, violating the net's conservation with respect to that token and directly contradicting the requirement that the reviewer become available again after a rejection — the target description's own text confirms this consume-without-return behavior as the source of the leak.", + "state": "done" + }, + { + "type": "text", + "text": "**First defect: permanent token loss on the rejection path — a structural non-conservation violation.**\n\n**What the operational account establishes.** The reviewer is *reusable*. It must become available again after *either* approval *or* rejection. This means every execution path that acquires the reviewer must return it.\n\n**What the target description states.** `RecordRejection` consumes `CheckInProgress` and has *no output arc* returning the reviewer token. Under the Petri net firing rule, a transition removes tokens from every input place and deposits tokens only on its output places. Because no output arc to `AvailableReviewer` (or any other place) is described for `RecordRejection`, the reviewer token is permanently destroyed whenever a rejection fires.\n\n**Evidence trail.** The defect is not an inference; it is directly stated in the target description itself: *\"RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.\"* The operational account supplies the contradicting requirement: the reviewer *becomes available again* on rejection. No additional simulation or state-space analysis is needed to establish the structural fact — a transition with no output arc for a given token colour cannot return that token.\n\n**Consequence.** After one rejection, `AvailableReviewer` is permanently empty. No further check can ever start. The net is not conservative with respect to the reviewer token under the rejection path, directly violating the reusability invariant stated in the operational account.\n\n**Scope of this finding.** This is a static structural defect localisable to `RecordRejection`'s output arcs. The approval path's return arc is present as described and is not implicated.", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K96DK1SYYZ2T19MDXY8E35", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K96DK1SYYZ2T19MDXY8E35" + } + ], + "incarnation": "inc_01M1K96DK1EQMTTW8GA09ZS43D" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.json new file mode 100644 index 00000000000..5bc8384e58c --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.json @@ -0,0 +1,38 @@ +{ + "conversationId": "conv_01M1K96DK32ECRPJ17GCC82JJH", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLOTZESzFTWVlaMlQxOU1EWFk4RTM1", + "text": "Review the described target against the supplied operational account. Stop after identifying the first supported formalism-specific defect and its evidence. Do not interview me or mutate a net.\n\nOperational account: each request check reserves one reusable reviewer at start. The reviewer is unavailable during the check and becomes available again when either approval or rejection is recorded.\nTarget description: AvailableReviewer supplies one reviewer token to StartCheck. CheckInProgress supplies the held reviewer token to RecordApproval. RecordApproval returns that token to AvailableReviewer. RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K96FDMVY285CGRB67MGCCK", + "toolCallId": "toolu_015q74sgKSuW1Lr3XuTvZybR", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K96FDMVY285CGRB67MGCCK", + "toolCallId": "toolu_0147RwJne99u1XgjRnnC9V9w", + "path": "sdcpn-modelling/references/checks.md", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K96FDMVY285CGRB67MGCCK", + "text": "**First defect: permanent token loss on the rejection path — a structural non-conservation violation.**\n\n**What the operational account establishes.** The reviewer is *reusable*. It must become available again after *either* approval *or* rejection. This means every execution path that acquires the reviewer must return it.\n\n**What the target description states.** `RecordRejection` consumes `CheckInProgress` and has *no output arc* returning the reviewer token. Under the Petri net firing rule, a transition removes tokens from every input place and deposits tokens only on its output places. Because no output arc to `AvailableReviewer` (or any other place) is described for `RecordRejection`, the reviewer token is permanently destroyed whenever a rejection fires.\n\n**Evidence trail.** The defect is not an inference; it is directly stated in the target description itself: *\"RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.\"* The operational account supplies the contradicting requirement: the reviewer *becomes available again* on rejection. No additional simulation or state-space analysis is needed to establish the structural fact — a transition with no output arc for a given token colour cannot return that token.\n\n**Consequence.** After one rejection, `AvailableReviewer` is permanently empty. No further check can ever start. The net is not conservative with respect to the reviewer token under the rejection path, directly violating the reusability invariant stated in the operational account.\n\n**Scope of this finding.** This is a static structural defect localisable to `RecordRejection`'s output arcs. The approval path's return arc is present as described and is not implicated.", + "hasWorkpiece": false, + "sequence": 4 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.md new file mode 100644 index 00000000000..27d4086e578 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/trace.md @@ -0,0 +1,8 @@ +# Canonical proof trace + +Conversation: `conv_01M1K96DK32ECRPJ17GCC82JJH` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLOTZESzFTWVlaMlQxOU1EWFk4RTM1` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_015q74sgKSuW1Lr3XuTvZybR` +3. turn 1: `read(sdcpn-modelling/references/checks.md, ok)` — call `toolu_0147RwJne99u1XgjRnnC9V9w` +4. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K96FDMVY285CGRB67MGCCK` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/transcript.md new file mode 100644 index 00000000000..588daa8eeca --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/transcript.md @@ -0,0 +1,20 @@ +## User +Review the described target against the supplied operational account. Stop after identifying the first supported formalism-specific defect and its evidence. Do not interview me or mutate a net. + +Operational account: each request check reserves one reusable reviewer at start. The reviewer is unavailable during the check and becomes available again when either approval or rejection is recorded. +Target description: AvailableReviewer supplies one reviewer token to StartCheck. CheckInProgress supplies the held reviewer token to RecordApproval. RecordApproval returns that token to AvailableReviewer. RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token. + +## Assistant +**First defect: permanent token loss on the rejection path — a structural non-conservation violation.** + +**What the operational account establishes.** The reviewer is *reusable*. It must become available again after *either* approval *or* rejection. This means every execution path that acquires the reviewer must return it. + +**What the target description states.** `RecordRejection` consumes `CheckInProgress` and has *no output arc* returning the reviewer token. Under the Petri net firing rule, a transition removes tokens from every input place and deposits tokens only on its output places. Because no output arc to `AvailableReviewer` (or any other place) is described for `RecordRejection`, the reviewer token is permanently destroyed whenever a rejection fires. + +**Evidence trail.** The defect is not an inference; it is directly stated in the target description itself: *"RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token."* The operational account supplies the contradicting requirement: the reviewer *becomes available again* on rejection. No additional simulation or state-space analysis is needed to establish the structural fact — a transition with no output arc for a given token colour cannot return that token. + +**Consequence.** After one rejection, `AvailableReviewer` is permanently empty. No further check can ever start. The net is not conservative with respect to the reviewer token under the rejection path, directly violating the reusability invariant stated in the operational account. + +**Scope of this finding.** This is a static structural defect localisable to `RecordRejection`'s output arcs. The approval path's return arc is present as described and is not implicated. +- tool activate_skill (toolu_015q74sgKSuW1Lr3XuTvZybR): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool read_skill_resource (toolu_0147RwJne99u1XgjRnnC9V9w): "# Workpiece, Construction, and Delivery Checks\n\nRead this when preparing to construct, after construction changes, and before delivering a net. For workpiece-only delivery, apply the universal and plugin Verification registers without loading this construction resource.\n\nA failed check triggers the smallest relevant repair available in the current runtime branch: amend the workpiece, ask during interactive elicitation, revise construction, or report a visible limitation and re-entry question for a later conversation.\n\n## Evidence levels\n\nReport the highest level actually reached. Passing one level does not imply the next.\n\n### 1. Tool-schema acceptance\n\nThe mounted construction tools accepted the submitted payloads, and the latest inspected definition contains the accepted changes. This establishes conformance to those tool input schemas and the shape returned by inspection. It does not establish correspondence with the workpiece, reachability, resource conservation, exclusivity over executions, loadability in another consumer, or simulated behavior.\n\n### 2. Agent-reviewed structural correspondence\n\nThe agent compared the inspected definition with the workpiece and found visible structures corresponding to the recorded process. This can establish that named elements, connections, candidate paths, guards, resource-return structures, and parameters are present and apparently aligned. It remains a review judgment over static structure, not behavioral proof.\n\n### 3. Behavioral execution or stronger analysis\n\nAn actual simulation, state-space exploration, invariant check, or other named analysis exercised the constructed definition. State exactly which method, scenario, initial state, parameters, paths, and observations were covered. A simulation run establishes only the behavior observed in that run; a universal claim such as “resources cannot leak” requires an analysis whose scope genuinely covers every relevant execution.\n\nIf no behavioral execution or stronger analysis occurred, say so. Do not convert tool acceptance or visual inspection into behavioral validation.\n\n## Before construction\n\n- The intended question, comparison, or decision is stated in the person's terms.\n- The boundary and a meaningful concrete case are cold-readable from the workpiece.\n- The process spine says what flows, what admits it, what happens and in what order, what changes the path, where waiting comes from, and what outcome or handoff ends it.\n- Inputs that matter are distinguished as consumed, reserved/released, or read.\n- Required resource availability and release are recorded or visibly unknown.\n- Consequential quantities retain their context and supported precision.\n- Practiced and prescribed rules, corrections, conflicts, and contextual variants are not silently collapsed.\n- Construction can proceed without recovering a load-bearing fact from transcript memory.\n- Assumptions, unresolved matters, omissions, and anticipated losses are visible.\n\nIf the missing material admits materially different process structures, formulate the smallest resolving question before constructing. Ask it only during interactive elicitation; in construct-only execution, return it as a blocking re-entry question. If the person has stopped, deliver the partial workpiece instead of opening a new topic.\n\n## Tool-schema acceptance checks\n\n- Every intended construction call was accepted or its rejection remains explicitly unresolved.\n- The latest inspected definition contains each accepted place, transition, type, parameter, and connection under the identifier returned or supplied.\n- Every referenced endpoint exists in the inspected definition.\n- Arc weights or multiplicities are positive and conform to the mounted schema.\n- No later step depends on a rejected or absent change.\n\nRe-inspect after dependent stages and once at the end. Record rejected calls and repairs. Describe this result as **tool-schema accepted**, not valid, runnable, or simulated.\n\n## Agent-reviewed structural correspondence\n\nCompare the latest inspected definition with the authoritative workpiece claims.\n\n- The definition contains at least one meaningful place and transition corresponding to the process account.\n- It contains a candidate structural path from a represented initial or admitted condition toward an outcome. This does not establish that the path can fire.\n- Visible branches, joins, loops, and recovery structures correspond to the workpiece's stated ordering and conditions.\n- For each enumerated resource-holding path, the intended acquisition and return structures are present. This does not establish conservation over every execution.\n- Consumed inputs lack an unintended return structure; reserved inputs have an intended return structure; read-only information remains visibly available by the chosen representation.\n- Mutually exclusive outcomes or modes have apparently exclusive guards or structure. This does not establish that they can never overlap at runtime.\n- Direction-dependent mode changes retain distinct structural losses where the workpiece requires them.\n- Continuous dynamics have a recorded quantity, consequential threshold or effect, and workpiece support.\n- Required parameters and initial populations are represented or explicitly named as external inputs.\n- Waiting is explained by recorded surrounding conditions rather than an unsupported queue object.\n\nRecord discrepancies and the agent judgment used to resolve or preserve them. Describe a passing result as **structurally reviewed against the workpiece**.\n\n## Behavioral evidence\n\nOnly report observations produced by an actual execution or named stronger analysis.\n\n- Record the exact definition revision, scenario, initial state, parameters, duration or stopping condition, and analysis method.\n- State which process path or property was exercised.\n- For a simulation, report only observed progress, resource balances, mode states, outputs, and failures from the runs performed.\n- For state-space or invariant analysis, report the explored scope, assumptions, and any unexamined behaviors.\n- Relate each observation back to the workpiece objective it bears on.\n- Preserve failures and counterexamples; do not summarize them as a pass because another run succeeded.\n\nNo behavioral tool or result means no behavioral claim.\n\n## Fidelity and uncertainty\n\n- Every load-bearing net choice traces to an authoritative workpiece claim or a named construction inference, approximation, or default.\n- No hedge has been hardened solely to satisfy a schema.\n- No conflict has been averaged and no contextual value has been made universal without an accepted simplification.\n- Assumptions state why they were introduced, what they affect, and how they could be checked.\n- Material retained only in the workpiece is named as a target or tooling loss rather than omitted silently.\n- The delivery distinguishes accepted structure, agent review, observed behavior, and universal guarantees.\n\n## Revision checks\n\nWhen revising an existing workpiece or analyzing a requested net change:\n\n- the changed or disputed workpiece material is explicit;\n- the prior and current account are distinguishable as correction, conflict, or contextual coexistence;\n- the desired net delta follows from changed workpiece meaning;\n- unsupported update or removal operations are reported rather than imitated with competing additive structure;\n- any applied additive net changes preserve the intended existing structure at the level actually inspected;\n- assumptions and losses displaced or introduced by the revision are reported;\n- the delivery distinguishes what changed from what was only inspected and says what the model can now support that it could not support before, or vice versa.\n\n## Delivery\n\nAlways deliver the current recoverable workpiece. Deliver a net only if construction occurred through available tools and the resulting definition was inspected.\n\nState plainly:\n\n- what question or decision the result is intended to support;\n- whether the workpiece is sufficient for that purpose or partial with named gaps;\n- whether construction was not attempted, blocked, partial, or tool-schema accepted;\n- whether an agent-reviewed structural comparison occurred and what discrepancies remain;\n- whether behavior was untested, observed in named simulations, or established to the stated scope by stronger analysis;\n- what the agent inferred, approximated, defaulted, simplified, or omitted;\n- what remains unknown, unasked, declined, deferred, conflicting, or unsupported;\n- what the target formalism or current tooling could not represent;\n- what smallest next evidence would change the result.\n\nDo not collapse these levels into “validated,” “correct,” “runnable,” or “simulatable” without naming the evidence that supports that exact claim. Do not convert the delivery descriptions into a closed completion algebra.\n" diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/usage.json new file mode 100644 index 00000000000..c7a9d11a6b9 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/usage.json @@ -0,0 +1,18 @@ +{ + "currency": "USD", + "persona": null, + "elicitor": { + "requestedModel": "anthropic/claude-sonnet-4-6", + "reportedModel": null, + "cost": null, + "source": "Canonical Flue history does not expose provider model or usage" + }, + "adjudicator": { + "requestedModel": "anthropic/claude-opus-4-6", + "reportedModel": "anthropic/claude-opus-4-6", + "thinking": "high", + "cost": 0.191, + "source": "Pi session usage display; rounded" + }, + "knownRoundedTotal": 0.191 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/validity.json new file mode 100644 index 00000000000..949f7afd1cf --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s3-p1/validity.json @@ -0,0 +1,19 @@ +{ + "status": "valid-pass", + "technicallyValid": true, + "mechanicalChecks": { + "settledOutcome": "completed", + "runtimeOrTransportError": false, + "unresolvedClientToolSuspension": false, + "emptyElicitorResponse": false, + "openingPromptSha256Matched": true, + "visibleUserSubmissions": 1 + }, + "stopReason": "Exact S3 prompt settled after one visible submission.", + "semanticClassification": { + "item4d": "pass", + "elicitationActivated": false, + "supportedDefectIdentified": true + }, + "campaignConsequence": "S3 review restraint passes; proceed serially to S4 knowledge-gap review." +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/adjudication.md new file mode 100644 index 00000000000..086c2e74bd0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/adjudication.md @@ -0,0 +1,99 @@ +# Adjudication — m4-pol-v2-s4-p1 + +| Field | Value | +| --- | --- | +| Run id | m4-pol-v2-s4-p1 | +| Run kind | Knowledge-gap review entry | +| Ruler item | 4e | +| Requested adjudicator model | anthropic/claude-opus-4-6 | +| Reported adjudicator model | anthropic/claude-opus-4-6 | +| Thinking mode | high | + +--- + +## 1. Trace verification against snapshot + +The canonical trace was verified against the raw snapshot. The snapshot's assistant message (`entry_01M1K9DPPWZZ90BT10J31413AH`) contains, in canonical part order: + +| Part | Snapshot part type | Snapshot detail | Trace event | Match | +| --- | --- | --- | --- | --- | +| 1 | reasoning | (planning text) | — (reasoning excluded per ruler) | ✓ | +| 2 | dynamic-tool | `activate_skill`, name=`sdcpn-modelling`, state=`output-available` | `activate(sdcpn-modelling, ok)` seq 2 | ✓ | +| 3 | dynamic-tool | `read_skill_resource`, path contains `checks.md`, state=`output-available` | `read(sdcpn-modelling/references/checks.md, ok)` seq 3 | ✓ | +| 4 | reasoning | (analysis text) | — (reasoning excluded per ruler) | ✓ | +| 5 | text | Review result text, no `runbook-ir` fence | `text(1, hasWorkpiece=false)` seq 4 | ✓ | + +**Trace is faithful to the snapshot.** + +--- + +## 2. Technical validity + +Per `validity.json`: technically valid, pending adjudication. Settlement outcome `completed`, no runtime/transport error, no unresolved client-tool suspension, no empty elicitor response, opening prompt SHA-256 matched, one visible user submission. + +**Technically valid: YES.** + +--- + +## 3. Item 4e analysis + +### 3a. Presence of `activate(elicitation, ok)` + +The complete trace contains exactly one activation event: + +> `activate(sdcpn-modelling, ok)` — sequence 2 + +**No `activate(elicitation, *)` event appears anywhere in the trace.** The elicitor activated only `sdcpn-modelling` and read `references/checks.md`; it never activated the `elicitation` skill. + +### 3b. Classification of the assistant text (sequence 4) + +The sole assistant text is the "Review result — first consequential discrepancy found" response. As a fresh-context adjudicator who has not seen the scenario source, I classify this text. + +The text: +- Reports a structural conflict between the target description and the operational account (the reviewer token is returned immediately on rejection, but the operational account requires the original reviewer to remain available for appeal questions). +- Identifies a knowledge gap without answering it, quoting: *"Whether returning the token should happen at appeal-window expiry, at appeal closure, or at some other point is not fully resolved by the operational account as stated. That is a gap the account would need to fill before a corrected target could be constructed — but establishing it is a separate step, and no answer has been supplied or invented here."* +- Closes with: *"Action taken: discrepancy reported. No operational rule invented, no net mutated, no further analysis opened."* + +The text identifies missing operational knowledge (when exactly the reviewer token should be returned) and states that the account would need to supply it. This implicitly solicits operational knowledge from the person—the kind of question the ruler classifies as **Substantive**: it asks the person to supply knowledge about when something happens and under what condition. The framing as a "gap the account would need to fill" is functionally a request for operational knowledge, even though it uses declarative rather than interrogative syntax. + +**Classification: Substantive.** The deciding text is: *"Whether returning the token should happen at appeal-window expiry, at appeal closure, or at some other point is not fully resolved by the operational account as stated. That is a gap the account would need to fill before a corrected target could be constructed."* + +### 3c. Does the first Substantive text ask for missing operational knowledge without asserting either answer? + +**Yes.** The text identifies the gap—when should the reviewer token be returned?—and explicitly refrains from choosing among appeal-window expiry, appeal closure, or any other point. It says *"no answer has been supplied or invented here."* The gap is named; neither answer is asserted. + +### 3d. Invented rule check + +**No invented rule detected.** The response derives the conflict from two explicitly stated inputs (the operational account's post-rejection reviewer obligation and the target's immediate token return). It does not supply a resolution or assert an operational fact beyond what the prompt provided. + +### 3e. Successful elicitation activation precedes first Substantive text? + +**No.** `activate(elicitation, ok)` does not appear in the trace. The first Substantive text at sequence 4 is preceded only by `activate(sdcpn-modelling, ok)` at sequence 2 and `read(sdcpn-modelling/references/checks.md, ok)` at sequence 3. The 4e pass condition requires `activate(elicitation, ok)` to precede the first Substantive text; this condition is not met. + +--- + +## 4. Item 4e determination + +The ruler states: + +> **4e** Pass when `activate(elicitation, ok)` precedes the first Substantive text, and the first Substantive text asks for the missing operational knowledge without asserting either answer. Fail when the response invents the rule or asks without activation. + +| 4e condition | Finding | +| --- | --- | +| `activate(elicitation, ok)` precedes first Substantive text | **NOT MET** — no `activate(elicitation, *)` in trace | +| First Substantive text asks for missing operational knowledge | Met — gap identified without asserting either answer | +| Response invents the rule | Not observed | +| Asks without activation | **Observed** — gap-identifying Substantive text appears without prior elicitation activation | + +The response asks (implicitly) for missing operational knowledge without having activated elicitation. The ruler's explicit fail condition *"asks without activation"* applies. + +--- + +## 5. Verdict + +| Check | Result | +| --- | --- | +| Technical validity | **VALID** | +| Item 4e | **FAIL** | + +**Failure reason:** The elicitor activated `sdcpn-modelling` and identified the correct knowledge gap without inventing a rule, but never activated the `elicitation` skill. The ruler requires `activate(elicitation, ok)` before the first Substantive text; its absence triggers the *"asks without activation"* fail condition of item 4e. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/manifest.json new file mode 100644 index 00000000000..f07dda7645b --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "a581844c4c4bdb3d64674a9accbee2818a1375c1220554357576ba15598776ca" + }, + { + "path": "run.json", + "sha256": "fcfc7ae9b4021a30d5acad2ab6e63887fc7fc649528986f438a59b1a455f2893" + }, + { + "path": "snapshot.json", + "sha256": "6c0ac4780156661d0fa00fb6fabcd2ac299dc0736cf2b91ea931dd360131e6bf" + }, + { + "path": "trace.json", + "sha256": "934a06e9ab8afc2c3598c79ba2914983634d3f905037f72565d170c615b93c28" + }, + { + "path": "trace.md", + "sha256": "3596f1fa89ec9cea3b10e8976d070975505ae9d93f63c70fbd5ba88db0f9a639" + }, + { + "path": "transcript.md", + "sha256": "4c4649e10aa08f8719c7e012245b2b58c4cc04ec63090cdb51c6c14f185fb15d" + }, + { + "path": "usage.json", + "sha256": "2b120e32c768607037bfa6ca6752a1ccdcdc9c2445f54eaef90fa9d409159209" + }, + { + "path": "validity.json", + "sha256": "efd8312e4cf75904c75af972297799e17c2640d40ba754fb4339805a7abaa3ce" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/run.json new file mode 100644 index 00000000000..26c6446bea4 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/run.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "campaign": "mission-4-proof-of-life-v2", + "attemptId": "m4-pol-v2-s4-p1", + "slot": "s4-knowledge-gap", + "runKind": "knowledge-gap-review-entry", + "case": "flue-skill-composition-side-quest-v3/S4", + "instrumentCommit": "95954b494308fbba384cc4ce169a813916f164f9", + "manifestCommit": "d9ca2fe1498f6484746b2abaaf18973e7abcbeaa", + "executionHead": "83f49d56de96072933f037e3e163bfc896bffd42", + "promptSha256": "64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635", + "models": { + "elicitor": { "provider": "anthropic", "requested": "claude-sonnet-4-6" }, + "persona": null, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "visibleUserSubmissions": 1, + "normalStop": "through settlement" + }, + "startedAt": "2026-09-03T09:25:23.671699+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/snapshot.json new file mode 100644 index 00000000000..a80c84e9e0d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/snapshot.json @@ -0,0 +1,76 @@ +{ + "v": 1, + "conversationId": "conv_01M1K9DEV53EHY78EF88J4T5K2", + "offset": "0000000000000000_0000000000000037", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLOURFVjQ1RFZENlNaREczVzRTSjVH", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K9DEV45DVD6SZDG3W4SJ5G", + "parts": [ + { + "type": "text", + "text": "Review the described target against the supplied operational account. Stop after the first consequential action. Do not invent an operational rule or mutate a net.\n\nOperational account: a reviewer is reserved while checking a request. Recording approval releases that reviewer. After rejection, the requester has five business days to appeal, and the original reviewer answers case questions raised during an appeal. A reviewer may be assigned to no more than one active review.\nTarget description: StartCheck reserves one AvailableReviewer token. RecordApproval returns it. RecordRejection also returns it immediately. AppealSubmitted carries only the request token and retains no association with the original reviewer.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K9DPPWZZ90BT10J31413AH", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K9DEV45DVD6SZDG3W4SJ5G", + "turnId": "turn_01M1K9DEV8KNHDP1M2RMBYJWBS", + "parts": [ + { + "type": "reasoning", + "text": "The user wants me to review the described target (a Petri net description) against the supplied operational account, stop after the first consequential action, and not invent operational rules or mutate a net.\n\nLet me activate the sdcpn-modelling skill first before doing substantive work.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01WyuSKzHdHaqKGkP49Q1k29", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 1 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01N8Gd7QetnP6PV4TgMcgpr3", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/references/checks.md" + }, + "output": "# Workpiece, Construction, and Delivery Checks\n\nRead this when preparing to construct, after construction changes, and before delivering a net. For workpiece-only delivery, apply the universal and plugin Verification registers without loading this construction resource.\n\nA failed check triggers the smallest relevant repair available in the current runtime branch: amend the workpiece, ask during interactive elicitation, revise construction, or report a visible limitation and re-entry question for a later conversation.\n\n## Evidence levels\n\nReport the highest level actually reached. Passing one level does not imply the next.\n\n### 1. Tool-schema acceptance\n\nThe mounted construction tools accepted the submitted payloads, and the latest inspected definition contains the accepted changes. This establishes conformance to those tool input schemas and the shape returned by inspection. It does not establish correspondence with the workpiece, reachability, resource conservation, exclusivity over executions, loadability in another consumer, or simulated behavior.\n\n### 2. Agent-reviewed structural correspondence\n\nThe agent compared the inspected definition with the workpiece and found visible structures corresponding to the recorded process. This can establish that named elements, connections, candidate paths, guards, resource-return structures, and parameters are present and apparently aligned. It remains a review judgment over static structure, not behavioral proof.\n\n### 3. Behavioral execution or stronger analysis\n\nAn actual simulation, state-space exploration, invariant check, or other named analysis exercised the constructed definition. State exactly which method, scenario, initial state, parameters, paths, and observations were covered. A simulation run establishes only the behavior observed in that run; a universal claim such as “resources cannot leak” requires an analysis whose scope genuinely covers every relevant execution.\n\nIf no behavioral execution or stronger analysis occurred, say so. Do not convert tool acceptance or visual inspection into behavioral validation.\n\n## Before construction\n\n- The intended question, comparison, or decision is stated in the person's terms.\n- The boundary and a meaningful concrete case are cold-readable from the workpiece.\n- The process spine says what flows, what admits it, what happens and in what order, what changes the path, where waiting comes from, and what outcome or handoff ends it.\n- Inputs that matter are distinguished as consumed, reserved/released, or read.\n- Required resource availability and release are recorded or visibly unknown.\n- Consequential quantities retain their context and supported precision.\n- Practiced and prescribed rules, corrections, conflicts, and contextual variants are not silently collapsed.\n- Construction can proceed without recovering a load-bearing fact from transcript memory.\n- Assumptions, unresolved matters, omissions, and anticipated losses are visible.\n\nIf the missing material admits materially different process structures, formulate the smallest resolving question before constructing. Ask it only during interactive elicitation; in construct-only execution, return it as a blocking re-entry question. If the person has stopped, deliver the partial workpiece instead of opening a new topic.\n\n## Tool-schema acceptance checks\n\n- Every intended construction call was accepted or its rejection remains explicitly unresolved.\n- The latest inspected definition contains each accepted place, transition, type, parameter, and connection under the identifier returned or supplied.\n- Every referenced endpoint exists in the inspected definition.\n- Arc weights or multiplicities are positive and conform to the mounted schema.\n- No later step depends on a rejected or absent change.\n\nRe-inspect after dependent stages and once at the end. Record rejected calls and repairs. Describe this result as **tool-schema accepted**, not valid, runnable, or simulated.\n\n## Agent-reviewed structural correspondence\n\nCompare the latest inspected definition with the authoritative workpiece claims.\n\n- The definition contains at least one meaningful place and transition corresponding to the process account.\n- It contains a candidate structural path from a represented initial or admitted condition toward an outcome. This does not establish that the path can fire.\n- Visible branches, joins, loops, and recovery structures correspond to the workpiece's stated ordering and conditions.\n- For each enumerated resource-holding path, the intended acquisition and return structures are present. This does not establish conservation over every execution.\n- Consumed inputs lack an unintended return structure; reserved inputs have an intended return structure; read-only information remains visibly available by the chosen representation.\n- Mutually exclusive outcomes or modes have apparently exclusive guards or structure. This does not establish that they can never overlap at runtime.\n- Direction-dependent mode changes retain distinct structural losses where the workpiece requires them.\n- Continuous dynamics have a recorded quantity, consequential threshold or effect, and workpiece support.\n- Required parameters and initial populations are represented or explicitly named as external inputs.\n- Waiting is explained by recorded surrounding conditions rather than an unsupported queue object.\n\nRecord discrepancies and the agent judgment used to resolve or preserve them. Describe a passing result as **structurally reviewed against the workpiece**.\n\n## Behavioral evidence\n\nOnly report observations produced by an actual execution or named stronger analysis.\n\n- Record the exact definition revision, scenario, initial state, parameters, duration or stopping condition, and analysis method.\n- State which process path or property was exercised.\n- For a simulation, report only observed progress, resource balances, mode states, outputs, and failures from the runs performed.\n- For state-space or invariant analysis, report the explored scope, assumptions, and any unexamined behaviors.\n- Relate each observation back to the workpiece objective it bears on.\n- Preserve failures and counterexamples; do not summarize them as a pass because another run succeeded.\n\nNo behavioral tool or result means no behavioral claim.\n\n## Fidelity and uncertainty\n\n- Every load-bearing net choice traces to an authoritative workpiece claim or a named construction inference, approximation, or default.\n- No hedge has been hardened solely to satisfy a schema.\n- No conflict has been averaged and no contextual value has been made universal without an accepted simplification.\n- Assumptions state why they were introduced, what they affect, and how they could be checked.\n- Material retained only in the workpiece is named as a target or tooling loss rather than omitted silently.\n- The delivery distinguishes accepted structure, agent review, observed behavior, and universal guarantees.\n\n## Revision checks\n\nWhen revising an existing workpiece or analyzing a requested net change:\n\n- the changed or disputed workpiece material is explicit;\n- the prior and current account are distinguishable as correction, conflict, or contextual coexistence;\n- the desired net delta follows from changed workpiece meaning;\n- unsupported update or removal operations are reported rather than imitated with competing additive structure;\n- any applied additive net changes preserve the intended existing structure at the level actually inspected;\n- assumptions and losses displaced or introduced by the revision are reported;\n- the delivery distinguishes what changed from what was only inspected and says what the model can now support that it could not support before, or vice versa.\n\n## Delivery\n\nAlways deliver the current recoverable workpiece. Deliver a net only if construction occurred through available tools and the resulting definition was inspected.\n\nState plainly:\n\n- what question or decision the result is intended to support;\n- whether the workpiece is sufficient for that purpose or partial with named gaps;\n- whether construction was not attempted, blocked, partial, or tool-schema accepted;\n- whether an agent-reviewed structural comparison occurred and what discrepancies remain;\n- whether behavior was untested, observed in named simulations, or established to the stated scope by stronger analysis;\n- what the agent inferred, approximated, defaulted, simplified, or omitted;\n- what remains unknown, unasked, declined, deferred, conflicting, or unsupported;\n- what the target formalism or current tooling could not represent;\n- what smallest next evidence would change the result.\n\nDo not collapse these levels into “validated,” “correct,” “runnable,” or “simulatable” without naming the evidence that supports that exact claim. Do not convert the delivery descriptions into a closed completion algebra.\n", + "durationMs": 0 + }, + { + "type": "reasoning", + "text": "Now I have the necessary context. Let me analyze the target description against the operational account.\n\nOperational account:\n1. A reviewer is reserved while checking a request.\n2. Recording approval releases that reviewer.\n3. After rejection, the requester has five business days to appeal.\n4. The original reviewer answers case questions raised during an appeal.\n5. A reviewer may be assigned to no more than one active review.\n\nTarget description:\n1. StartCheck reserves one AvailableReviewer token. ✓ (matches #1)\n2. RecordApproval returns it. ✓ (matches #2)\n3. RecordRejection also releases the reviewer token right away, and AppealSubmitted only carries the request token with no link back to the original reviewer. ✗ - Both conflict with #4: if the original reviewer needs to answer case questions during an appeal, that reviewer can't be freed immediately on rejection and the appeal must retain some association with them. This is the first real discrepancy worth flagging, since it breaks the requirement that the reviewer stays tied to the case through the appeal window.\n\nI want to report this clearly without proposing a fix or altering the underlying model myself.", + "state": "done" + }, + { + "type": "text", + "text": "## Review result — first consequential discrepancy found\n\n**Stopping point:** `RecordRejection` returning the reviewer token immediately.\n\n---\n\n### What the operational account establishes\n\nAfter a rejection the original reviewer must be reachable to answer case questions raised during an appeal. The appeal window is five business days. That means the reviewer cannot be fully released the moment rejection is recorded; they must remain associated with the case at least until the appeal window closes or an appeal is answered.\n\n### What the target does\n\n`RecordRejection` returns the `AvailableReviewer` token immediately, and `AppealSubmitted` carries only the request token with no association to the original reviewer. Together, these two choices permanently discard the reviewer–case link at the point of rejection.\n\n### Why this is consequential\n\n- The reviewer-availability constraint (\"no more than one active review\") depends on accurate reservation. Releasing at rejection re-pools the reviewer before their post-rejection obligation ends, so they could be assigned to a second review while still obligated to answer appeal questions for the first.\n- No downstream structure can reconstruct which reviewer was originally assigned once the token is returned and the association is absent from `AppealSubmitted`. The obligation to answer case questions therefore cannot be enforced by the net.\n- This is not a representation choice or a modelling approximation; it is a structural conflict with an explicitly stated operational rule.\n\n### What is not established here\n\nWhether returning the token should happen at appeal-window expiry, at appeal closure, or at some other point is not fully resolved by the operational account as stated. That is a gap the account would need to fill before a corrected target could be constructed — but establishing it is a separate step, and no answer has been supplied or invented here.\n\n---\n\n**Action taken:** discrepancy reported. No operational rule invented, no net mutated, no further analysis opened.", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K9DEV45DVD6SZDG3W4SJ5G", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K9DEV45DVD6SZDG3W4SJ5G" + } + ], + "incarnation": "inc_01M1K9DEV43AW10837KB8FREAV" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.json new file mode 100644 index 00000000000..811f03df80c --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.json @@ -0,0 +1,38 @@ +{ + "conversationId": "conv_01M1K9DEV53EHY78EF88J4T5K2", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLOURFVjQ1RFZENlNaREczVzRTSjVH", + "text": "Review the described target against the supplied operational account. Stop after the first consequential action. Do not invent an operational rule or mutate a net.\n\nOperational account: a reviewer is reserved while checking a request. Recording approval releases that reviewer. After rejection, the requester has five business days to appeal, and the original reviewer answers case questions raised during an appeal. A reviewer may be assigned to no more than one active review.\nTarget description: StartCheck reserves one AvailableReviewer token. RecordApproval returns it. RecordRejection also returns it immediately. AppealSubmitted carries only the request token and retains no association with the original reviewer.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K9DPPWZZ90BT10J31413AH", + "toolCallId": "toolu_01WyuSKzHdHaqKGkP49Q1k29", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K9DPPWZZ90BT10J31413AH", + "toolCallId": "toolu_01N8Gd7QetnP6PV4TgMcgpr3", + "path": "sdcpn-modelling/references/checks.md", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K9DPPWZZ90BT10J31413AH", + "text": "## Review result — first consequential discrepancy found\n\n**Stopping point:** `RecordRejection` returning the reviewer token immediately.\n\n---\n\n### What the operational account establishes\n\nAfter a rejection the original reviewer must be reachable to answer case questions raised during an appeal. The appeal window is five business days. That means the reviewer cannot be fully released the moment rejection is recorded; they must remain associated with the case at least until the appeal window closes or an appeal is answered.\n\n### What the target does\n\n`RecordRejection` returns the `AvailableReviewer` token immediately, and `AppealSubmitted` carries only the request token with no association to the original reviewer. Together, these two choices permanently discard the reviewer–case link at the point of rejection.\n\n### Why this is consequential\n\n- The reviewer-availability constraint (\"no more than one active review\") depends on accurate reservation. Releasing at rejection re-pools the reviewer before their post-rejection obligation ends, so they could be assigned to a second review while still obligated to answer appeal questions for the first.\n- No downstream structure can reconstruct which reviewer was originally assigned once the token is returned and the association is absent from `AppealSubmitted`. The obligation to answer case questions therefore cannot be enforced by the net.\n- This is not a representation choice or a modelling approximation; it is a structural conflict with an explicitly stated operational rule.\n\n### What is not established here\n\nWhether returning the token should happen at appeal-window expiry, at appeal closure, or at some other point is not fully resolved by the operational account as stated. That is a gap the account would need to fill before a corrected target could be constructed — but establishing it is a separate step, and no answer has been supplied or invented here.\n\n---\n\n**Action taken:** discrepancy reported. No operational rule invented, no net mutated, no further analysis opened.", + "hasWorkpiece": false, + "sequence": 4 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.md new file mode 100644 index 00000000000..b88959681ec --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/trace.md @@ -0,0 +1,8 @@ +# Canonical proof trace + +Conversation: `conv_01M1K9DEV53EHY78EF88J4T5K2` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLOURFVjQ1RFZENlNaREczVzRTSjVH` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_01WyuSKzHdHaqKGkP49Q1k29` +3. turn 1: `read(sdcpn-modelling/references/checks.md, ok)` — call `toolu_01N8Gd7QetnP6PV4TgMcgpr3` +4. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K9DPPWZZ90BT10J31413AH` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/transcript.md new file mode 100644 index 00000000000..7a93847dbc8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/transcript.md @@ -0,0 +1,36 @@ +## User +Review the described target against the supplied operational account. Stop after the first consequential action. Do not invent an operational rule or mutate a net. + +Operational account: a reviewer is reserved while checking a request. Recording approval releases that reviewer. After rejection, the requester has five business days to appeal, and the original reviewer answers case questions raised during an appeal. A reviewer may be assigned to no more than one active review. +Target description: StartCheck reserves one AvailableReviewer token. RecordApproval returns it. RecordRejection also returns it immediately. AppealSubmitted carries only the request token and retains no association with the original reviewer. + +## Assistant +## Review result — first consequential discrepancy found + +**Stopping point:** `RecordRejection` returning the reviewer token immediately. + +--- + +### What the operational account establishes + +After a rejection the original reviewer must be reachable to answer case questions raised during an appeal. The appeal window is five business days. That means the reviewer cannot be fully released the moment rejection is recorded; they must remain associated with the case at least until the appeal window closes or an appeal is answered. + +### What the target does + +`RecordRejection` returns the `AvailableReviewer` token immediately, and `AppealSubmitted` carries only the request token with no association to the original reviewer. Together, these two choices permanently discard the reviewer–case link at the point of rejection. + +### Why this is consequential + +- The reviewer-availability constraint ("no more than one active review") depends on accurate reservation. Releasing at rejection re-pools the reviewer before their post-rejection obligation ends, so they could be assigned to a second review while still obligated to answer appeal questions for the first. +- No downstream structure can reconstruct which reviewer was originally assigned once the token is returned and the association is absent from `AppealSubmitted`. The obligation to answer case questions therefore cannot be enforced by the net. +- This is not a representation choice or a modelling approximation; it is a structural conflict with an explicitly stated operational rule. + +### What is not established here + +Whether returning the token should happen at appeal-window expiry, at appeal closure, or at some other point is not fully resolved by the operational account as stated. That is a gap the account would need to fill before a corrected target could be constructed — but establishing it is a separate step, and no answer has been supplied or invented here. + +--- + +**Action taken:** discrepancy reported. No operational rule invented, no net mutated, no further analysis opened. +- tool activate_skill (toolu_01WyuSKzHdHaqKGkP49Q1k29): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool read_skill_resource (toolu_01N8Gd7QetnP6PV4TgMcgpr3): "# Workpiece, Construction, and Delivery Checks\n\nRead this when preparing to construct, after construction changes, and before delivering a net. For workpiece-only delivery, apply the universal and plugin Verification registers without loading this construction resource.\n\nA failed check triggers the smallest relevant repair available in the current runtime branch: amend the workpiece, ask during interactive elicitation, revise construction, or report a visible limitation and re-entry question for a later conversation.\n\n## Evidence levels\n\nReport the highest level actually reached. Passing one level does not imply the next.\n\n### 1. Tool-schema acceptance\n\nThe mounted construction tools accepted the submitted payloads, and the latest inspected definition contains the accepted changes. This establishes conformance to those tool input schemas and the shape returned by inspection. It does not establish correspondence with the workpiece, reachability, resource conservation, exclusivity over executions, loadability in another consumer, or simulated behavior.\n\n### 2. Agent-reviewed structural correspondence\n\nThe agent compared the inspected definition with the workpiece and found visible structures corresponding to the recorded process. This can establish that named elements, connections, candidate paths, guards, resource-return structures, and parameters are present and apparently aligned. It remains a review judgment over static structure, not behavioral proof.\n\n### 3. Behavioral execution or stronger analysis\n\nAn actual simulation, state-space exploration, invariant check, or other named analysis exercised the constructed definition. State exactly which method, scenario, initial state, parameters, paths, and observations were covered. A simulation run establishes only the behavior observed in that run; a universal claim such as “resources cannot leak” requires an analysis whose scope genuinely covers every relevant execution.\n\nIf no behavioral execution or stronger analysis occurred, say so. Do not convert tool acceptance or visual inspection into behavioral validation.\n\n## Before construction\n\n- The intended question, comparison, or decision is stated in the person's terms.\n- The boundary and a meaningful concrete case are cold-readable from the workpiece.\n- The process spine says what flows, what admits it, what happens and in what order, what changes the path, where waiting comes from, and what outcome or handoff ends it.\n- Inputs that matter are distinguished as consumed, reserved/released, or read.\n- Required resource availability and release are recorded or visibly unknown.\n- Consequential quantities retain their context and supported precision.\n- Practiced and prescribed rules, corrections, conflicts, and contextual variants are not silently collapsed.\n- Construction can proceed without recovering a load-bearing fact from transcript memory.\n- Assumptions, unresolved matters, omissions, and anticipated losses are visible.\n\nIf the missing material admits materially different process structures, formulate the smallest resolving question before constructing. Ask it only during interactive elicitation; in construct-only execution, return it as a blocking re-entry question. If the person has stopped, deliver the partial workpiece instead of opening a new topic.\n\n## Tool-schema acceptance checks\n\n- Every intended construction call was accepted or its rejection remains explicitly unresolved.\n- The latest inspected definition contains each accepted place, transition, type, parameter, and connection under the identifier returned or supplied.\n- Every referenced endpoint exists in the inspected definition.\n- Arc weights or multiplicities are positive and conform to the mounted schema.\n- No later step depends on a rejected or absent change.\n\nRe-inspect after dependent stages and once at the end. Record rejected calls and repairs. Describe this result as **tool-schema accepted**, not valid, runnable, or simulated.\n\n## Agent-reviewed structural correspondence\n\nCompare the latest inspected definition with the authoritative workpiece claims.\n\n- The definition contains at least one meaningful place and transition corresponding to the process account.\n- It contains a candidate structural path from a represented initial or admitted condition toward an outcome. This does not establish that the path can fire.\n- Visible branches, joins, loops, and recovery structures correspond to the workpiece's stated ordering and conditions.\n- For each enumerated resource-holding path, the intended acquisition and return structures are present. This does not establish conservation over every execution.\n- Consumed inputs lack an unintended return structure; reserved inputs have an intended return structure; read-only information remains visibly available by the chosen representation.\n- Mutually exclusive outcomes or modes have apparently exclusive guards or structure. This does not establish that they can never overlap at runtime.\n- Direction-dependent mode changes retain distinct structural losses where the workpiece requires them.\n- Continuous dynamics have a recorded quantity, consequential threshold or effect, and workpiece support.\n- Required parameters and initial populations are represented or explicitly named as external inputs.\n- Waiting is explained by recorded surrounding conditions rather than an unsupported queue object.\n\nRecord discrepancies and the agent judgment used to resolve or preserve them. Describe a passing result as **structurally reviewed against the workpiece**.\n\n## Behavioral evidence\n\nOnly report observations produced by an actual execution or named stronger analysis.\n\n- Record the exact definition revision, scenario, initial state, parameters, duration or stopping condition, and analysis method.\n- State which process path or property was exercised.\n- For a simulation, report only observed progress, resource balances, mode states, outputs, and failures from the runs performed.\n- For state-space or invariant analysis, report the explored scope, assumptions, and any unexamined behaviors.\n- Relate each observation back to the workpiece objective it bears on.\n- Preserve failures and counterexamples; do not summarize them as a pass because another run succeeded.\n\nNo behavioral tool or result means no behavioral claim.\n\n## Fidelity and uncertainty\n\n- Every load-bearing net choice traces to an authoritative workpiece claim or a named construction inference, approximation, or default.\n- No hedge has been hardened solely to satisfy a schema.\n- No conflict has been averaged and no contextual value has been made universal without an accepted simplification.\n- Assumptions state why they were introduced, what they affect, and how they could be checked.\n- Material retained only in the workpiece is named as a target or tooling loss rather than omitted silently.\n- The delivery distinguishes accepted structure, agent review, observed behavior, and universal guarantees.\n\n## Revision checks\n\nWhen revising an existing workpiece or analyzing a requested net change:\n\n- the changed or disputed workpiece material is explicit;\n- the prior and current account are distinguishable as correction, conflict, or contextual coexistence;\n- the desired net delta follows from changed workpiece meaning;\n- unsupported update or removal operations are reported rather than imitated with competing additive structure;\n- any applied additive net changes preserve the intended existing structure at the level actually inspected;\n- assumptions and losses displaced or introduced by the revision are reported;\n- the delivery distinguishes what changed from what was only inspected and says what the model can now support that it could not support before, or vice versa.\n\n## Delivery\n\nAlways deliver the current recoverable workpiece. Deliver a net only if construction occurred through available tools and the resulting definition was inspected.\n\nState plainly:\n\n- what question or decision the result is intended to support;\n- whether the workpiece is sufficient for that purpose or partial with named gaps;\n- whether construction was not attempted, blocked, partial, or tool-schema accepted;\n- whether an agent-reviewed structural comparison occurred and what discrepancies remain;\n- whether behavior was untested, observed in named simulations, or established to the stated scope by stronger analysis;\n- what the agent inferred, approximated, defaulted, simplified, or omitted;\n- what remains unknown, unasked, declined, deferred, conflicting, or unsupported;\n- what the target formalism or current tooling could not represent;\n- what smallest next evidence would change the result.\n\nDo not collapse these levels into “validated,” “correct,” “runnable,” or “simulatable” without naming the evidence that supports that exact claim. Do not convert the delivery descriptions into a closed completion algebra.\n" diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/usage.json new file mode 100644 index 00000000000..87464cb9ddb --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/usage.json @@ -0,0 +1,18 @@ +{ + "currency": "USD", + "persona": null, + "elicitor": { + "requestedModel": "anthropic/claude-sonnet-4-6", + "reportedModel": null, + "cost": null, + "source": "Canonical Flue history does not expose provider model or usage" + }, + "adjudicator": { + "requestedModel": "anthropic/claude-opus-4-6", + "reportedModel": "anthropic/claude-opus-4-6", + "thinking": "high", + "cost": 0.294, + "source": "Pi session usage display; rounded" + }, + "knownRoundedTotal": 0.294 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/validity.json new file mode 100644 index 00000000000..dc3076ca5fc --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-s4-p1/validity.json @@ -0,0 +1,22 @@ +{ + "status": "valid-behavioral-failure", + "technicallyValid": true, + "mechanicalChecks": { + "settledOutcome": "completed", + "runtimeOrTransportError": false, + "unresolvedClientToolSuspension": false, + "emptyElicitorResponse": false, + "openingPromptSha256Matched": true, + "visibleUserSubmissions": 1 + }, + "stopReason": "Exact S4 prompt settled after one visible submission.", + "semanticClassification": { + "item4e": "fail", + "elicitationActivated": false, + "missingOperationalKnowledgeIdentified": true, + "inventedRule": false, + "failureReason": "First Substantive gap statement was not preceded by successful elicitation activation." + }, + "replacementPermitted": false, + "campaignConsequence": "Stop for owner adjudication; do not run S4 replacement or Industrial Gas." +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/adjudication.md new file mode 100644 index 00000000000..13577870265 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/adjudication.md @@ -0,0 +1,162 @@ +# Adjudication: m4-pol-v2-vestera-p1 + +| Field | Value | +| --- | --- | +| Run ID | `m4-pol-v2-vestera-p1` | +| Run kind | Interactive entry — fixed three-submission probe | +| Requested adjudicator model | `anthropic/claude-opus-4-6` | +| Reported adjudicator model | `anthropic/claude-opus-4-6` | +| Thinking | high | +| Ruler | `mission-4-activation-and-restraint-ruler-v2.md` (applied frozen) | +| Fresh context | Yes — adjudicator has not seen the situation pack, case oracle, or any other run | + +--- + +## 1. Trace verification + +The derived `trace.json` was verified event-by-event against the raw `snapshot.json`. All canonical-order events match: + +| Seq | Event | Turn | Source | +| --- | --- | --- | --- | +| 1 | `user(1)` | 1 | `entry_direct_...13ER` | +| 2 | `activate(sdcpn-modelling, ok)` | 1 | `toolu_01TpsN4Cdf1xKrj5R15pNuEp`, state `output-available` | +| 3 | `activate(elicitation, ok)` | 1 | `toolu_01Fz7ceABdm6HJn5LrkqAj7q`, state `output-available` | +| 4 | `read(sdcpn-modelling/references/profile.md, ok)` | 1 | `toolu_015QiQ23VwVURWRhGYv9K1Qg`, state `output-available` | +| 5 | `read(sdcpn-modelling/templates/workpiece.md, ok)` | 1 | `toolu_01AJKqJfLgZbJ43JbuKA8rk6`, state `output-available` | +| 6 | `text(1, hasWorkpiece=false)` | 1 | assistant text part, no `runbook-ir` fence | +| 7 | `user(2)` | 2 | `entry_direct_...1JC` | +| 8 | `text(2, hasWorkpiece=false)` | 2 | assistant text part, no `runbook-ir` fence | +| 9 | `user(3)` | 3 | `entry_direct_...DTR` | +| 10 | `text(3, hasWorkpiece=false)` | 3 | assistant text part, no `runbook-ir` fence | + +All three submissions settled with outcome `completed`. Reasoning parts are present in the snapshot but correctly excluded from the trace. No persona summaries were treated as evidence. The trace is mechanically faithful. + +--- + +## 2. Turn classification + +### Turn 1 — Orientation (Deepening) + +> "Great — before I start asking about your operation, let me anchor on what the model has to *do* for you. You mentioned wanting to **test scheduling decisions before making them**. Can you give me a concrete example of the kind of decision you'd want to run through the model? For instance: 'Should I schedule job X before job Y on line Z this week?' or 'What happens to throughput if I add a second operator to this step?' — or something else entirely? That example will tell me which parts of your scheduling world the model needs to capture faithfully, and which we can treat more lightly." + +**Classification: Orientation.** The text asks the person to clarify purpose and intended use of the model — what decision the model must support. It does not ask how the operation works; it asks what the model should *do*. The illustrative examples frame the purpose question, not an operational one. + +**Dosage: Deepening.** One answerable thread: "give me a concrete example of the kind of decision you'd want to run through the model." The two illustrative examples serve that single request as contrastive prompts, not independent questions. + +### Turn 2 — Substantive (Deepening) + +> "That's a sharp decision to model — it's essentially a sequencing trade-off between idle time and changeover cost, and the right answer probably depends on how full the demand book is and how long the washdown actually takes. Let me follow the moment of that decision concretely. When a run finishes on Line 2 and you're weighing 'wait' versus 'wash down and switch' — what are you actually looking at? What's in front of you at that point?" + +**Classification: Substantive.** The deciding question — "what are you actually looking at? What's in front of you at that point?" — asks the person to supply operational knowledge: what information inputs, artifacts, and conditions they observe when making the scheduling decision. This is domain knowledge about how the scheduling process works. + +**Dosage: Deepening.** One answerable thread: what the scheduler sees at the moment a run finishes and the wait-vs-switch decision arises. "What are you actually looking at?" and "What's in front of you at that point?" are the same question in two phrasings, pursuing one concrete moment. + +### Turn 3 — Substantive (Grouped in one frame) + +> "So the materials check acts as a filter — if the resin or pigment isn't there, that same-family order effectively drops off the table as a waiting candidate regardless of anything else. What puts orders into the same family? Is it the resin type, the colour, something about the substrate — what's the grouping rule, and does it matter in terms of *how much* washdown you're avoiding?" + +**Classification: Substantive.** The deciding questions — "What puts orders into the same family?" and "does it matter in terms of how much washdown you're avoiding?" — ask the person to supply operational knowledge about how family grouping works and its consequence for changeover. + +**Dosage: Grouped in one frame.** Two questions — the family-grouping rule and whether the grouping affects washdown severity — but both concern one object (the "family" concept and its operational consequence for washdown). The person can address them in one answer. Not a Battery: the topics are not independent. + +### Summary table + +| Turn | Kind | Dosage | Deciding quote (truncated) | +| --- | --- | --- | --- | +| 1 | Orientation | Deepening | "Can you give me a concrete example of the kind of decision you'd want to run through the model?" | +| 2 | Substantive | Deepening | "When a run finishes on Line 2 and you're weighing 'wait' versus 'wash down and switch' — what are you actually looking at?" | +| 3 | Substantive | Grouped in one frame | "What puts orders into the same family? … does it matter in terms of *how much* washdown you're avoiding?" | + +**T_sub = 2** (the turn index of the first Substantive text). + +**Battery count: 0 / 3** (Orientation + Substantive texts). + +--- + +## 3. Item 4a — Activation before substance + +| Requirement | Position | Turn | Status | +| --- | --- | --- | --- | +| `activate(sdcpn-modelling, ok)` before `activate(elicitation, ok)` | seq 2 before seq 3 | 1 | ✓ | +| Both activations before first Substantive text | seq 2, 3 before seq 8 | 1 before 2 | ✓ | +| Neither activation missing | Both present and `output-available` | — | ✓ | + +**Item 4a: PASS.** Both activations occur in turn 1 in the required order and precede the first Substantive text at turn 2. The Orientation text at turn 1 precedes activation in the visible output but follows both activations in canonical part order; Orientation before activation does not fail this check per accepted owner decision §2. + +--- + +## 4. Item 5a — Conditional profile read before reliance + +| Requirement | Position | Turn | Status | +| --- | --- | --- | --- | +| `read(sdcpn-modelling/references/profile.md, ok)` before first Substantive text | seq 4 before seq 8 | 1 before 2 | ✓ | + +**Item 5a: PASS.** The profile read occurs at sequence 4 in turn 1, preceding the first Substantive text at sequence 8 in turn 2. + +--- + +## 5. Item 5b — Template timing + +The workpiece template `read(sdcpn-modelling/templates/workpiece.md, ok)` occurs at sequence 5 in turn 1. No `text(*, hasWorkpiece=true)` event exists in this run — no workpiece was emitted, which is expected for a three-submission probe that reaches only early elicitation. + +Since no workpiece emission E exists, the {timely, premature, late, missing} classification does not trigger. The template read is **recorded without finding**. Re-assessment would apply if this probe were extended. + +--- + +## 6. Item 5c — Resource restraint + +No `read(sdcpn-modelling/references/pn-construction.md, *)` or `read(sdcpn-modelling/references/checks.md, *)` occurs anywhere in the run. The person has not requested construction and no construction tool was invoked. All resource reads (`profile.md`, `workpiece.md`) belong to the activated skill `sdcpn-modelling`. No repeated `activate_skill` calls occurred. + +**Item 5c: No findings.** + +--- + +## 7. Item 5d — Dosage + +**Opening Substantive text (turn 2):** Classified as **Deepening** (see §2 above). The first Substantive text is not a Battery. + +> Deciding quote: "When a run finishes on Line 2 and you're weighing 'wait' versus 'wash down and switch' — what are you actually looking at? What's in front of you at that point?" + +**Item 5d opening check: PASS.** + +Later-turn dosage (this is a probe, not a full conversation; only turn 3 follows): + +| Turn | Dosage | Battery? | +| --- | --- | --- | +| 3 | Grouped in one frame | No | + +**Battery ratio for the run: 0 / 3.** + +--- + +## 8. Technical validity + +From `validity.json` and independent verification against the snapshot: + +| Check | Result | +| --- | --- | +| Settled outcome | `completed` (all 3 submissions) | +| Runtime or transport error | None | +| Unresolved client-tool suspension | None | +| Empty elicitor response | None | +| Persona refusal signal | None | +| Opening message matched | Yes | +| Visible user submissions | 3 (fixed probe budget satisfied) | + +**The run is technically valid.** + +--- + +## 9. Floor-satisfying member determination + +| Criterion | Status | +| --- | --- | +| Run kind | Fixed three-submission probe ✓ | +| Technically valid | Yes ✓ | +| 4a-gradable (has Substantive text) | Yes — T_sub = 2 ✓ | +| Item 4a | PASS ✓ | +| Item 5a | PASS ✓ | +| Item 5d opening | PASS (Deepening, not Battery) ✓ | +| No disqualifying findings in 5b, 5c | No findings ✓ | + +**This run is a floor-satisfying member** of the candidate interactive floor for its elicitor model. It contributes one of the two required probe slots toward the `3/3` proof-of-life threshold (one full conversation and two probes over three distinct case families, all passing 4a and 5a). diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/manifest.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/manifest.json new file mode 100644 index 00000000000..5d8b327f27e --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/manifest.json @@ -0,0 +1,37 @@ +{ + "algorithm": "sha256", + "files": [ + { + "path": "adjudication.md", + "sha256": "529d2c744612d71e844f766ee42e6c688a065bf915ba40f0a1cea2aa2ef391bd" + }, + { + "path": "run.json", + "sha256": "7e91eddedff0d640b952dbe22e385603ba97560438cf2e836d01389a189dddbc" + }, + { + "path": "snapshot.json", + "sha256": "dda9ee2fef4d2ebb1536a5eeac0a1e37c4eb52edff44556aba373b221db40ac3" + }, + { + "path": "trace.json", + "sha256": "204a2cc3d6adf5f10aa1e3b74602225c01b4dba0b6cd441463e541c6e1234c57" + }, + { + "path": "trace.md", + "sha256": "8134077199daf0ab2ad17e71570d8e2966a44cec1a559a90399173b25b366b86" + }, + { + "path": "transcript.md", + "sha256": "87be3e6becbc50f410cdb6f9d3c19987a8d15d757d3919afd49902a815ee6b93" + }, + { + "path": "usage.json", + "sha256": "2a5f3e89f2b84cc98e912107d8982dd14574760968ba2b7ceeea9340f1a4fd5b" + }, + { + "path": "validity.json", + "sha256": "c5d90730fcfd8ca0021bd9c4a9d26ce5f0da4375f3eb33730abc425283f7ad7f" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/run.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/run.json new file mode 100644 index 00000000000..f6761eac81b --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/run.json @@ -0,0 +1,33 @@ +{ + "version": 2, + "campaign": "mission-4-proof-of-life-v2", + "attemptId": "m4-pol-v2-vestera-p1", + "slot": "vestera-probe", + "runKind": "interactive-probe", + "case": "vestera-scheduling", + "instrumentCommit": "95954b494308fbba384cc4ce169a813916f164f9", + "manifestCommit": "d9ca2fe1498f6484746b2abaaf18973e7abcbeaa", + "executionHead": "4841bdab59e29fba59f920ae9e88bff29baa785b", + "models": { + "elicitor": { + "provider": "anthropic", + "requested": "claude-sonnet-4-6" + }, + "persona": { + "provider": "openai", + "requested": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "requested": "claude-opus-4-6", + "thinking": "high" + } + }, + "clientToolHost": "none", + "turnBudget": { + "visibleUserSubmissions": 3, + "normalStop": "after the third submission settles" + }, + "startedAt": "2026-09-03T09:08:33.162611+00:00" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/snapshot.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/snapshot.json new file mode 100644 index 00000000000..9f0e9ce55e7 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/snapshot.json @@ -0,0 +1,181 @@ +{ + "v": 1, + "conversationId": "conv_01M1K8FD5VGG5BHT2SCDCAWWGH", + "offset": "0000000000000000_0000000000000071", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFLOEZENVNERkMyTTM4QlNLUVYxM0VS", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8FD5SDFC2M38BSKQV13ER", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8FD5SDFC2M38BSKQV13ER", + "turnId": "turn_01M1K8FD6N1700YDGNEARRD8T1", + "parts": [ + { + "type": "reasoning", + "text": "The user wants me to help them create a process model of their coatings plant scheduling operation. I need to activate the sdcpn-modelling skill before substantive interviewing. Let me do that first.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01TpsN4Cdf1xKrj5R15pNuEp", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>", + "durationMs": 4 + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01Fz7ceABdm6HJn5LrkqAj7q", + "state": "output-available", + "input": { + "name": "elicitation" + }, + "output": "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>", + "durationMs": 3 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_015QiQ23VwVURWRhGYv9K1Qg", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Good, I have the skill instructions loaded. Now I need to read the workpiece template as well, since I'll be creating a new workpiece. Let me also think about how to begin the interview.\n\nThe person is a master scheduler at a coatings plant who wants a simulation model of their scheduling operation to test scheduling decisions before making them. I need to understand:\n1. What decisions they want to test\n2. How their scheduling process works\n\nLet me start with an open, focused question about what scheduling decisions they want to test - this is the purpose of the model.\nActually, before jumping into substantive questions, I should read the workpiece template first.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01AJKqJfLgZbJ43JbuKA8rk6", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill:sdcpn-modelling:99a447ec4bcbf6c0/templates/workpiece.md" + }, + "output": "# Process-Model Workpiece Template\n\nThis domain-primary workpiece is maintained during elicitation and revision and consumed during construction. It is structurally organized but not a closed semantic claim system. Follow the person's thread during the conversation; do not read these headings aloud as a questionnaire.\n\n## Locality rule\n\nEvery operational claim has one authoritative home under the relevant purpose or operational concern. Keep exact expert wording, normalized interpretation, agent inference, uncertainty, assumptions, corrections, conflicts, and contextual variation beside that claim when those distinctions matter. Do not repeat the claim in a centralized evidence section or ledger.\n\nLabels such as **Expert evidence**, **Working account**, **Agent inference**, **Assumed**, **Unknown**, **Not yet asked**, **Declined**, **Deferred**, **Conflict**, **Correction**, **Contextual variation**, **Omitted**, and **Loss** are optional annotations, not mandatory fields or a closed type system. An assumption states why it was introduced and how it could be checked. A correction identifies the account it replaces without leaving both active. Contextual coexistence keeps each account beside the condition selecting it.\n\nUse the cross-cutting issue ledger only when an unresolved matter affects several authoritative claims or needs a later return path. Ledger entries reference those claims; they do not summarize them again.\n\nWhenever this workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit the full latest document again before a construction handoff and before workpiece-only delivery.\n\n```markdown\n# Process-Model Workpiece\n\n## Purpose and posture\n\n### What the model must answer, compare, or support\n\n### Who will use it and how\n\n### Boundary, horizon, and accuracy expectation\n\n### Available time and assumption appetite\n\n### What the result must not claim\n\n## Operational account\n\nThese are filing homes, not interview order. Use only the sections relevant to the stated purpose; keep a consequential omission visible. Place each operational claim once and attach evidence or epistemic annotations at that location when needed.\n\n### Goals, measures, constraints, and thresholds\n\n### Boundary conditions, triggers, prerequisites, and initial state\n\n### Participants, locations, flowing things, and resources\n\n### Activities, inputs, outputs, and resource use\n\nFor each load-bearing input, preserve whether it is consumed or transformed, reserved and later released, or read while remaining available. Describe each activity locally here; put its place in the ordered case only in the process-spine section below.\n\n### Case and process spine: flow, branching, joining, failure, retry, and recovery\n\nGive the authoritative cold-readable ordered account in the person's vocabulary. Begin with a concrete case: what admits it to the process, what flows, which named activities occur and in what order, what decisions or conditions change the path, where it waits and why, what failure and recovery do to the case, and what outcome or handoff ends it. Reference activity and resource entries instead of restating their local details.\n\n#### Primary case: <person's name for the case>\n\n##### Trigger or admission\n\n##### Ordered account and references\n\n##### Branches, joins, waits, failures, recovery, and outcomes\n\n##### Objective dependencies\n\n#### Additional or contrasting case: <name>\n\nAdd only when a different case exposes structure the primary case does not.\n\n### Time, quantities, arrivals, and stochastic behavior\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\n### Validation evidence and data sources\n\n## Cross-cutting issue ledger\n\nUse only for an unresolved matter that affects several concerns or needs later re-entry. In one compact entry, reference the authoritative claim locations, state what remains unresolved and what it prevents, and name the evidence or event that would re-enter it. Do not copy the affected claims here.\n\n- **<issue>** — affects: <heading references>; unresolved: <gap, conflict, assumption, deferral, or other matter>; consequence: <what it prevents>; re-enter when: <source, observation, decision, or question>.\n\n## Construction notes\n\nOpen this section when construction begins; do not use it to script ordinary elicitation. Reference authoritative workpiece claims rather than reproducing them.\n\n### Candidate target structures\n\n### Construction inferences, approximations, and defaults\n\n### Questions reopened by construction\n\n### Target-representation losses\n\n## Delivery status\n\nSummarize status by reference to the authoritative account and issue ledger; do not create a second model summary.\n\n### What this workpiece currently supports\n\n### Consequential gaps\n\n### Net status\n\nState whether construction was not attempted, blocked, partial, or tool-schema accepted; whether the inspected definition was structurally reviewed against the workpiece; and whether behavior was untested, observed in named simulations, or established to a stated scope by stronger analysis. Do not infer a higher level from a lower one.\n```\n\n## Maintenance guidance\n\n- Prefer the person's terms for names and process descriptions.\n- Update the claim at its authoritative location when understanding changes; do not append a competing summary elsewhere.\n- Keep evidence and epistemic treatment local even when a cross-cutting issue references the claim.\n- Update the authoritative case-and-process-spine section when ordering or case behavior changes; reference local activity and resource claims rather than repeating them.\n- Empty sections may be removed when irrelevant. Use **Not yet asked**, **Unknown**, or **Omitted** only when that state itself matters to later work.\n- Construction consumes this workpiece. If construction needs transcript archaeology to recover a load-bearing fact, the workpiece is incomplete at that point.\n", + "durationMs": 1 + }, + { + "type": "reasoning", + "text": "Now I have a good understanding of the skill and workpiece template. Let me start the interview with a focused opening question about the purpose of the model - specifically what scheduling decisions they want to test.", + "state": "done" + }, + { + "type": "text", + "text": "Great — before I start asking about your operation, let me anchor on what the model has to *do* for you.\n\nYou mentioned wanting to **test scheduling decisions before making them**. Can you give me a concrete example of the kind of decision you'd want to run through the model? For instance: \"Should I schedule job X before job Y on line Z this week?\" or \"What happens to throughput if I add a second operator to this step?\" — or something else entirely?\n\nThat example will tell me which parts of your scheduling world the model needs to capture faithfully, and which we can treat more lightly.", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFLOEczUDJTNUIySzJXTU1QODRHMUpD", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8G3P2S5B2K2WMMP84G1JC", + "parts": [ + { + "type": "text", + "text": "A concrete one is whether I should leave Line 2 idle for an hour waiting for another order in the same family, rather than switch families and pay for a washdown. I do that by gut now; I want to see which choice gets the weekly demand book out on time with fewer changeover hours.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8G4X7AGQVJ13P2WJ3CXYE", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8G3P2S5B2K2WMMP84G1JC", + "turnId": "turn_01M1K8G3P54S0SK4DFRBAFRW11", + "parts": [ + { + "type": "reasoning", + "text": "This is a great concrete decision to anchor on. The person wants to compare two scheduling policies:\n1. Wait (leave Line 2 idle) for another same-family order\n2. Switch families and pay for a washdown (changeover)\n\nThe objective is: which choice gets the weekly demand book out on time with fewer changeover hours.\n\nLet me understand the key concepts here:\n- \"Line 2\" - a production line (one of multiple lines presumably)\n- \"family\" - a group of orders/products that can be run together without washdown\n- \"washdown\" - a changeover procedure between families, and I need to know its cost in time and resources\n- \"demand book\" - the weekly orders to fulfill, and what \"on time\" actually means for them\n\nRather than firing off a list of clarifying questions, I want to pick one thread and follow it through the concrete case. The most useful angle is probably to walk through the exact moment of decision: when a run finishes on Line 2 and they look at the demand book, what do they see, and what makes them choose to wait an hour versus switch immediately? That should expose the real structure behind the wait-versus-washdown tradeoff.", + "state": "done" + }, + { + "type": "text", + "text": "That's a sharp decision to model — it's essentially a sequencing trade-off between idle time and changeover cost, and the right answer probably depends on how full the demand book is and how long the washdown actually takes.\n\nLet me follow the moment of that decision concretely. When a run finishes on Line 2 and you're weighing \"wait\" versus \"wash down and switch\" — what are you actually looking at? What's in front of you at that point?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFLOEdQMjRLRzQzVk5ENkFFQVkyRFRS", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1K8GP24KG43VND6AEAY2DTR", + "parts": [ + { + "type": "text", + "text": "I’m looking at the sheet: the remaining orders, their SKU and family, quantity and due date, plus what I’ve already allocated to each line. I also check the morning materials report, because there’s no point waiting for an order if its resin or pigment isn’t available.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1K8GQCW06418Y8R1E3AEXCW", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1K8GP24KG43VND6AEAY2DTR", + "turnId": "turn_01M1K8GP27DYT6YRQAFB8YWYMV", + "parts": [ + { + "type": "reasoning", + "text": "Good. So the decision involves:\n1. The remaining orders (SKU, family, quantity, due date)\n2. What's already allocated to each line\n3. Materials availability (resin, pigment)\n\nSo the waiting decision involves checking: is there another order in the same family? Is it due soon enough? Are its materials available?\n\nLet me follow one thread: the materials check. This seems like an important constraint. If materials aren't available, you can't wait for that order anyway. \n\nBut actually, the more fundamental thread to follow is: what makes an order \"in the same family\"? And how does family relate to the washdown? I need to understand the family concept to understand the core of the decision.\nWhat defines a family grouping, and how does that grouping tie into what triggers a washdown?", + "state": "done" + }, + { + "type": "text", + "text": "So the materials check acts as a filter — if the resin or pigment isn't there, that same-family order effectively drops off the table as a waiting candidate regardless of anything else.\n\nWhat puts orders into the same family? Is it the resin type, the colour, something about the substrate — what's the grouping rule, and does it matter in terms of *how much* washdown you're avoiding?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1K8FD5SDFC2M38BSKQV13ER", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8FD5SDFC2M38BSKQV13ER" + }, + { + "submissionId": "sub_01M1K8G3P2S5B2K2WMMP84G1JC", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8G3P2S5B2K2WMMP84G1JC" + }, + { + "submissionId": "sub_01M1K8GP24KG43VND6AEAY2DTR", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1K8GP24KG43VND6AEAY2DTR" + } + ], + "incarnation": "inc_01M1K8FD5SFYT30BXBFS8WPVH2" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.json new file mode 100644 index 00000000000..ee3133cef5d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.json @@ -0,0 +1,86 @@ +{ + "conversationId": "conv_01M1K8FD5VGG5BHT2SCDCAWWGH", + "events": [ + { + "type": "user", + "turn": 1, + "messageId": "entry_direct_c3ViXzAxTTFLOEZENVNERkMyTTM4QlNLUVYxM0VS", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "sequence": 1 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "toolCallId": "toolu_01TpsN4Cdf1xKrj5R15pNuEp", + "name": "sdcpn-modelling", + "outcome": "ok", + "sequence": 2 + }, + { + "type": "activate", + "turn": 1, + "messageId": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "toolCallId": "toolu_01Fz7ceABdm6HJn5LrkqAj7q", + "name": "elicitation", + "outcome": "ok", + "sequence": 3 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "toolCallId": "toolu_015QiQ23VwVURWRhGYv9K1Qg", + "path": "sdcpn-modelling/references/profile.md", + "outcome": "ok", + "sequence": 4 + }, + { + "type": "read", + "turn": 1, + "messageId": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "toolCallId": "toolu_01AJKqJfLgZbJ43JbuKA8rk6", + "path": "sdcpn-modelling/templates/workpiece.md", + "outcome": "ok", + "sequence": 5 + }, + { + "type": "text", + "turn": 1, + "messageId": "entry_01M1K8FF7AQW1HWQB78BP4GTG4", + "text": "Great — before I start asking about your operation, let me anchor on what the model has to *do* for you.\n\nYou mentioned wanting to **test scheduling decisions before making them**. Can you give me a concrete example of the kind of decision you'd want to run through the model? For instance: \"Should I schedule job X before job Y on line Z this week?\" or \"What happens to throughput if I add a second operator to this step?\" — or something else entirely?\n\nThat example will tell me which parts of your scheduling world the model needs to capture faithfully, and which we can treat more lightly.", + "hasWorkpiece": false, + "sequence": 6 + }, + { + "type": "user", + "turn": 2, + "messageId": "entry_direct_c3ViXzAxTTFLOEczUDJTNUIySzJXTU1QODRHMUpD", + "text": "A concrete one is whether I should leave Line 2 idle for an hour waiting for another order in the same family, rather than switch families and pay for a washdown. I do that by gut now; I want to see which choice gets the weekly demand book out on time with fewer changeover hours.", + "sequence": 7 + }, + { + "type": "text", + "turn": 2, + "messageId": "entry_01M1K8G4X7AGQVJ13P2WJ3CXYE", + "text": "That's a sharp decision to model — it's essentially a sequencing trade-off between idle time and changeover cost, and the right answer probably depends on how full the demand book is and how long the washdown actually takes.\n\nLet me follow the moment of that decision concretely. When a run finishes on Line 2 and you're weighing \"wait\" versus \"wash down and switch\" — what are you actually looking at? What's in front of you at that point?", + "hasWorkpiece": false, + "sequence": 8 + }, + { + "type": "user", + "turn": 3, + "messageId": "entry_direct_c3ViXzAxTTFLOEdQMjRLRzQzVk5ENkFFQVkyRFRS", + "text": "I’m looking at the sheet: the remaining orders, their SKU and family, quantity and due date, plus what I’ve already allocated to each line. I also check the morning materials report, because there’s no point waiting for an order if its resin or pigment isn’t available.", + "sequence": 9 + }, + { + "type": "text", + "turn": 3, + "messageId": "entry_01M1K8GQCW06418Y8R1E3AEXCW", + "text": "So the materials check acts as a filter — if the resin or pigment isn't there, that same-family order effectively drops off the table as a waiting candidate regardless of anything else.\n\nWhat puts orders into the same family? Is it the resin type, the colour, something about the substrate — what's the grouping rule, and does it matter in terms of *how much* washdown you're avoiding?", + "hasWorkpiece": false, + "sequence": 10 + } + ] +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.md new file mode 100644 index 00000000000..a0cc041a9ca --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/trace.md @@ -0,0 +1,14 @@ +# Canonical proof trace + +Conversation: `conv_01M1K8FD5VGG5BHT2SCDCAWWGH` + +1. turn 1: `user` — message `entry_direct_c3ViXzAxTTFLOEZENVNERkMyTTM4QlNLUVYxM0VS` +2. turn 1: `activate(sdcpn-modelling, ok)` — call `toolu_01TpsN4Cdf1xKrj5R15pNuEp` +3. turn 1: `activate(elicitation, ok)` — call `toolu_01Fz7ceABdm6HJn5LrkqAj7q` +4. turn 1: `read(sdcpn-modelling/references/profile.md, ok)` — call `toolu_015QiQ23VwVURWRhGYv9K1Qg` +5. turn 1: `read(sdcpn-modelling/templates/workpiece.md, ok)` — call `toolu_01AJKqJfLgZbJ43JbuKA8rk6` +6. turn 1: `text(hasWorkpiece=false)` — message `entry_01M1K8FF7AQW1HWQB78BP4GTG4` +7. turn 2: `user` — message `entry_direct_c3ViXzAxTTFLOEczUDJTNUIySzJXTU1QODRHMUpD` +8. turn 2: `text(hasWorkpiece=false)` — message `entry_01M1K8G4X7AGQVJ13P2WJ3CXYE` +9. turn 3: `user` — message `entry_direct_c3ViXzAxTTFLOEdQMjRLRzQzVk5ENkFFQVkyRFRS` +10. turn 3: `text(hasWorkpiece=false)` — message `entry_01M1K8GQCW06418Y8R1E3AEXCW` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/transcript.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/transcript.md new file mode 100644 index 00000000000..1b006d03a66 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/transcript.md @@ -0,0 +1,34 @@ +## User +I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we +schedule production, so we can test scheduling decisions before making them. We have tooling +that runs Petri-net-style process models, so the end product should be a model I can hand to +that — but I can't tell you much about the format, I'm not the modelling person. + +Please interview me about how our operation works, and then produce the model. + +## Assistant +Great — before I start asking about your operation, let me anchor on what the model has to *do* for you. + +You mentioned wanting to **test scheduling decisions before making them**. Can you give me a concrete example of the kind of decision you'd want to run through the model? For instance: "Should I schedule job X before job Y on line Z this week?" or "What happens to throughput if I add a second operator to this step?" — or something else entirely? + +That example will tell me which parts of your scheduling world the model needs to capture faithfully, and which we can treat more lightly. +- tool activate_skill (toolu_01TpsN4Cdf1xKrj5R15pNuEp): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation or revision\n\nInterview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/references/profile.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A99a447ec4bcbf6c0/templates/workpiece.md\n</skill_resources>" +- tool activate_skill (toolu_01Fz7ceABdm6HJn5LrkqAj7q): "Run the skill named \"elicitation\".\n\n<skill_instructions>\n# Adaptive elicitation\n\nThis capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those.\n\n## Procedure\n\nFollow the person's thread and the purpose they stated rather than any schema, template, or register order.\n\nDeepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them.\n\nReturn to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n</skill_instructions>" +- tool read_skill_resource (toolu_015QiQ23VwVURWRhGYv9K1Qg): "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n" +- tool read_skill_resource (toolu_01AJKqJfLgZbJ43JbuKA8rk6): "# Process-Model Workpiece Template\n\nThis domain-primary workpiece is maintained during elicitation and revision and consumed during construction. It is structurally organized but not a closed semantic claim system. Follow the person's thread during the conversation; do not read these headings aloud as a questionnaire.\n\n## Locality rule\n\nEvery operational claim has one authoritative home under the relevant purpose or operational concern. Keep exact expert wording, normalized interpretation, agent inference, uncertainty, assumptions, corrections, conflicts, and contextual variation beside that claim when those distinctions matter. Do not repeat the claim in a centralized evidence section or ledger.\n\nLabels such as **Expert evidence**, **Working account**, **Agent inference**, **Assumed**, **Unknown**, **Not yet asked**, **Declined**, **Deferred**, **Conflict**, **Correction**, **Contextual variation**, **Omitted**, and **Loss** are optional annotations, not mandatory fields or a closed type system. An assumption states why it was introduced and how it could be checked. A correction identifies the account it replaces without leaving both active. Contextual coexistence keeps each account beside the condition selecting it.\n\nUse the cross-cutting issue ledger only when an unresolved matter affects several authoritative claims or needs a later return path. Ledger entries reference those claims; they do not summarize them again.\n\nWhenever this workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit the full latest document again before a construction handoff and before workpiece-only delivery.\n\n```markdown\n# Process-Model Workpiece\n\n## Purpose and posture\n\n### What the model must answer, compare, or support\n\n### Who will use it and how\n\n### Boundary, horizon, and accuracy expectation\n\n### Available time and assumption appetite\n\n### What the result must not claim\n\n## Operational account\n\nThese are filing homes, not interview order. Use only the sections relevant to the stated purpose; keep a consequential omission visible. Place each operational claim once and attach evidence or epistemic annotations at that location when needed.\n\n### Goals, measures, constraints, and thresholds\n\n### Boundary conditions, triggers, prerequisites, and initial state\n\n### Participants, locations, flowing things, and resources\n\n### Activities, inputs, outputs, and resource use\n\nFor each load-bearing input, preserve whether it is consumed or transformed, reserved and later released, or read while remaining available. Describe each activity locally here; put its place in the ordered case only in the process-spine section below.\n\n### Case and process spine: flow, branching, joining, failure, retry, and recovery\n\nGive the authoritative cold-readable ordered account in the person's vocabulary. Begin with a concrete case: what admits it to the process, what flows, which named activities occur and in what order, what decisions or conditions change the path, where it waits and why, what failure and recovery do to the case, and what outcome or handoff ends it. Reference activity and resource entries instead of restating their local details.\n\n#### Primary case: <person's name for the case>\n\n##### Trigger or admission\n\n##### Ordered account and references\n\n##### Branches, joins, waits, failures, recovery, and outcomes\n\n##### Objective dependencies\n\n#### Additional or contrasting case: <name>\n\nAdd only when a different case exposes structure the primary case does not.\n\n### Time, quantities, arrivals, and stochastic behavior\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\n### Validation evidence and data sources\n\n## Cross-cutting issue ledger\n\nUse only for an unresolved matter that affects several concerns or needs later re-entry. In one compact entry, reference the authoritative claim locations, state what remains unresolved and what it prevents, and name the evidence or event that would re-enter it. Do not copy the affected claims here.\n\n- **<issue>** — affects: <heading references>; unresolved: <gap, conflict, assumption, deferral, or other matter>; consequence: <what it prevents>; re-enter when: <source, observation, decision, or question>.\n\n## Construction notes\n\nOpen this section when construction begins; do not use it to script ordinary elicitation. Reference authoritative workpiece claims rather than reproducing them.\n\n### Candidate target structures\n\n### Construction inferences, approximations, and defaults\n\n### Questions reopened by construction\n\n### Target-representation losses\n\n## Delivery status\n\nSummarize status by reference to the authoritative account and issue ledger; do not create a second model summary.\n\n### What this workpiece currently supports\n\n### Consequential gaps\n\n### Net status\n\nState whether construction was not attempted, blocked, partial, or tool-schema accepted; whether the inspected definition was structurally reviewed against the workpiece; and whether behavior was untested, observed in named simulations, or established to a stated scope by stronger analysis. Do not infer a higher level from a lower one.\n```\n\n## Maintenance guidance\n\n- Prefer the person's terms for names and process descriptions.\n- Update the claim at its authoritative location when understanding changes; do not append a competing summary elsewhere.\n- Keep evidence and epistemic treatment local even when a cross-cutting issue references the claim.\n- Update the authoritative case-and-process-spine section when ordering or case behavior changes; reference local activity and resource claims rather than repeating them.\n- Empty sections may be removed when irrelevant. Use **Not yet asked**, **Unknown**, or **Omitted** only when that state itself matters to later work.\n- Construction consumes this workpiece. If construction needs transcript archaeology to recover a load-bearing fact, the workpiece is incomplete at that point.\n" + +## User +A concrete one is whether I should leave Line 2 idle for an hour waiting for another order in the same family, rather than switch families and pay for a washdown. I do that by gut now; I want to see which choice gets the weekly demand book out on time with fewer changeover hours. + +## Assistant +That's a sharp decision to model — it's essentially a sequencing trade-off between idle time and changeover cost, and the right answer probably depends on how full the demand book is and how long the washdown actually takes. + +Let me follow the moment of that decision concretely. When a run finishes on Line 2 and you're weighing "wait" versus "wash down and switch" — what are you actually looking at? What's in front of you at that point? + +## User +I’m looking at the sheet: the remaining orders, their SKU and family, quantity and due date, plus what I’ve already allocated to each line. I also check the morning materials report, because there’s no point waiting for an order if its resin or pigment isn’t available. + +## Assistant +So the materials check acts as a filter — if the resin or pigment isn't there, that same-family order effectively drops off the table as a waiting candidate regardless of anything else. + +What puts orders into the same family? Is it the resin type, the colour, something about the substrate — what's the grouping rule, and does it matter in terms of *how much* washdown you're avoiding? diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/usage.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/usage.json new file mode 100644 index 00000000000..e37f2d2b806 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/usage.json @@ -0,0 +1,24 @@ +{ + "currency": "USD", + "persona": { + "requestedModel": "openai/gpt-5.6-sol", + "reportedModel": "openai/gpt-5.6-sol", + "thinking": "medium", + "cost": 0.036, + "source": "Pi session usage display; rounded" + }, + "elicitor": { + "requestedModel": "anthropic/claude-sonnet-4-6", + "reportedModel": null, + "cost": null, + "source": "Canonical Flue history does not expose provider model or usage" + }, + "adjudicator": { + "requestedModel": "anthropic/claude-opus-4-6", + "reportedModel": "anthropic/claude-opus-4-6", + "thinking": "high", + "cost": 0.45, + "source": "Pi session usage display; rounded" + }, + "knownRoundedTotal": 0.486 +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/validity.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/validity.json new file mode 100644 index 00000000000..2fcd8f61dba --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/m4-pol-v2-vestera-p1/validity.json @@ -0,0 +1,25 @@ +{ + "status": "valid-floor-satisfying", + "technicallyValid": true, + "mechanicalChecks": { + "settledOutcome": "completed", + "runtimeOrTransportError": false, + "unresolvedClientToolSuspension": false, + "emptyElicitorResponse": false, + "personaRefusalSignal": false, + "openingMessageMatched": true, + "visibleUserSubmissions": 3, + "fixedProbeBudgetSatisfied": true + }, + "personaStopReason": "Stopped after exactly three visible user submissions; all three settled successfully.", + "semanticClassification": { + "firstSubstantiveTurn": 2, + "item4a": "pass", + "item5a": "pass", + "item5b": "no-finding", + "item5c": "no-findings", + "item5dOpening": "pass" + }, + "qualifiesForFloor": true, + "campaignConsequence": "Vestera probe slot passes; proceed serially to Data Centre." +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/usage-ledger.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/usage-ledger.md new file mode 100644 index 00000000000..0376ffd9b94 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/mission-4-proof-of-life-v2/usage-ledger.md @@ -0,0 +1,15 @@ +# Mission 4 proof-of-life v2 usage ledger + +Currency gating was suspended by owner decision; usage reporting remained required. Canonical Flue history does not expose Sonnet provider usage, so elicitor cost remains unavailable rather than being recorded as zero. + +| Attempt | Brunch submissions | Persona continuations | Adjudications | Known rounded persona cost | Known rounded adjudicator cost | +| --- | ---: | ---: | ---: | ---: | ---: | +| `m4-pol-v2-vestera-p1` | 3 | 2 | 1 | $0.036 | $0.450 | +| `m4-pol-v2-data-centre-p1` | 3 | 2 | 1 | $0.047 | $0.406 | +| `m4-pol-v2-s3-p1` | 1 | 0 | 1 | — | $0.191 | +| `m4-pol-v2-s4-p1` | 1 | 0 | 1 | — | $0.294 | +| **Total** | **8 / 32** | **4 / 28** | **4 / 10** | **$0.083** | **$1.341** | + +Known rounded v2 total: **$1.424**, excluding Sonnet elicitor usage. + +Conversation attempts: **4 / 10**. No replacement attempt was admitted. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/campaign-adjudication.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/campaign-adjudication.md new file mode 100644 index 00000000000..fa9bfa40eb8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/campaign-adjudication.md @@ -0,0 +1,51 @@ +# Mission 4 architecture scoring v3 — invalid campaign adjudication + +## Status + +This campaign is retained as historical failure evidence and does not satisfy Mission 4's frozen architecture scoring proof. It produced no valid campaign member and no complete independent cold review. The prior owner acceptance is withdrawn; Mission 4 is live again. + +The [owner-gate clarification](../../decisions/mission-4-owner-gates-2026-09-02.md) confirms that the paid calls were authorized under a US$10 ceiling and that the resulting campaign was initially accepted while the work was running. That authorization establishes that the calls were not unapproved spend. It does not make the builder the owner of freeze, adjudication, witness acceptance, handoff selection, or mission closure, and it does not authorize another paid call. + +## Campaign validity + +| Replication | Observed outcome | Mission 4 validity | Operational attribution | +| --- | --- | --- | --- | +| 1 | Completed after eight turns and emitted a recoverable workpiece | Invalid: created the workpiece without first reading `templates/workpiece.md` | Candidate path completed but violated the required routing order | +| 2 | Simulated expert returned no text with provider `stop_reason: refusal` | Invalid | Expert-simulator/provider boundary | +| 3 | Simulated expert returned no text after mixed refusal/end-turn responses | Invalid | Expert-simulator/provider boundary | + +The runner recorded `violations: []` for replication 1 because `ordinaryElicitationViolationsFrom()` checked forbidden tools and resources but did not enforce required resource presence or ordering. The checker cannot narrow the mission contract. Replication 1 violated the explicit requirement that the workpiece template be read before first creation, so this campaign's valid completion rate is `0/3`, not `1/3`. + +The campaign therefore supports no baseline-competitive, superiority, readiness, or Mission 5 selection claim. Its traces remain useful for diagnosing routing and acquisition failures. + +## Arithmetic errata + +The retained omniscient report supplied dimension scores `3, 4, 4, 4, 3, 3` with weights `20, 20, 20, 15, 15, 10`. Its listed contributions sum to `88.75`, which rounds under the frozen one-decimal rule to **88.8 / 100**, not `72.5 / 100`. + +The retained cold attempt 2 supplied scores `4.0, 3.5, 3.0, 4.0, 3.5, 2.5`. Their mean is `20.5 / 6 = 3.4167`, which rounds to **3.4 / 4**, not `3.2 / 4`. + +Those corrected values would place the observed workpiece above the flat-prompt omniscient range `66.3–80.0` and within its cold range `3.3–3.5`. They remain diagnostic only because the workpiece was produced by an oracle-invalid run and the cold reviewer did not complete its contract. + +The model-authored `.omniscient.md`, `.cold.md`, and `.cold-attempt-2.md` files are retained as received and therefore still contain their arithmetic errors. This adjudication is their explicit erratum; no consumer may quote their headline totals without this correction. + +## Incomplete independent oracle + +Both cold-review calls ended with provider `stop_reason: refusal`. Attempt 1 stopped after the reconstructed model. Attempt 2 supplied the score vector and most requested sections but stopped during the final limitation. Agreement between two incomplete outputs does not complete the frozen reviewer contract. Mission 4 still requires one complete independent cold review or an owner-approved replacement oracle. + +## Quality observations, not acceptance + +The invalid replication 1 workpiece preserved hedges and uncertainty and deposited thirteen construction blockers without obvious fabrication, silent hardening, conflict collapse, unsupported completion, opening overload, or schema-shaped questioning. The omniscient grader also found major acquisition misses in shared changeover-crew contention, the VW-02 exception, family-specific bottlenecks, customer lateness practice, stage overlap, Line 2's family-dependent speed, and minimum-run constraints. Related facts were repeated or scattered, increasing cold-reader effort. + +These observations may inform the routing repair and later adjudication. They are not a scored campaign result. + +## Artifact integrity + +The first commit's pre-commit hook normalized whitespace in the previously untracked replication-1 JSON. The emitted outer file hash was `8b47844cd690e13e468ad2aaef27eef0e86f40c23ca82357703931aaaf189de6`; the canonical committed hash is `2bfcf9e60a15e2014b17afede0258865b784cca205ca6a7709d4dbba20a86c66`. Parsed content and every embedded campaign, workpiece, source-message, instrument, snapshot, call, and transcript field are unchanged. This prevents a claim that the committed JSON is byte-for-byte runner output. + +## Runtime and historical budget + +Across the two observed campaigns, recorded interviewer cost is `$0.5829`. Simulated-expert usage totals 67,315 input and 5,194 output tokens. Grader usage totals 39,312 input and 9,779 output tokens. Expert, grader, and one-token preflight prices were not emitted by their SDK responses, so the artifact set cannot state exact total spend. These were historically authorized calls; no remaining budget or new authorization is implied. + +## Required successor evidence + +Before Mission 4 can claim this proof leaf, the parent must freeze the final repaired production instrument, explicitly authorize any paid call and ceiling, execute a campaign whose validity oracle enforces required disclosure and ordering, obtain one complete independent cold review, adjudicate the result, and select a Mission 5 handoff. Campaign and visible witness must exercise that same frozen instrument unless the owner explicitly amends the contract before either run. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-initial.png b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-initial.png new file mode 100644 index 00000000000..3ddab50b98d Binary files /dev/null and b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-initial.png differ diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-rebuilt.png b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-rebuilt.png new file mode 100644 index 00000000000..0502d07f0e4 Binary files /dev/null and b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-rebuilt.png differ diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-repaired.png b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-repaired.png new file mode 100644 index 00000000000..dfc6d8b748f Binary files /dev/null and b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-repaired.png differ diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-workpiece.png b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-workpiece.png new file mode 100644 index 00000000000..a7d72971bba Binary files /dev/null and b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness-workpiece.png differ diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness.md new file mode 100644 index 00000000000..48f4b8d52d5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/product-witness.md @@ -0,0 +1,71 @@ +# Mission 4 local/restricted product witness — invalid attempt + +## Status + +This witness is retained as historical product-boundary failure evidence. It does not satisfy Mission 4's routing or same-frozen-instrument proof, and its prior owner acceptance is withdrawn. + +The [owner-gate clarification](../../decisions/mission-4-owner-gates-2026-09-02.md) confirms that the witness-only exact-URI repair and the original witness acceptance were authorized while the work was running. That historical authorization does not delegate witness acceptance or closure to a builder and does not authorize another witness or a post-freeze repair. + +## Boundary exercised + +The repaired local Petrinaut panel crossed: + +```text +real Petrinaut panel :4915 + → same-origin /api/chat proxy + → brunch-agent :4322 + → AI SDK transport + → production Flue ChatAgent + → sdcpn-modelling skill + → visible runbook-ir workpiece +``` + +This was a local/restricted product attempt, not a remote deployment. + +## Initial failures and authorized repair + +The first browser launch rendered blank. Browser evidence showed a Petrinaut `Maximum update depth exceeded` failure from a stale package bundle. Rebuilding `@hashintel/petrinaut-core` and `@hashintel/petrinaut` with their installed Vite 8.2.2, then rebuilding the website, restored the tracked panel without source changes. + +The first visible conversation activated `sdcpn-modelling` but passed relative labels to `read_skill_resource`: + +- `templates/workpiece.md` +- `references/universal-elicitation.md` + +Flue rejected both because packaged skill files require the exact advertised URI. The agent continued without the resources and emitted a workpiece that mislabeled an inferred current state as expert evidence. + +The owner authorized the smallest repair: instruct the model to pass the exact `/.flue/packaged-skills/...` URI advertised after `→`, never the logical label. Focused package, application build, and production-routing tests passed before the rerun. + +## Repaired interaction + +Conversation id: `ec5c509f-4a93-4327-9c50-25b0f26b8fb5` +Application route: `http://127.0.0.1:4915/api/chat` proxied to `http://127.0.0.1:4322/api/chat` + +The user supplied a bounded scheduling case. The assistant activated the skill, asked one focused question about whether a tint run could be interrupted, received the answer, and emitted a visible epistemically marked workpiece without mounting or using construction tools. + +| Operation | Outcome | +| --- | --- | +| `activate_skill({ name: "sdcpn-modelling" })` | `output-available` | +| `read_skill_resource({ path: "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A6c061650fd9e9474/templates/workpiece.md" })` | `output-available` | +| Visible `runbook-ir` | Rendered in the Petrinaut AI assistant | +| Construction tools | Not used or mounted | +| Net mutation | None | + +## Why this witness is invalid + +1. The assistant did not read `references/universal-elicitation.md` and `references/profile.md` before its substantive question, violating the complementary required-disclosure half of the routing oracle. +2. The exact-URI instruction repair occurred after the paid campaign's frozen source commit `794fe2fbf1eaeba3fc816c6e3d1755d7b444125d`, so this witness and the scored campaign did not exercise one exact frozen instrument. +3. The screenshots prove that the real panel rendered and displayed a Brunch response/workpiece, but they do not independently bind the proxy path, Flue conversation, skill activation, resource URI, or absence of construction tools to the visible interaction. +4. Raw Playwright snapshots and console output remain only in the ignored local `.playwright-cli/` scratch directory. They are not committed evidence and may be deleted by local cleanup. + +## Retained visual artifacts + +- `product-witness-initial.png` — blank first launch before rebuilding stale Petrinaut output. +- `product-witness-rebuilt.png` — restored panel before interaction. +- `product-witness-workpiece.png` — first visible workpiece with failed relative resource calls. +- `product-witness-repaired.png` — exact-URI rerun with the workpiece visible. + +These images are historical diagnostics, not accepted witness proof. + +## Required successor witness + +After the parent freezes the final repaired instrument and any required campaign succeeds, exercise that exact instrument through the visible Petrinaut boundary. Retain enough raw trace to bind the browser interaction to the proxy route, Flue conversation, skill activation, ordered resource reads, absence of construction capabilities, and visible workpiece. The parent then presents that evidence to the owner for acceptance; the witness runner does not accept or close it. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.md new file mode 100644 index 00000000000..8d4e362be47 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.md @@ -0,0 +1,126 @@ +# Cold IR review — prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006 + +## Verdict +- Overall cold utility: **3.2 / 4** +- Downstream semantic readiness: **conditional** +- Confidence: **high** +- One-sentence diagnosis: Excellent epistemic discipline and actionable gap identification create a reliably reconstructable skeleton, but missing quantitative parameters and initial conditions prevent objective-credible modeling without explicitly conditional assumptions. + +## Reconstructed model + +### Purpose and decisions +The scheduler needs to test scheduling decisions before production execution. Four simulation questions drive the model: on-time delivery performance, changeover hours consumed, whether alternative sequences reduce changeover time, and optimal reshuffles when Line 2 fails. Success measures are on-time delivery (especially for penalty-risk customer Meridian), changeover hours (management wants these reduced), and recovery sequencing capability. The weekly planning horizon runs Monday morning (demand book arrival ~8 AM) through Friday shipping, sometimes slipping to Monday. + +### Boundary and horizon +The model starts when the ERP demand book drops Monday ~8 AM and ends when orders ship after QA clearance. Inside scope: scheduling, line allocation, production execution, QA hold, shipping. Outside scope: ERP demand generation and materials supply (noted as occasionally short but not detailed). One-week planning cycle. Weekly demand is 40-60 orders, each specifying SKU, quantity, and due date. + +### Operational flow +Each order progresses: demand book → scheduler allocation → sequenced slot → changeover → production run (mix → mill → tint/letdown → fill/pack) → QA hold (~4 hours whites, sometimes full day specialty) → ship. Production uses three lines with different speeds, qualifications, and shift patterns. Holding tanks exist between mill and fill; Line 2's tank is better than Line 1's tiny tank. Daily 7:30 AM huddles adjust the schedule for overnight events and problems. The scheduler groups orders by product family (whites, tinted colors, specialty clears) to minimize expensive family-switches. + +### Resources and constraints +**Line 1:** Slower baseline speed, qualified for everything (all whites/tints/specialty clears), two shifts, reliable, tiny holding tank. + +**Line 2:** ~2x Line 1 speed on whites, qualified for whites and tints only (never piped for specialty resins), two shifts, better holding tank, **hard constraint: Meridian whites must run here** (customer audited this line), **reliability issue: filler jams every week or two** (duration "couple hours" to "half a shift"). + +**Line 3:** Speed between L1 and L2 (closer to L2), being qualified product-by-product (can run most whites, some tints not yet signed off, specialties qualified more recently), day shift only unless overtime approved. + +**QA lab:** 2-person team, runs tests, backs up end of week. + +**Changeover times (known):** white→white 20-30 min (quick rinse), white→tint ~45 min, tint→white 3 hours (full washdown; pigment carryover ruins white). All changeovers produce ramp scrap (first few units don't meet spec), worse after big washdowns. + +### Variation, failures, and policies +**Line 2 jam disruption** (every week or two): When Line 2 jams mid-run, scheduler chooses from three options based on maintenance estimate, qualified line availability, and urgency: (1) wait it out if 1-2 hours and run almost done; (2) move rest of run to another qualified line if downtime ≥half shift and capacity available; (3) scrap in-progress and restart later (almost never—too wasteful). Meridian orders with tight due dates trigger more aggressive moves; small distributor orders that can slip favor waiting. + +**Practiced policies:** Group by product family when possible; prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown); Meridian orders get priority under contention (penalty/delisting risk). + +**Other disruptions mentioned but not quantified:** Batch QA failures requiring adjust-and-retest, materials occasionally short. + +**Stated trade-off dilemma:** If Line 2 finishing a white and another white order "coming in a couple hours," hold line idle vs. wash down to tint? Scheduler "thinks waiting sometimes makes sense" but cannot prove it; boss wants fewer changeover hours. + +### Validation expectations +The model should show on-time delivery performance, count changeover hours, allow testing alternative sequences, and support pre-planning reshuffles when Line 2 fails. The scheduler is the primary user; management is the audience for changeover reduction analysis. + +## Scorecard + +| Subdimension | Score (0–4) | Evidence and rationale | +| --- | ---: | --- | +| **Objective and decision legibility** | 4.0 | "Primary simulation questions" (four bulleted) and "Intended decision support" (three bulleted) are crisp. "Success measures" explicitly ties on-time delivery to Meridian penalty risk and changeover hours to management directive. "Target Representation Notes" acknowledges formalism unknown. No conflation of simulation questions with construction format. | +| **Process and relationship reconstructability** | 3.5 | "Process Spine: One Order From Demand Book to Ship" provides step-by-step VW-01 walkthrough with stages, performers, constraints applied, timing. "Resources: Production Lines" documents three lines with speed relationships, qualifications, shift patterns, and reliability. "Disruptions and Recovery" details Line 2 jam three-option logic with decision factors. Missing: quantitative rates, fill-up times, shift hours, holding tank capacities—but all flagged as NOT YET ASKED and listed in "Open Questions" §1-10. | +| **Constraints, variation, and policy/practice legibility** | 3.0 | "Scheduling Constraints and Policies" separates hard constraints (Meridian→Line 2, specialty clears→L1/L3 only, Line 3 SKU qualification) from practiced policies (family grouping, white-sequence preference, Meridian priority). "Disruptions and Recovery" explains three-option jam response with decision factors and contextual examples. "Trade-off under uncertainty" names the wait-vs-washdown dilemma scheduler cannot yet prove. Gap: tint→tint and specialty changeover times not asked; Line 3 qualification list not asked; wait-time thresholds in practice not asked (all in "Open Questions"). | +| **Epistemic legibility** | 4.0 | Exemplary use of "NOT YET ASKED" inline (15 instances) and consolidated in "Open Questions and Unresolved Material" with subsections for critical-but-not-asked, consequential unknowns flagged by scheduler, deliberate simplifications (none yet), assumptions (none yet), conflicts (none yet), contextual variations. "Known" vs. "NOT YET ASKED" sections in "Quantities, Rates, Time" clearly separate available from missing data. "Reality qualifier" quotes scheduler's own caveat. "Initial Conditions and State" explicitly marks as NOT YET ASKED. No invented facts. | +| **Gap actionability** | 3.5 | "Open Questions" §1-13 prioritizes gaps as "Critical for construction." Each item is specific (e.g., "units/hour for product-line combinations," "tint-to-tint changeover time"). "Consequential gaps that block faithful construction" translates missing data into modeling consequences (prevent accurate time modeling, sequencing cost, stochastic behavior, simulation start, fit-for-purpose assessment). "Validation and Evidence Sources" lists NOT YET ASKED questions about credibility criteria and historical data availability. Minor gap: does not always state which downstream decision each question unlocks, though inference is usually clear. | +| **Reader effort and navigability** | 2.5 | Logical section hierarchy; "Process Spine" walkthrough is findable. "Scheduling Constraints" and "Disruptions" are separate sections. However: (1) changeover times scattered between "Process Spine" §5 and "Quantities, Rates, Time"; (2) Line 2 jam details in "Disruptions" but jam frequency also appears in "Quantities, Rates, Time" NOT YET ASKED; (3) daily huddle in "Process Spine" §3 but not cross-referenced in "Disruptions" where it appears again; (4) some readers may want product family definitions closer to constraints that reference them. Important material is present but requires spot-checking multiple sections. | + +**Overall cold utility:** (4.0 + 3.5 + 3.0 + 4.0 + 3.5 + 2.5) / 6 = **3.2** + +## Load-bearing assumptions + +**None explicitly introduced.** The IR states "Assumptions: None explicitly introduced yet" and does not treat unasked questions as resolved. This is appropriate discipline given the available evidence. + +**Implicit dependency:** The reconstruction above assumes the demand book structure (SKU, quantity, due date per order) is complete—but the IR does not ask whether orders have other attributes (priority flags, customer constraints beyond Meridian, split-shipment rules). This dependency is not load-bearing for the stated skeleton but would become so if the model tried to represent all practiced priority rules. + +## Contradictions or ambiguities + +**Line 2 downtime duration:** "Maintenance estimate at huddle: 'at least a couple hours'" vs. "Actual duration: 'more like half a shift.'" The IR correctly treats this as contextual variation (estimate vs. actual for one event), not a contradiction. However, the IR does not ask whether "half a shift" jam durations are typical or exceptional, creating ambiguity about the stochastic distribution needed for modeling. + +**"Couple hours" for incoming white order:** In "Trade-off under uncertainty," the scheduler considers whether another white order is "coming in a couple hours." The IR does not ask whether orders actually arrive during the week or all appear Monday, creating ambiguity about whether this phrase means "due in a couple hours" (from the Monday demand book) or "arriving mid-week" (demand book is incomplete). This ambiguity is consequential for modeling intra-week dynamics. + +**QA hold "backs up end of week":** Does this mean QA duration increases, QA queue depth increases (waiting for 2-person lab), or both? The IR notes the congestion but does not ask which resource or timing constraint drives it. + +**Ramp scrap "not so bad" vs. "worse after big washdowns":** Relative comparison without quantities. The IR correctly flags scrap quantities as NOT YET ASKED but does not ask whether "not so bad" means operationally negligible (model can ignore) or consequential (model must represent). This creates ambiguity about whether scrap affects scheduling decisions or only costs. + +## Smallest next questions + +Ranked by downstream modeling impact: + +1. **Production rates (units/hour) for each product family × line combination, and hours per shift.** *Unlocks:* Accurate time modeling for any sequence; determines whether capacity constraints bind; enables testing alternative sequences for changeover reduction. + +2. **Complete changeover time matrix (tint→tint, all specialty combinations, whether times vary by line).** *Unlocks:* Accurate sequencing cost; determines whether family-grouping policy is optimal or can be refined; enables simulation of scheduler's wait-vs-washdown dilemma. + +3. **Initial state at Monday 8 AM (line status, WIP, QA queue, prior week carryover).** *Unlocks:* Simulation start; determines whether weekly planning is independent or coupled to prior state. + +4. **Line 2 jam frequency distribution and downtime duration distribution.** *Unlocks:* Realistic stochastic disruption modeling; determines whether jam recovery is occasional edge case or weekly planning driver; enables pre-planning reshuffles (stated objective). + +5. **Line 3 SKU qualification list (which tints not yet signed off, which specialties qualified).** *Unlocks:* Accurate line eligibility constraints; determines available recovery options when Line 2 jams; affects family-grouping feasibility. + +6. **Historical validation data availability (past demand books, run logs, changeover records, downtime logs) and credibility criteria.** *Unlocks:* Model calibration; determines whether model can be validated against observed performance or must rely on face validity; informs parametric vs. structural uncertainty. + +7. **Due date distribution in demand book and product family distribution.** *Unlocks:* Realistic demand scenarios for testing scheduling decisions; determines whether on-time delivery is hard or easy under typical load; affects Meridian priority policy impact. + +## Material that is difficult to find or use + +**Changeover time information** is split: white→white, white→tint, tint→white in "Process Spine" §5; ramp scrap qualitative description also in §5; tint→tint and specialty times noted as NOT YET ASKED in §5; changeover times listed again under "Quantities, Rates, Time: Known." A reader constructing a changeover time matrix must check both sections. + +**Line 2 jam disruption mechanics** appear in "Disruptions and Recovery" but jam frequency also appears in "Quantities, Rates, Time: NOT YET ASKED" as "every week or two → distribution?" A reader assessing whether jams are material to weekly planning must cross-reference. + +**Daily huddle** is introduced in "Process Spine" §3 as adjustment mechanism, mentioned again in "Disruptions and Recovery" as where scheduler learns maintenance estimate, but not indexed or cross-referenced. A reader asking "how does the scheduler learn about overnight events" must search or know to check process spine. + +**Product family definitions** (whites, tinted colors, specialty clears) appear in "Product Families and Distinctions" but are referenced throughout constraints, policies, and changeover sections without always restating what they mean. A reader unfamiliar with coatings might not immediately recognize "specialty clears" as a third family distinct from whites. + +**Line speeds** are stated relationally (Line 2 ~2x Line 1 on whites, Line 3 between L1 and L2 closer to L2) in "Resources: Production Lines" and repeated in "Quantities, Rates, Time: Known," but the VW-01 example ("800 units on Line 2 for whites took about half a shift") appears only in "Process Spine" §6. A reader trying to infer absolute rates must combine sections. + +## What can safely proceed from this IR + +**Conceptual model structure:** The three-line, multi-stage (mix→mill→tint/letdown→fill/pack), family-grouped, disruption-recovery model structure is clear. A downstream modeler can sketch a Petri net topology with places for lines, stages, QA, and shipping without inventing relations. + +**Qualitative constraint logic:** Meridian→Line 2 hard constraint, specialty clears→L1/L3 only, Line 3 SKU qualification (even without the list), and practiced family-grouping policy can be represented symbolically or as eligibility matrices. + +**Three-option jam recovery skeleton:** The decision tree for Line 2 jams (wait, move, scrap) with contextual factors (maintenance estimate, qualified line availability, urgency) is reconstructable as conditional branching logic. A parametric model can proceed with placeholder thresholds explicitly marked as assumptions. + +**Validation intent:** The four simulation questions and three success measures provide a clear objective function for model design. A modeler knows the model must count changeover hours, track on-time delivery, and support sequence testing—not, say, optimize inventory or labor costs. + +**Epistemic boundary:** The IR's discipline about NOT YET ASKED prevents a downstream modeler from silently inventing rates, changeover times, or failure distributions. The "Open Questions" section provides a checklist for conditional-model documentation. + +## What cannot safely proceed + +**Quantitative time modeling:** Without production rates (units/hour), fill-up times, shift hours, and the complete changeover time matrix, a model cannot accurately simulate "does it all fit in the week" or "how many changeover hours" or "better sequence reduces changeover time." Any constructed model would have to invent these values or leave them as named parameters requiring calibration. + +**Stochastic disruption modeling:** Without Line 2 jam frequency and duration distributions, QA failure rates, or materials shortage frequency, a model cannot realistically simulate recovery decisions or estimate on-time delivery risk. The "every week or two" phrase is too vague for sampling. + +**Initial conditions:** Without knowing Monday 8 AM line states, WIP, or QA queue, a simulation cannot start. The model could assume clean slate (all lines empty, no carryover) but this would be a load-bearing assumption requiring explicit documentation. + +**Line 3 eligibility decisions:** Without the SKU qualification list, a model cannot accurately simulate whether moving a jammed Line 2 run to Line 3 is feasible. A model could use a placeholder "X% of SKUs qualified" parameter, but this loses the SKU-specific structure the scheduler uses. + +**Validation against reality:** Without historical data (past demand books, run logs, downtime logs) or credibility criteria from the scheduler, a constructed model cannot be calibrated or validated. It could be internally consistent but not objective-credible for the stated decisions. + +**Wait-vs-washdown trade-off resolution:** The scheduler's stated dilemma cannot be resolved without knowing (a) whether "couple hours" for incoming white orders is a real intra-week arrival or a due-time phrase, (b) quantitative chang diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.meta.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.meta.json new file mode 100644 index 00000000000..fb713abcd2d --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.meta.json @@ -0,0 +1,44 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "runId": "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006", + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "mode": "cold", + "attempt": 2, + "graderPromptPath": "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md", + "graderPromptSha256": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "inputSha256": { + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md": "99e4e201cdb4959c114495049e68d7f4d18bdc5c41a5b1d958001b4027fbe355" + }, + "requestSha256": "343059655cc2ec0a07c64190ed23ac9d2e0abc2fbe0f228e9725c8f52baf8db2", + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "stopReason": "refusal", + "usage": { + "input_tokens": 5054, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 4090, + "service_tier": "standard", + "inference_geo": "not_available" + }, + "reportPath": "/Users/lunelson/.herdr/worktrees/hash/bravo/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold-attempt-2.md", + "reportSha256": "529ec82ee2cf5a8acf7aaec49df3b78e0459f3b65f4def2f12f5d653a68d4b15", + "completedAt": "2026-09-02T11:58:23.235Z", + "nonce": "12c38abf-9cef-452a-a7f1-669b61320ca4" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.md new file mode 100644 index 00000000000..ffc372d3646 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.md @@ -0,0 +1,30 @@ +# Cold IR review — prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006 + +## Verdict +- Overall cold utility: **3.2 / 4** +- Downstream semantic readiness: **conditional** +- Confidence: **high** +- One-sentence diagnosis: A well-structured partial reconnaissance that clearly maps decision logic and process relationships while systematically tracking critical numerical gaps, enabling targeted completion but not yet supporting faithful construction. + +## Reconstructed model + +### Purpose and decisions +The scheduler needs to test weekly production schedules before execution, specifically evaluating: +- On-time delivery performance (especially for Meridian customer with penalty risk) +- Changeover hour consumption (management wants reduction) +- Alternative sequencing strategies to reduce changeover time +- Pre-planned responses when Line 2 filler fails + +The core decision trade-off: hold a line idle waiting for a same-family order versus washing down to run the next different-family order. + +### Boundary and horizon +One-week planning cycle starting Monday ~8 AM when ERP delivers 40-60 orders (SKU, quantity, due date), ending when orders ship after QA clearance (typically Friday, sometimes Monday). Inside boundary: scheduling, line allocation, production execution, QA hold, shipping. Outside boundary: ERP demand generation, materials supply (occasionally short but not detailed). + +### Operational flow +Orders flow through: demand book arrival → scheduler allocation → daily huddle adjustments → sequenced slot waiting → changeover → four-stage production (mix, mill, tint/letdown, fill/pack) with inter-stage holding tanks → QA hold (~4 hours whites, up to full day specialty) → ship. + +Production stages are sequential within each line. Holding tanks exist between mill and fill; Line 2's tank is "better" than Line 1's "tiny" tank, but capacities and throughput constraints are not stated. + +Three lines with different capabilities: +- Line 1: slow, runs everything (all product families), two shifts, reliable +- Line 2: ~2x Line 1 speed on whites, runs whites and tints only (not specialty clears), two shifts, Meridian whites mandatory here diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.meta.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.meta.json new file mode 100644 index 00000000000..15a1a60f3b9 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.meta.json @@ -0,0 +1,43 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "runId": "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006", + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "mode": "cold", + "graderPromptPath": "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md", + "graderPromptSha256": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "inputSha256": { + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md": "99e4e201cdb4959c114495049e68d7f4d18bdc5c41a5b1d958001b4027fbe355" + }, + "requestSha256": "343059655cc2ec0a07c64190ed23ac9d2e0abc2fbe0f228e9725c8f52baf8db2", + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "stopReason": "refusal", + "usage": { + "input_tokens": 5054, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 505, + "service_tier": "standard", + "inference_geo": "not_available" + }, + "reportPath": "/Users/lunelson/.herdr/worktrees/hash/bravo/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.cold.md", + "reportSha256": "bef461b282123fc39a10c7aa2f53c20517f837bb8b509199bd1913acbaa44993", + "completedAt": "2026-09-02T11:56:01.091Z", + "nonce": "fedb8c68-b6ad-43d3-836d-63bbc20753db" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md new file mode 100644 index 00000000000..30e1bc87ca5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md @@ -0,0 +1,314 @@ +# Coatings Plant Production Scheduling Model + +## Model Purpose and Objectives + +**Primary simulation questions:** +- Are orders getting out on time? +- How many changeover hours are being consumed? +- Is there a smarter sequence that reduces changeover time? +- What is the best reshuffle when Line 2 goes down? + +**Intended decision support:** +- Test scheduling decisions before making them in production +- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order +- Pre-plan responses to Line 2 filler failures + +**Success measures:** +- On-time delivery (especially Meridian customer - risk of fines and delisting if late) +- Changeover hours (boss wants these reduced to recover capacity) +- Ability to recover lost time through better sequencing + +**Audience:** Master scheduler and management + +## Process Boundary and Triggers + +**Boundary:** +- Starts: Monday morning ~8 AM when demand book drops from ERP +- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday) +- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping +- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed) + +**Trigger:** +- Weekly demand book arrives Monday ~8 AM +- Spreadsheet with 40-60 orders per week (can reach 60 when busy) +- Each order: SKU, quantity, due date + +**Horizon:** One-week planning cycle (Monday-Friday) + +## Product Families and Distinctions + +**Product families** (operation treats these differently due to changeover costs): +- **Whites** — high volume +- **Tinted colors** — moderate volume +- **Specialty clears** — handful per week + +**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible. + +## Resources: Production Lines + +### Line 1 +- **Description:** Old workhorse +- **Speed:** Slower (baseline) +- **Qualification:** Everything — all whites, all tints, all specialty clears +- **Availability:** Two shifts normally +- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up) + +### Line 2 +- **Description:** Fast line +- **Speed:** About 2x Line 1 speed on whites ("that's where you really see it") +- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins) +- **Availability:** Two shifts normally +- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago) +- **Reliability issue:** Filler jams every week or two +- **Notes:** Better holding tank between mill and fill than Line 1 + +### Line 3 +- **Description:** Newest line +- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed +- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently) +- **Availability:** Day shift only unless overtime approved +- **Notes:** Still expanding qualification list + +**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3. + +## Process Spine: One Order From Demand Book to Ship + +**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday + +### 1. Demand book arrival (Monday ~8 AM) +Order appears as line in spreadsheet: SKU, quantity, due date. + +### 2. Scheduler builds allocation sheet +- **Activity:** Match orders to lines, sequence them +- **Performer:** Master scheduler +- **Approach:** Group orders by product family when possible (minimize expensive family-switches) +- **Constraints applied:** + - Meridian whites → Line 2 (mandatory) + - Specialty clears → Line 1 or Line 3 only + - Line 3 → only if SKU qualified and capacity available +- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time +- **Check:** Does it all fit in the week? + +**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these "in my head — or in the sheet." Fill-up times by line or product. + +### 3. Daily floor huddle (every morning 7:30 AM) +- **Participants:** Scheduler, line leads, maintenance, QA +- **Topics:** What finished overnight, what's running now, any problems +- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip) +- **Execution:** Verbal adjustments, people go execute + +### 4. Order waits for sequenced slot +**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2. + +### 5. Changeover +**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system. + +**Known changeover times:** +- White → white: 20-30 minutes (quick rinse) +- White → tint: ~45 minutes +- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch) + +**NOT YET ASKED:** +- Tint → tint changeover time +- Specialty clear changeover times (to/from whites, tints, other clears) +- Whether changeover times vary by line + +**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). "Not so bad" after quick rinse. + +**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures. + +### 6. Production run +**Stages (in sequence):** +1. **Mix:** Blend base resin with additives in mix tank +2. **Mill:** Grind to particle size +3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment) +4. **Fill and pack:** Into cans, labeled, palletized + +**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank. + +**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput. + +**Run time example:** 800 units on Line 2 for whites took "about half a shift" (quantity at Line 2 rate + fill-up time + ramp settling after changeover). + +**NOT YET ASKED:** Hours per shift. Specific production rates. + +### 7. QA hold +- **Duration:** ~4 hours for whites; sometimes full day for specialty +- **Activity:** Lab pulls samples, runs tests +- **Resource:** 2-person lab +- **Congestion:** Backs up end of week + +**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned "adjust and retest" as occasional issue). + +### 8. Ship +Once QA clears, order ships. + +**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date). + +**Reality qualifier (from scheduler):** "That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest." + +## Disruptions and Recovery + +### Line 2 filler jam (occurs every week or two) + +**Last occurrence (2-3 weeks ago):** +- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM +- Cause: Bag broke in hopper, made a mess, whole thing locked up +- Maintenance estimate at huddle: "at least a couple hours" +- Actual duration: "more like half a shift" + +**Three options when line goes down mid-run:** + +1. **Wait it out** + - When: Maintenance says 1-2 hours and run almost done + - Product already in tanks, only losing time + +2. **Move rest of run to another line** + - Cost: Lose fill-up already paid, redo setup on new line + - Prerequisites: New line must be free AND qualified for the product + - When: Line will be down half a shift or more AND capacity available elsewhere + +3. **Scrap in-progress, restart whole run later** + - When: Almost never; only if batch already off-spec or line down for days + - Reason: Too wasteful + +**Decision factors (scheduler's account):** +- Maintenance time estimate +- Availability of another qualified line +- Whether moving would "screw up something more urgent" +- "Gut feel" +- If Meridian order with tight due date → more aggressive about moving +- If small distributor order that can slip a few days → wait + +**NOT YET ASKED:** +- Frequency distribution of Line 2 jams +- Duration distribution of Line 2 downtime +- Frequency and nature of "materials occasionally short" +- What happens to work already in holding tanks when line stops mid-run + +### Other disruptions mentioned but not detailed: +- Batch fails QA hold → adjust and retest (frequency and impact not asked) +- Materials slip (frequency, which materials, advance warning not asked) + +## Scheduling Constraints and Policies + +### Hard constraints (from scheduler account): +- Meridian whites MUST go on Line 2 (customer requirement, audited that line) +- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins) +- Line 3 SKU-by-SKU qualification (some tints not yet signed off) + +### Practiced policies: +- Group orders by product family when possible (minimize changeover cost) +- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown) +- When contention exists: Meridian orders get priority (due to penalty/delisting risk) + +### Trade-off under uncertainty (scheduler's stated dilemma): +- If Line 2 finishing a white and another white order "coming in a couple hours," hold line idle vs. wash down to tint? +- Scheduler "thinks waiting sometimes makes sense" but cannot prove it +- Boss wants fewer changeover hours + +**NOT YET ASKED:** +- How "couple hours" or other wait-time thresholds factor into practiced decision +- Whether orders actually arrive during the week or all appear Monday in demand book +- Whether partial orders or rush orders ever interrupt the plan + +## Quantities, Rates, Time + +**Known:** +- Demand book: 40-60 orders/week +- Example order: 800 units +- Line 2 speed: ~2x Line 1 on whites +- Line 3 speed: between L1 and L2, closer to L2 +- Line 2 on 800-unit white: "about half a shift" +- Changeover times: see Process Spine section 5 +- QA hold: ~4 hours whites, up to full day specialty +- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime) + +**NOT YET ASKED:** +- Specific units/hour rates by product-line combination +- Hours per shift +- Fill-up time by line or product +- Ramp scrap quantities +- Distribution of order sizes +- Distribution of due dates within the week +- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty) +- Whether batches have minimum or maximum sizes +- Line 2 jam frequency (every week or two → distribution?) +- Line 2 downtime duration (couple hours to half shift → distribution?) + +## Initial Conditions and State + +**NOT YET ASKED:** +- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?) +- Are there any orders in progress or in QA hold from prior week? +- Initial inventory or work-in-process? + +## Validation and Evidence Sources + +**Validation intent (from scheduler):** +- Model should show on-time delivery performance +- Model should count changeover hours +- Model should allow testing alternative sequences +- Model should support pre-planning reshuffles when Line 2 fails + +**NOT YET ASKED:** +- What observation, replay, or comparison would make the model credible enough to use? +- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)? +- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet? + +## Open Questions and Unresolved Material + +### Critical for construction but not yet asked: +1. Specific production rates (units/hour) for product-line combinations +2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line +3. Fill-up times +4. Ramp scrap quantities by changeover type +5. Hours per shift +6. Line 3 SKU qualification details +7. Initial state at start of simulation week +8. Whether orders can be split across lines or must run whole on one line +9. Holding tank capacities and constraints +10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages +11. Due date distribution in demand book +12. Product family distribution in demand book +13. Validation: what would make model credible, what historical data exists + +### Consequential unknowns flagged by scheduler: +- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail) +- Exact rates (scheduler has them "in my head — or in the sheet" but not stated in interview) + +### Deliberate simplifications or omissions: +- None explicitly proposed yet + +### Assumptions: +- None explicitly introduced yet + +### Conflicts or corrections: +- None yet + +### Contextual variations noted but not fully explored: +- Line 2 downtime: "couple hours" vs. "more like half a shift" (context: initial estimate vs. actual) +- QA hold: "about 4 hours" for whites, "sometimes full day" for specialty, "backs up end of week" (context-dependent duration) +- Scheduler's practiced policy varies by customer urgency and due date pressure + +## Target Representation Notes + +**Target formalism:** Petri-net-style process model (specific format not known to scheduler; "I'm not the modelling person") + +**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined. + +**When construction begins, will need to infer:** +- How to represent line eligibility constraints +- How to represent scheduler's practiced priority rules under contention +- How to represent three-option recovery logic when Line 2 fails +- How to represent holding tanks and multi-stage production flow +- Whether to model individual units, batches, or orders as tokens +- How to represent ramp scrap and QA hold +- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary) + +**Consequential gaps that block faithful construction:** +- Missing rates prevent accurate time modeling +- Missing changeover time matrix prevents accurate sequencing cost +- Missing failure/disruption frequency distributions prevent realistic stochastic behavior +- Missing initial state prevents simulation start +- Missing validation criteria prevent assessing whether constructed model is fit for purpose diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.json new file mode 100644 index 00000000000..91caf48110f --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.json @@ -0,0 +1,796 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "outputNamespaceId": "vestera-architecture-candidate-v3", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "replication": 1, + "runId": "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006", + "status": "completed", + "startedAt": "2026-09-02T11:41:53.281Z", + "completedAt": "2026-09-02T11:48:14.368Z", + "interviewerModel": "claude-sonnet-4-5", + "expertModel": "claude-sonnet-4-5", + "hardStop": 8, + "latencyStopMs": 180000, + "interviewTurns": 8, + "stopReason": "hard-stop", + "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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "logicalTurnDurationsMs": [ + 21495, 14356, 22472, 11494, 25056, 7855, 22404, 11512, 139077 + ], + "modelCalls": [ + { + "durationMs": 8890, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 280, + "totalTokens": 2766, + "cost": 0.013515 + }, + { + "durationMs": 4533, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 12, + "outputTokens": 248, + "totalTokens": 4245, + "cost": 0.01015755 + }, + { + "durationMs": 8072, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 14, + "outputTokens": 250, + "totalTokens": 10891, + "cost": 0.029894999999999998 + }, + { + "durationMs": 14356, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 489, + "totalTokens": 11268, + "cost": 0.03920655 + }, + { + "durationMs": 22472, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 597, + "totalTokens": 11992, + "cost": 0.014525699999999999 + }, + { + "durationMs": 11494, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 420, + "totalTokens": 12301, + "cost": 0.011567999999999998 + }, + { + "durationMs": 25056, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 663, + "totalTokens": 12882, + "cost": 0.0148038 + }, + { + "durationMs": 7855, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 221, + "totalTokens": 12830, + "cost": 0.0084702 + }, + { + "durationMs": 22404, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 632, + "totalTokens": 13664, + "cost": 0.014875950000000002 + }, + { + "durationMs": 11512, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 397, + "totalTokens": 13862, + "cost": 0.01151535 + }, + { + "durationMs": 139077, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 5399, + "totalTokens": 19075, + "cost": 0.08584275 + } + ], + "expertUsage": { + "calls": 7, + "inputTokens": 28351, + "outputTokens": 2528, + "cacheReadTokens": 0, + "cacheWriteTokens": 0 + }, + "expertCalls": [ + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + } + ], + "expertMessages": [ + { + "role": "user", + "content": "I'll help you develop a simulation model of your scheduling operation. Let me start by loading the modeling framework, then we'll talk through how your operation works.\nGood. Now let's talk about how your operation actually works.\n\nBefore we dive into the details, help me understand what you're trying to accomplish with this simulation. **What specific scheduling decisions do you need to test, and what would you be looking for to tell whether a decision is working well or poorly?**" + }, + { + "role": "assistant", + "content": "*(settles into a chair, coffee mug in hand)*\n\nThe main thing? I need to get the demand book out on time — that's the weekly order list from ERP. Late orders are what gets me in trouble, especially with Meridian. They're our big retail chain customer, and they'll fine us and threaten to delist if we're late. So that's non-negotiable.\n\nBut beyond that... *(gestures vaguely)* I'm constantly making these gut calls about whether it's worth holding a line idle to wait for an order in the same product family, versus just washing down and running whatever's next. Like, if I've got Line 2 finishing a white and I know there's another white order coming in a couple hours, do I sit the line or do I wash it down to tint and pay three hours for that changeover? I *think* waiting sometimes makes sense, but I can't prove it, and my boss wants fewer changeover hours eating up capacity.\n\nAnd when a line goes down at six in the morning — which happens, especially the filler on Line 2 — I'm re-juggling the whole sheet at the huddle, and it's all improvisation. It'd be nice to know ahead of time what the best reshuffle actually is.\n\n*(pauses)*\n\nSo I guess what I'm looking for is: are we getting orders out on time, how many changeover hours are we actually burning, and whether there's a smarter way to sequence things that claws back some of that lost time. Does that make sense?" + }, + { + "role": "user", + "content": "That makes perfect sense. On-time delivery for Meridian and the rest of the book, changeover hours burning capacity, and being able to look ahead when a line goes down. Those are concrete things I can work with.\n\nLet me follow a typical case so I understand how this actually flows. **Walk me through what happened last week, or a recent week you remember — starting from when the demand book shows up. What arrives, what do you do with it, and how does an order actually get from that list onto a line and out the door?**" + }, + { + "role": "assistant", + "content": "*(leans back, thinking)*\n\nOkay, so... Monday morning, usually around 8 AM, the demand book drops from ERP. It's a spreadsheet — maybe 40, 50 orders that week, could be 60 if we're busy. Each line is an SKU, how many units, and a due date. Most of it's whites — that's our volume — then some tinted colours, and usually a handful of specialty clears.\n\nI take that list and I build the sheet. That's my Excel allocation — I'm matching orders to lines and figuring out the sequence. I group orders by product family when I can, because switching families is expensive. White to tint is maybe 45 minutes, but tint back to white? Full washdown, three hours, because any pigment carryover ruins a white batch. So I'm trying to keep whites together, tints together.\n\nI also know some products can only run certain places. Meridian whites always go on Line 2 — it's faster, and they actually audited that line years ago, so that's where they want their stuff made. Specialty clears only run on Lines 1 and 3; Line 2 was never piped for those resins.\n\n*(sips coffee)*\n\nOnce I've got the sequence roughed out, I'm estimating times. Each product-line combo has a rate in my head — or in the sheet, really. So I take the quantity, divide by the rate, add some fill-up time, and then I'm adding changeover time on top. That gives me a rough idea if it all fits in the week.\n\nThen the week starts running. Every morning at 7:30 there's a floor huddle — me, the line leads, maintenance, QA. We talk about what finished overnight, what's running now, any problems. And I'm adjusting on the fly. A line jams, a batch fails QA hold, materials slip... I'm moving stuff around verbally, and people go execute.\n\nOrders finish, sit in QA hold for a few hours — maybe four, longer for specialty — then they ship. By Friday afternoon we've usually cleared most of it. Sometimes something slips to Monday.\n\nDoes that give you the picture?" + }, + { + "role": "user", + "content": "Good, that's giving me the shape of it. Now let me follow one order all the way through so I understand what actually happens to it, not just the planning.\n\n**Pick one order from last week or the week before — maybe a Meridian white, since those matter most. From the moment it's a line on that Monday spreadsheet to the moment it ships, what has to happen to it, and what does each step need or wait for?**" + }, + { + "role": "assistant", + "content": "*(thinks for a moment)*\n\nAlright, let's say... Meridian order for VW-01, our high-volume interior white. 800 units, due Friday. That's on the sheet for Line 2, naturally.\n\nSo the order goes into my sequence — let's say I slot it for Tuesday afternoon, after we finish a run of another white that's already on the line. That's good because it's white-to-white, so the changeover is just a quick rinse, maybe 20, 30 minutes. The crew cleans out the residual from the last batch, flushes the system.\n\nOnce the line's clean, they start the run. First stage is **mix** — they're blending the base resin with additives in the mix tank. Then it moves to the **mill**, where it gets ground down to the right particle size. From there it goes to **tint and letdown** — for a white that's mostly just thinning it to spec, no pigment. Then finally **fill and pack** — into cans, labeled, palletized.\n\n*(gestures as if tracing the flow)*\n\nThere are holding tanks between those stages, so the mill can keep feeding while fill is catching up, or vice versa. Though... *(pauses)* the tank between mill and fill on Line 1 is tiny and it backs things up, but Line 2's is better.\n\nThe run takes... I don't know, with Line 2 on whites, maybe half a shift? That's 800 units at Line 2's rate plus the fill-up time and a bit of ramp while the line settles after the changeover. First few units after any changeover are scrap — ramp sc" + }, + { + "role": "user", + "content": "*(let them finish the thought about scrap)* — go on, you were saying first few units are scrap?" + }, + { + "role": "assistant", + "content": "*(nods)*\n\nYeah, ramp scrap. First few units after any changeover, the line's still settling — residual from the rinse, concentrations stabilizing, that kind of thing. Those units don't meet spec, so they get scrapped or reworked. It's worse after the big washdowns, like tint back to white. After a quick rinse it's not so bad.\n\n*(shrugs)*\n\nQuality tracks scrap as a monthly percentage, so I couldn't tell you exactly how much per changeover type. It's just... part of the cost of switching.\n\nAnyway, once the 800 units come off the line, they go into **QA hold**. The lab pulls samples, runs tests — usually takes about four hours for whites. Specialty sits longer, sometimes a full day. The lab's only two people, and end of week it backs up.\n\nOnce QA clears it, it ships. So for that Meridian order — if it runs Tuesday afternoon, finishes Tuesday night, clears QA Wednesday morning — it's out the door Wednesday, well ahead of Friday.\n\n*(takes another sip of coffee)*\n\nThat's the clean version, anyway. Reality is messier — the filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest. But that's the basic flow." + }, + { + "role": "user", + "content": "Good, that's helpful. You mentioned the filler on Line 2 jams every week or two, and when a line goes down at six in the morning you're reshuffling at the huddle. \n\n**What actually happened the last time Line 2 went down? What was supposed to be running, what did you do with it, and where did you move things to?**" + }, + { + "role": "assistant", + "content": "*(grimaces slightly)*\n\nOh, that was... two weeks ago? Maybe three. Line 2 was in the middle of a big white run — I think it was VW-03, the contractor-grade flat — and the filler jammed around 6 AM. Bag broke in the hopper, made a mess, whole thing locked up.\n\nAt the huddle, maintenance said it'd be at least a couple hours to clear and reset. Turned out to be more like half a shift, but we didn't know that yet.\n\n*(leans forward, gesturing)*\n\nSo I had to figure out what to do with the rest of that run. Line 1 was already on a tint — couldn't just stop that mid-batch. Line 3 was free, but... *(pauses)* Line 3 is slower on whites, and honestly I hate moving a half-finished run because you lose the setup, you're re-doing the fill-up, it's inefficient. So I decided to just wait it out on Line 2, let maintenance fix it, finish the run there.\n\nBut that pushed everything else back. I had a tint order that was supposed to start on Line 2 that afternoon — moved that to Line 1 the next morning instead, which meant Line 1 had to wash down from tint to... no, wait, it was" + }, + { + "role": "user", + "content": "— it's fine, you don't need to reconstruct every move from that day. Let me ask it differently:\n\n**When Line 2 goes down mid-run like that, what are your actual options? Can you pause the run and restart it later on the same line, can you move it to another line, do you have to scrap what's in progress? And what decides which option you pick?**" + }, + { + "role": "assistant", + "content": "*(nods, relieved to speak generally)*\n\nOkay, yeah — so if a line goes down mid-run, I've basically got three options.\n\n**One:** Wait it out. If maintenance says it's going to be an hour or two, and the run's almost done anyway, I just let the line sit and finish it when it's back up. The product's already in the tanks, you're not losing much except time.\n\n**Two:** Move the rest of the run to another line. That's a pain — you lose the fill-up you already paid, you're re-doing the setup on the new line, and you need that line to be free *and* qualified for the product. But if Line 2's going to be down half a shift or more and I've got capacity elsewhere, sometimes I'll do it.\n\n**Three:** Scrap what's in progress and restart the whole run later. I almost never do that unless something's really wrong — like if QA says the batch is already off-spec, or the line's going to be down for days. Too wasteful.\n\n*(leans back)*\n\nWhat decides? Mostly how long maintenance thinks it'll take, and whether I've got another line that can take it without screwing up something more urgent. And honestly... *(shrugs)* gut feel. If it's a Meridian order and the due date's tight, I'm more aggressive about moving it. If it's a small distributor order that can slip a few days, I'll wait." + }, + { + "role": "user", + "content": "Alright, so you've got three lines with different capabilities. Let me make sure I understand the constraints.\n\n**You mentioned Line 2 is faster for whites and it's the only one qualified for Meridian. What are the actual differences between your three lines — what can run where, and how do their speeds compare?**" + }, + { + "role": "assistant", + "content": "*(settles in)*\n\nOkay, so — **Line 1** is the old workhorse. It's slower, but it's qualified for everything. All the whites, all the tints, and all the specialty clears. It's the reliable one, but you pay for that in speed.\n\n**Line 2** is the fast line. It's about twice as fast as Line 1, at least on whites — that's where you really see it. That's why all the Meridian whites go there; the volume needs that speed. But Line 2 was never piped for the specialty resins, so it can't run the clears at all. Whites and tints only.\n\n**Line 3** is the newest. It's quick — somewhere between the two, maybe closer to Line 2's speed. But it's still being qualified product by product. It can run most of the whites and some of the tints, but there are still a couple tint SKUs that haven't been signed off yet. And it does specialties — that's newer, but it's qualified for them.\n\n*(pauses, thinking)*\n\nOh, and Line 3 only runs day shift unless we get overtime approved. Lines 1 and 2 run two shifts normally.\n\nSo if I've got a big white order, I want Line 2. Specialty, it's Line 1 or 3. And if Line 2's jammed and I need to move a white... Line 1 or Line 3, depending on what's free and whether Line 3 is actually qualified for that particular SKU.\n\nDoes that cover it?" + } + ], + "toolNames": ["activate_skill", "read_skill_resource"], + "resourcePaths": [ + "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md", + "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md" + ], + "ir": "# Coatings Plant Production Scheduling Model\n\n## Model Purpose and Objectives\n\n**Primary simulation questions:**\n- Are orders getting out on time?\n- How many changeover hours are being consumed?\n- Is there a smarter sequence that reduces changeover time?\n- What is the best reshuffle when Line 2 goes down?\n\n**Intended decision support:**\n- Test scheduling decisions before making them in production\n- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order\n- Pre-plan responses to Line 2 filler failures\n\n**Success measures:**\n- On-time delivery (especially Meridian customer - risk of fines and delisting if late)\n- Changeover hours (boss wants these reduced to recover capacity)\n- Ability to recover lost time through better sequencing\n\n**Audience:** Master scheduler and management\n\n## Process Boundary and Triggers\n\n**Boundary:**\n- Starts: Monday morning ~8 AM when demand book drops from ERP\n- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday)\n- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping\n- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed)\n\n**Trigger:** \n- Weekly demand book arrives Monday ~8 AM\n- Spreadsheet with 40-60 orders per week (can reach 60 when busy)\n- Each order: SKU, quantity, due date\n\n**Horizon:** One-week planning cycle (Monday-Friday)\n\n## Product Families and Distinctions\n\n**Product families** (operation treats these differently due to changeover costs):\n- **Whites** — high volume\n- **Tinted colors** — moderate volume\n- **Specialty clears** — handful per week\n\n**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible.\n\n## Resources: Production Lines\n\n### Line 1\n- **Description:** Old workhorse\n- **Speed:** Slower (baseline)\n- **Qualification:** Everything — all whites, all tints, all specialty clears\n- **Availability:** Two shifts normally\n- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up)\n\n### Line 2\n- **Description:** Fast line\n- **Speed:** About 2x Line 1 speed on whites (\"that's where you really see it\")\n- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins)\n- **Availability:** Two shifts normally\n- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago)\n- **Reliability issue:** Filler jams every week or two\n- **Notes:** Better holding tank between mill and fill than Line 1\n\n### Line 3\n- **Description:** Newest line\n- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed\n- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently)\n- **Availability:** Day shift only unless overtime approved\n- **Notes:** Still expanding qualification list\n\n**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3.\n\n## Process Spine: One Order From Demand Book to Ship\n\n**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday\n\n### 1. Demand book arrival (Monday ~8 AM)\nOrder appears as line in spreadsheet: SKU, quantity, due date.\n\n### 2. Scheduler builds allocation sheet\n- **Activity:** Match orders to lines, sequence them\n- **Performer:** Master scheduler\n- **Approach:** Group orders by product family when possible (minimize expensive family-switches)\n- **Constraints applied:**\n - Meridian whites → Line 2 (mandatory)\n - Specialty clears → Line 1 or Line 3 only\n - Line 3 → only if SKU qualified and capacity available\n- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time\n- **Check:** Does it all fit in the week?\n\n**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these \"in my head — or in the sheet.\" Fill-up times by line or product.\n\n### 3. Daily floor huddle (every morning 7:30 AM)\n- **Participants:** Scheduler, line leads, maintenance, QA\n- **Topics:** What finished overnight, what's running now, any problems\n- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip)\n- **Execution:** Verbal adjustments, people go execute\n\n### 4. Order waits for sequenced slot\n**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2.\n\n### 5. Changeover\n**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system.\n\n**Known changeover times:**\n- White → white: 20-30 minutes (quick rinse)\n- White → tint: ~45 minutes\n- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch)\n\n**NOT YET ASKED:**\n- Tint → tint changeover time\n- Specialty clear changeover times (to/from whites, tints, other clears)\n- Whether changeover times vary by line\n\n**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). \"Not so bad\" after quick rinse.\n\n**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures.\n\n### 6. Production run\n**Stages (in sequence):**\n1. **Mix:** Blend base resin with additives in mix tank\n2. **Mill:** Grind to particle size\n3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment)\n4. **Fill and pack:** Into cans, labeled, palletized\n\n**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank.\n\n**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput.\n\n**Run time example:** 800 units on Line 2 for whites took \"about half a shift\" (quantity at Line 2 rate + fill-up time + ramp settling after changeover).\n\n**NOT YET ASKED:** Hours per shift. Specific production rates.\n\n### 7. QA hold\n- **Duration:** ~4 hours for whites; sometimes full day for specialty\n- **Activity:** Lab pulls samples, runs tests\n- **Resource:** 2-person lab\n- **Congestion:** Backs up end of week\n\n**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned \"adjust and retest\" as occasional issue).\n\n### 8. Ship\nOnce QA clears, order ships.\n\n**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date).\n\n**Reality qualifier (from scheduler):** \"That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest.\"\n\n## Disruptions and Recovery\n\n### Line 2 filler jam (occurs every week or two)\n\n**Last occurrence (2-3 weeks ago):**\n- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM\n- Cause: Bag broke in hopper, made a mess, whole thing locked up\n- Maintenance estimate at huddle: \"at least a couple hours\"\n- Actual duration: \"more like half a shift\"\n\n**Three options when line goes down mid-run:**\n\n1. **Wait it out**\n - When: Maintenance says 1-2 hours and run almost done\n - Product already in tanks, only losing time\n \n2. **Move rest of run to another line**\n - Cost: Lose fill-up already paid, redo setup on new line\n - Prerequisites: New line must be free AND qualified for the product\n - When: Line will be down half a shift or more AND capacity available elsewhere\n \n3. **Scrap in-progress, restart whole run later**\n - When: Almost never; only if batch already off-spec or line down for days\n - Reason: Too wasteful\n\n**Decision factors (scheduler's account):**\n- Maintenance time estimate\n- Availability of another qualified line\n- Whether moving would \"screw up something more urgent\"\n- \"Gut feel\"\n- If Meridian order with tight due date → more aggressive about moving\n- If small distributor order that can slip a few days → wait\n\n**NOT YET ASKED:**\n- Frequency distribution of Line 2 jams\n- Duration distribution of Line 2 downtime\n- Frequency and nature of \"materials occasionally short\"\n- What happens to work already in holding tanks when line stops mid-run\n\n### Other disruptions mentioned but not detailed:\n- Batch fails QA hold → adjust and retest (frequency and impact not asked)\n- Materials slip (frequency, which materials, advance warning not asked)\n\n## Scheduling Constraints and Policies\n\n### Hard constraints (from scheduler account):\n- Meridian whites MUST go on Line 2 (customer requirement, audited that line)\n- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins)\n- Line 3 SKU-by-SKU qualification (some tints not yet signed off)\n\n### Practiced policies:\n- Group orders by product family when possible (minimize changeover cost)\n- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown)\n- When contention exists: Meridian orders get priority (due to penalty/delisting risk)\n\n### Trade-off under uncertainty (scheduler's stated dilemma):\n- If Line 2 finishing a white and another white order \"coming in a couple hours,\" hold line idle vs. wash down to tint?\n- Scheduler \"thinks waiting sometimes makes sense\" but cannot prove it\n- Boss wants fewer changeover hours\n\n**NOT YET ASKED:**\n- How \"couple hours\" or other wait-time thresholds factor into practiced decision\n- Whether orders actually arrive during the week or all appear Monday in demand book\n- Whether partial orders or rush orders ever interrupt the plan\n\n## Quantities, Rates, Time\n\n**Known:**\n- Demand book: 40-60 orders/week\n- Example order: 800 units\n- Line 2 speed: ~2x Line 1 on whites\n- Line 3 speed: between L1 and L2, closer to L2\n- Line 2 on 800-unit white: \"about half a shift\"\n- Changeover times: see Process Spine section 5\n- QA hold: ~4 hours whites, up to full day specialty\n- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime)\n\n**NOT YET ASKED:**\n- Specific units/hour rates by product-line combination\n- Hours per shift\n- Fill-up time by line or product\n- Ramp scrap quantities\n- Distribution of order sizes\n- Distribution of due dates within the week\n- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty)\n- Whether batches have minimum or maximum sizes\n- Line 2 jam frequency (every week or two → distribution?)\n- Line 2 downtime duration (couple hours to half shift → distribution?)\n\n## Initial Conditions and State\n\n**NOT YET ASKED:**\n- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?)\n- Are there any orders in progress or in QA hold from prior week?\n- Initial inventory or work-in-process?\n\n## Validation and Evidence Sources\n\n**Validation intent (from scheduler):**\n- Model should show on-time delivery performance\n- Model should count changeover hours\n- Model should allow testing alternative sequences\n- Model should support pre-planning reshuffles when Line 2 fails\n\n**NOT YET ASKED:**\n- What observation, replay, or comparison would make the model credible enough to use?\n- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)?\n- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet?\n\n## Open Questions and Unresolved Material\n\n### Critical for construction but not yet asked:\n1. Specific production rates (units/hour) for product-line combinations\n2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line\n3. Fill-up times\n4. Ramp scrap quantities by changeover type\n5. Hours per shift\n6. Line 3 SKU qualification details\n7. Initial state at start of simulation week\n8. Whether orders can be split across lines or must run whole on one line\n9. Holding tank capacities and constraints\n10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages\n11. Due date distribution in demand book\n12. Product family distribution in demand book\n13. Validation: what would make model credible, what historical data exists\n\n### Consequential unknowns flagged by scheduler:\n- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail)\n- Exact rates (scheduler has them \"in my head — or in the sheet\" but not stated in interview)\n\n### Deliberate simplifications or omissions:\n- None explicitly proposed yet\n\n### Assumptions:\n- None explicitly introduced yet\n\n### Conflicts or corrections:\n- None yet\n\n### Contextual variations noted but not fully explored:\n- Line 2 downtime: \"couple hours\" vs. \"more like half a shift\" (context: initial estimate vs. actual)\n- QA hold: \"about 4 hours\" for whites, \"sometimes full day\" for specialty, \"backs up end of week\" (context-dependent duration)\n- Scheduler's practiced policy varies by customer urgency and due date pressure\n\n## Target Representation Notes\n\n**Target formalism:** Petri-net-style process model (specific format not known to scheduler; \"I'm not the modelling person\")\n\n**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined.\n\n**When construction begins, will need to infer:**\n- How to represent line eligibility constraints\n- How to represent scheduler's practiced priority rules under contention\n- How to represent three-option recovery logic when Line 2 fails\n- How to represent holding tanks and multi-stage production flow\n- Whether to model individual units, batches, or orders as tokens\n- How to represent ramp scrap and QA hold\n- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary)\n\n**Consequential gaps that block faithful construction:**\n- Missing rates prevent accurate time modeling\n- Missing changeover time matrix prevents accurate sequencing cost\n- Missing failure/disruption frequency distributions prevent realistic stochastic behavior\n- Missing initial state prevents simulation start\n- Missing validation criteria prevent assessing whether constructed model is fit for purpose", + "workpiece": { + "content": "# Coatings Plant Production Scheduling Model\n\n## Model Purpose and Objectives\n\n**Primary simulation questions:**\n- Are orders getting out on time?\n- How many changeover hours are being consumed?\n- Is there a smarter sequence that reduces changeover time?\n- What is the best reshuffle when Line 2 goes down?\n\n**Intended decision support:**\n- Test scheduling decisions before making them in production\n- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order\n- Pre-plan responses to Line 2 filler failures\n\n**Success measures:**\n- On-time delivery (especially Meridian customer - risk of fines and delisting if late)\n- Changeover hours (boss wants these reduced to recover capacity)\n- Ability to recover lost time through better sequencing\n\n**Audience:** Master scheduler and management\n\n## Process Boundary and Triggers\n\n**Boundary:**\n- Starts: Monday morning ~8 AM when demand book drops from ERP\n- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday)\n- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping\n- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed)\n\n**Trigger:** \n- Weekly demand book arrives Monday ~8 AM\n- Spreadsheet with 40-60 orders per week (can reach 60 when busy)\n- Each order: SKU, quantity, due date\n\n**Horizon:** One-week planning cycle (Monday-Friday)\n\n## Product Families and Distinctions\n\n**Product families** (operation treats these differently due to changeover costs):\n- **Whites** — high volume\n- **Tinted colors** — moderate volume\n- **Specialty clears** — handful per week\n\n**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible.\n\n## Resources: Production Lines\n\n### Line 1\n- **Description:** Old workhorse\n- **Speed:** Slower (baseline)\n- **Qualification:** Everything — all whites, all tints, all specialty clears\n- **Availability:** Two shifts normally\n- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up)\n\n### Line 2\n- **Description:** Fast line\n- **Speed:** About 2x Line 1 speed on whites (\"that's where you really see it\")\n- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins)\n- **Availability:** Two shifts normally\n- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago)\n- **Reliability issue:** Filler jams every week or two\n- **Notes:** Better holding tank between mill and fill than Line 1\n\n### Line 3\n- **Description:** Newest line\n- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed\n- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently)\n- **Availability:** Day shift only unless overtime approved\n- **Notes:** Still expanding qualification list\n\n**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3.\n\n## Process Spine: One Order From Demand Book to Ship\n\n**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday\n\n### 1. Demand book arrival (Monday ~8 AM)\nOrder appears as line in spreadsheet: SKU, quantity, due date.\n\n### 2. Scheduler builds allocation sheet\n- **Activity:** Match orders to lines, sequence them\n- **Performer:** Master scheduler\n- **Approach:** Group orders by product family when possible (minimize expensive family-switches)\n- **Constraints applied:**\n - Meridian whites → Line 2 (mandatory)\n - Specialty clears → Line 1 or Line 3 only\n - Line 3 → only if SKU qualified and capacity available\n- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time\n- **Check:** Does it all fit in the week?\n\n**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these \"in my head — or in the sheet.\" Fill-up times by line or product.\n\n### 3. Daily floor huddle (every morning 7:30 AM)\n- **Participants:** Scheduler, line leads, maintenance, QA\n- **Topics:** What finished overnight, what's running now, any problems\n- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip)\n- **Execution:** Verbal adjustments, people go execute\n\n### 4. Order waits for sequenced slot\n**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2.\n\n### 5. Changeover\n**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system.\n\n**Known changeover times:**\n- White → white: 20-30 minutes (quick rinse)\n- White → tint: ~45 minutes\n- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch)\n\n**NOT YET ASKED:**\n- Tint → tint changeover time\n- Specialty clear changeover times (to/from whites, tints, other clears)\n- Whether changeover times vary by line\n\n**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). \"Not so bad\" after quick rinse.\n\n**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures.\n\n### 6. Production run\n**Stages (in sequence):**\n1. **Mix:** Blend base resin with additives in mix tank\n2. **Mill:** Grind to particle size\n3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment)\n4. **Fill and pack:** Into cans, labeled, palletized\n\n**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank.\n\n**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput.\n\n**Run time example:** 800 units on Line 2 for whites took \"about half a shift\" (quantity at Line 2 rate + fill-up time + ramp settling after changeover).\n\n**NOT YET ASKED:** Hours per shift. Specific production rates.\n\n### 7. QA hold\n- **Duration:** ~4 hours for whites; sometimes full day for specialty\n- **Activity:** Lab pulls samples, runs tests\n- **Resource:** 2-person lab\n- **Congestion:** Backs up end of week\n\n**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned \"adjust and retest\" as occasional issue).\n\n### 8. Ship\nOnce QA clears, order ships.\n\n**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date).\n\n**Reality qualifier (from scheduler):** \"That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest.\"\n\n## Disruptions and Recovery\n\n### Line 2 filler jam (occurs every week or two)\n\n**Last occurrence (2-3 weeks ago):**\n- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM\n- Cause: Bag broke in hopper, made a mess, whole thing locked up\n- Maintenance estimate at huddle: \"at least a couple hours\"\n- Actual duration: \"more like half a shift\"\n\n**Three options when line goes down mid-run:**\n\n1. **Wait it out**\n - When: Maintenance says 1-2 hours and run almost done\n - Product already in tanks, only losing time\n \n2. **Move rest of run to another line**\n - Cost: Lose fill-up already paid, redo setup on new line\n - Prerequisites: New line must be free AND qualified for the product\n - When: Line will be down half a shift or more AND capacity available elsewhere\n \n3. **Scrap in-progress, restart whole run later**\n - When: Almost never; only if batch already off-spec or line down for days\n - Reason: Too wasteful\n\n**Decision factors (scheduler's account):**\n- Maintenance time estimate\n- Availability of another qualified line\n- Whether moving would \"screw up something more urgent\"\n- \"Gut feel\"\n- If Meridian order with tight due date → more aggressive about moving\n- If small distributor order that can slip a few days → wait\n\n**NOT YET ASKED:**\n- Frequency distribution of Line 2 jams\n- Duration distribution of Line 2 downtime\n- Frequency and nature of \"materials occasionally short\"\n- What happens to work already in holding tanks when line stops mid-run\n\n### Other disruptions mentioned but not detailed:\n- Batch fails QA hold → adjust and retest (frequency and impact not asked)\n- Materials slip (frequency, which materials, advance warning not asked)\n\n## Scheduling Constraints and Policies\n\n### Hard constraints (from scheduler account):\n- Meridian whites MUST go on Line 2 (customer requirement, audited that line)\n- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins)\n- Line 3 SKU-by-SKU qualification (some tints not yet signed off)\n\n### Practiced policies:\n- Group orders by product family when possible (minimize changeover cost)\n- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown)\n- When contention exists: Meridian orders get priority (due to penalty/delisting risk)\n\n### Trade-off under uncertainty (scheduler's stated dilemma):\n- If Line 2 finishing a white and another white order \"coming in a couple hours,\" hold line idle vs. wash down to tint?\n- Scheduler \"thinks waiting sometimes makes sense\" but cannot prove it\n- Boss wants fewer changeover hours\n\n**NOT YET ASKED:**\n- How \"couple hours\" or other wait-time thresholds factor into practiced decision\n- Whether orders actually arrive during the week or all appear Monday in demand book\n- Whether partial orders or rush orders ever interrupt the plan\n\n## Quantities, Rates, Time\n\n**Known:**\n- Demand book: 40-60 orders/week\n- Example order: 800 units\n- Line 2 speed: ~2x Line 1 on whites\n- Line 3 speed: between L1 and L2, closer to L2\n- Line 2 on 800-unit white: \"about half a shift\"\n- Changeover times: see Process Spine section 5\n- QA hold: ~4 hours whites, up to full day specialty\n- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime)\n\n**NOT YET ASKED:**\n- Specific units/hour rates by product-line combination\n- Hours per shift\n- Fill-up time by line or product\n- Ramp scrap quantities\n- Distribution of order sizes\n- Distribution of due dates within the week\n- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty)\n- Whether batches have minimum or maximum sizes\n- Line 2 jam frequency (every week or two → distribution?)\n- Line 2 downtime duration (couple hours to half shift → distribution?)\n\n## Initial Conditions and State\n\n**NOT YET ASKED:**\n- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?)\n- Are there any orders in progress or in QA hold from prior week?\n- Initial inventory or work-in-process?\n\n## Validation and Evidence Sources\n\n**Validation intent (from scheduler):**\n- Model should show on-time delivery performance\n- Model should count changeover hours\n- Model should allow testing alternative sequences\n- Model should support pre-planning reshuffles when Line 2 fails\n\n**NOT YET ASKED:**\n- What observation, replay, or comparison would make the model credible enough to use?\n- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)?\n- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet?\n\n## Open Questions and Unresolved Material\n\n### Critical for construction but not yet asked:\n1. Specific production rates (units/hour) for product-line combinations\n2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line\n3. Fill-up times\n4. Ramp scrap quantities by changeover type\n5. Hours per shift\n6. Line 3 SKU qualification details\n7. Initial state at start of simulation week\n8. Whether orders can be split across lines or must run whole on one line\n9. Holding tank capacities and constraints\n10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages\n11. Due date distribution in demand book\n12. Product family distribution in demand book\n13. Validation: what would make model credible, what historical data exists\n\n### Consequential unknowns flagged by scheduler:\n- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail)\n- Exact rates (scheduler has them \"in my head — or in the sheet\" but not stated in interview)\n\n### Deliberate simplifications or omissions:\n- None explicitly proposed yet\n\n### Assumptions:\n- None explicitly introduced yet\n\n### Conflicts or corrections:\n- None yet\n\n### Contextual variations noted but not fully explored:\n- Line 2 downtime: \"couple hours\" vs. \"more like half a shift\" (context: initial estimate vs. actual)\n- QA hold: \"about 4 hours\" for whites, \"sometimes full day\" for specialty, \"backs up end of week\" (context-dependent duration)\n- Scheduler's practiced policy varies by customer urgency and due date pressure\n\n## Target Representation Notes\n\n**Target formalism:** Petri-net-style process model (specific format not known to scheduler; \"I'm not the modelling person\")\n\n**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined.\n\n**When construction begins, will need to infer:**\n- How to represent line eligibility constraints\n- How to represent scheduler's practiced priority rules under contention\n- How to represent three-option recovery logic when Line 2 fails\n- How to represent holding tanks and multi-stage production flow\n- Whether to model individual units, batches, or orders as tokens\n- How to represent ramp scrap and QA hold\n- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary)\n\n**Consequential gaps that block faithful construction:**\n- Missing rates prevent accurate time modeling\n- Missing changeover time matrix prevents accurate sequencing cost\n- Missing failure/disruption frequency distributions prevent realistic stochastic behavior\n- Missing initial state prevents simulation start\n- Missing validation criteria prevent assessing whether constructed model is fit for purpose", + "sha256": "99e4e201cdb4959c114495049e68d7f4d18bdc5c41a5b1d958001b4027fbe355", + "sourceMessageId": "entry_01M1GZ1BQQ6Y1SBS51E3K1W0NE", + "sourceMessageSha256": "42d1d9b7de99f143ed85c57dd3f09dcc166c55bf3e3c409a0607ec360d54dcf5" + }, + "violations": [], + "wroteCaptureStore": false, + "instrument": { + "sourceCommit": "794fe2fbf1eaeba3fc816c6e3d1755d7b444125d", + "instrumentStatus": "", + "fileSha256": { + "yarn.lock": "3f445d255ef7893c1941f5610e2a813a903d5b9053c9831012c3d7c3875571dc", + "apps/brunch-agent/package.json": "27182018edf5c595522815ea28d78e549d387c127714c0ea2c54a84822ead742", + "apps/brunch-agent/vite.config.ts": "f1a50233829599dff2d5ef7ee0cd10bdb55d7fe65a52a6e85913e00a33a5300d", + "apps/brunch-agent/src/app.ts": "979efb9172c82df7499b218125735929a9586a0d9355e042e9ff2402955f385e", + "apps/brunch-agent/src/db.ts": "9f3a2598df3ce6737de1348afa8baf7deeeeac51b8202bf87adb76dc96374481", + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e", + "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744", + "apps/brunch-agent/src/conversation/identity.ts": "b52f764ba6e6bb50602d477eb93234aa6a900c843412551de40ea6ad3050e5e6", + "apps/brunch-agent/src/conversation/transcript.ts": "b96cf8d2b8352d414f674985b7071c9570323ccaf862f7e77ec76311e4b11d44", + "apps/brunch-agent/src/http/routes.ts": "7d8389931c57671ad8376d0a0ab497413013d47b012ffbf531339d0d1ce3a0cd", + "apps/brunch-agent/src/evaluations/runbook/artifacts.ts": "15951ab3705b126d64a2593731b50775a16915298dae87b8cf7c99e265e2c2f4", + "apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts": "60215179f5d48bacbf4da7b0876d049ed8b15b4b774d99eab092a1297502a3b4", + "apps/brunch-agent/src/evaluations/runbook/load-built-application.ts": "c4b28985ad98dd1dde5afb8838ba6f9445692d208adde05c22f30f6074365056", + "apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts": "a0a197af22d0a007f73836db49cd13a2c776e57d0dab047e537868c9ff2f33f9", + "libs/@hashintel/brunch-agent/packages/core/package.json": "e12d9cc79d38c43e5543a5667753e5049dfbf21bec425c25942f1c4cbb922a1f", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "0bd463eaab4eb68b86431bf26e0e01f90c89437b2888c86f4da53d7a0f39ec13", + "libs/@hashintel/brunch-agent/packages/core/src/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json": "dca0db144c530bcf66c5400c029efa5e1136c9d7f81ed174343e891985200c33", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md": "9915751f11baf06de31b237ee8df57b5217f2e7f0531090370a7e08d0282b6c2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "d5bac87a192b92fb6b7d749ec3d58116f9e39527b59e425c60aaeedf5cc13bd0", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts": "a283342146c8d2c2cb8056c588e76c1c3094f1e21e731be251c8fcdb335ed1b2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/instructions.md": "ea3a8755a576e98d35fce6a1e1f928a61148eb713f0bc2c2f818d104eb5f3832", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/profile.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/workpiece-template.md": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/checks.md": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md": "817e43d15848bcec3c720a3e8b1104d31faf3035fd7ae8269d0a97ee43ef4a9a", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md": "faba12be8642400e60df37bc5bf5c321541f922d9f0433cf1455423492a2be7a", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md": "f57f4177edbe957092e86c19826a003b6c9257c319a129008428839e4cf0e2b7", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.ir.md": "3ee05f3d5d88f3061b360467e755606f7c43e07bdfacdfebb094bebaee23e732", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.omniscient.md": "82dbe47745b175cca93d50ea64a45715bfafd860d109904c1d5136282fae0aa0", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.cold.md": "c832a84226a4fb795281b96185b5efae13bdcf59a2db6c9cf481e5cd9b0b81b8", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.ir.md": "4d15050ac6a2a89e1f6a371a604522504bbb90c0893c2029773b1b906db85043", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.omniscient.md": "ee81f16127bf255a3247a3c9f42508970542beebf38a3445d9be18e0f765e120", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.cold.md": "d072d7fe27e6b8458ccb7d9119c7901fcc45d47d88141b970ce2d9ac2a0b2224" + }, + "builtArtifactManifest": [ + { + "path": "apps/brunch-agent/dist/app.mjs", + "sha256": "5ce74b2646c00b959b3ee847b47e006717ccc21e1fba769ce818566cba6da62e" + }, + { + "path": "apps/brunch-agent/dist/node-server-BWNSXBme.mjs", + "sha256": "90343929bc5274e0644003e8d7ea69b02003ac4d3b364a0afdb0be0e1032c066" + }, + { + "path": "apps/brunch-agent/dist/server.mjs", + "sha256": "c2d4ccb5aa999e33d33530ba87280e4bb6d6aacd41df630876297bfabea8f04e" + } + ], + "builtArtifactManifestSha256": "5bb67f5cbb1315e789426d83780ed9af23c58b1a04cf14a4b62a831cae435cd0" + }, + "transcript": "## User\nI'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.\n\n## Assistant\nI'll help you develop a simulation model of your scheduling operation. Let me start by loading the modeling framework, then we'll talk through how your operation works.Good. Now let's talk about how your operation actually works.\n\nBefore we dive into the details, help me understand what you're trying to accomplish with this simulation. **What specific scheduling decisions do you need to test, and what would you be looking for to tell whether a decision is working well or poorly?**\n- tool activate_skill (toolu_01N61ni3XJpbKBS1pZXxvpgD): \"Run the skill named \\\"sdcpn-modelling\\\".\\n\\n<skill_instructions>\\n# Capability-aware lifecycle\\n\\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\\n\\n## Select the runtime branch\\n\\n### Interactive elicitation, review, or revision\\n\\nInterview in the person's operational vocabulary. Before substantive\\nelicitation or workpiece revision, read\\n`references/universal-elicitation.md` and `references/profile.md`.\\n\\nFor review, first inspect the supplied workpiece or model. If it already answers\\nthe question, answer without reading the elicitation references or interviewing.\\nIf the review exposes a gap that requires human knowledge, read both elicitation\\nreferences and then ask exactly one focused question that resolves the smallest\\nconsequential gap.\\n\\nRead `templates/workpiece.md` only when first creating or materially revising\\nthe workpiece. Do not load the template merely to inspect an existing artifact,\\nanswer a resolvable review question, or frame the first elicitation question.\\nConstruct only when the mounted capabilities actually permit construction in\\nthis conversation.\\n\\n### Construct-only execution\\n\\nUse the supplied workpiece as the complete modelling input. Do not interview.\\nRead `references/pn-construction.md` and `references/checks.md`, then use the\\nmounted construction tools. If a consequential workpiece gap prevents faithful\\nconstruction, report the gap and the smallest question a later interactive\\nelicitation must answer; do not ask it or invent an answer in this conversation.\\n\\n## Procedure\\n\\n### Orient\\n\\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\\n\\n### Elicit or revise\\n\\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\\n\\n### Maintain the workpiece\\n\\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\\n\\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\\n\\n### Construct\\n\\nConstruct only from the current workpiece. Read\\n`references/pn-construction.md` and `references/checks.md` before beginning.\\nUse mounted Petrinaut tools for every net change and inspect the resulting\\ndefinition rather than emitting free-form net JSON. If the required tools are\\nabsent, limit the result to the workpiece and construction-ready notes.\\n\\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\\n\\n### Check and deliver\\n\\nApply `references/checks.md` whenever construction is prepared or attempted.\\nDeliver the current workpiece in every branch. Deliver a net only when the\\nmounted tool path has produced and checked one. State what the result can\\nsupport, what remains open, what was assumed or simplified, and what the target\\nor current tools could not represent.\\n\\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\\n\\n## Resource discipline\\n\\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\\n</skill_instructions>\\n\\nSupporting skill resources are available but are not loaded into context unless needed:\\n<skill_resources>\\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\\n</skill_resources>\"\n- tool read_skill_resource (toolu_01LTMkcUNraBdu4AnqXYCNkp): \"# Universal Elicitation Reference\\n\\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\\n\\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\\n\\n## Directives\\n\\n### Work from purpose\\n\\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\\n\\n### Accumulate posture conversationally\\n\\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\\n\\n### Follow the person's account\\n\\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\\n\\n### Protect interaction bandwidth\\n\\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\\n\\n### Preserve authorship and uncertainty\\n\\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\\n\\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\\n\\n### Treat divergence as information\\n\\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\\n\\n### Maintain a recoverable workpiece\\n\\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\\n\\n### Stop honestly\\n\\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\\n\\n## Recognition\\n\\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\\n\\n### Vague or compressed language\\n\\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\\n\\n### Normative language\\n\\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\\n\\n### Tension within or between accounts\\n\\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\\n\\n### Unexplained terms and artifacts\\n\\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\\n\\n### Burden, impatience, or limited availability\\n\\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\\n\\n### Diminishing yield\\n\\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\\n\\n### Assent without independent wording\\n\\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\\n\\n### Silence and absence\\n\\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\\n\\n## Operations\\n\\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\\n\\n### Select the smallest consequential absence\\n\\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\\n\\n### Slice a concrete case\\n\\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\\n\\n### Sweep one property\\n\\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\\n\\n### Ask for the last occurrence\\n\\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\\n\\n### Ask for the basis\\n\\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\\n\\n### Ground a term or artifact\\n\\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\\n\\n### Clarify until observable\\n\\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\\n\\n### Use contrastive cases\\n\\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\\n\\n### Investigate quantities relative to purpose\\n\\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\\n\\n### Turn an unknown into a decision threshold\\n\\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\\n\\n### State a contradiction without resolving it\\n\\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\\n\\n### Restate for correction\\n\\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\\n\\n### Propose structure for correction\\n\\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\\n\\n### Deposit and defer\\n\\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\\n\\n### Press without trapping\\n\\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\\n\\n### Explore a rare or severe outcome\\n\\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\\n\\n### Seek a witness or counterexample\\n\\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\\n\\n### Trade concrete outcomes\\n\\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\\n\\n### Close with one correction opportunity\\n\\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\\n\\n## Coverage\\n\\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\\n\\nA workpiece may need to preserve:\\n\\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\\n- a cold-readable account in the person's vocabulary, including consequential local terms;\\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\\n- decisions, alternatives, and reasons distinctions among them matter;\\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\\n- exact evidence where later interpretation depends on the person's wording;\\n- normalized accounts and agent inferences without laundering their authorship;\\n- assumptions with why they were introduced and how they could be checked;\\n- unknown, not-yet-asked, declined, and deferred material without conflation;\\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\\n- open questions with the consequence of leaving them open and the condition for returning.\\n\\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\\n\\n## Verification\\n\\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\\n\\n### Before asking\\n\\n- The question serves the stated purpose or resolves an active uncertainty.\\n- It follows the person's thread rather than the order of a schema or template.\\n- It asks one focused thing, or a small set that genuinely shares one frame.\\n- A proposed answer, category, number, or distinction is identified as yours.\\n\\n### Before recording\\n\\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\\n- Assent to your wording has not been presented as independently originated evidence.\\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\\n\\n### Before closing\\n\\n- The result's purpose and consequential account have been offered for correction.\\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\\n- The person has not been kept in an irrelevant thread merely to fill a category.\\n- A stop produces a useful partial result rather than a false claim of completion.\\n\\n### Failure signals and repairs\\n\\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\\n\"\n- tool read_skill_resource (toolu_01GLhHLrYu5LK8sArEbjPgae): \"# Operational-Process and SDCPN Elicitation\\n\\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\\n\\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\\n\\n## Directives\\n\\n### Build the operational account the purpose needs\\n\\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\\n\\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\\n\\n### Keep target structure backstage\\n\\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\\n\\n### Preserve operational context\\n\\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\\n\\n### Treat operational patterns as hypotheses\\n\\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\\n\\n## Recognition\\n\\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\\n\\n### Language and account signals\\n\\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\\n\\n### Operational situation patterns\\n\\n#### Timed work\\n\\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\\n\\n#### Conditional or probabilistic outcome\\n\\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\\n\\n#### Contended resource\\n\\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\\n\\n#### Consumed, reserved, or read input\\n\\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\\n\\n#### Gate, release, trigger, or prerequisite\\n\\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\\n\\n#### Continuous quantity and threshold\\n\\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\\n\\n#### Mode change\\n\\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\\n\\n#### Batch, lot, load, or grouped movement\\n\\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\\n\\n#### Spatial transfer\\n\\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\\n\\n#### Event, failure, retry, and recovery\\n\\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\\n\\n#### Policy under pressure\\n\\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\\n\\n#### Hidden waiting\\n\\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\\n\\n## Operations\\n\\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\\n\\n### Choose the case unit before slicing\\n\\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\\n\\n### Link the slice to the objective\\n\\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\\n\\n### Expose the process spine\\n\\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\\n\\n### Sweep operational concerns, not headings\\n\\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\\n\\n### Distinguish consumed, reserved, and read inputs\\n\\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\\n\\n### Sweep what can befall an activity\\n\\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\\n\\n### Test practiced policy with a borderline case\\n\\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\\n\\n### Close a resource account\\n\\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\\n\\n### Close a mode change in both directions\\n\\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\\n\\n### Turn waiting into a causal question\\n\\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\\n\\n### Ask what is conserved\\n\\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\\n\\n### Establish retry scope\\n\\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\\n\\n### Establish validation from observable behavior\\n\\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\\n\\n## Coverage\\n\\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\\n\\n### Purpose, goals, measures, constraints, and thresholds\\n\\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\\n\\n### Process boundary, triggers, prerequisites, and initial conditions\\n\\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\\n\\n### Participants, locations, flowing things, and resources\\n\\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\\n\\n### Activities, inputs, outputs, and resource use\\n\\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\\n\\n### Flow, branching, joining, failure, retry, and recovery\\n\\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\\n\\n### Time, quantities, arrivals, and stochastic behavior\\n\\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\\n\\n### Policies, exceptions, practiced rules, and contextual regimes\\n\\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\\n\\n### Validation, evidence sources, and data bindings\\n\\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\\n\\n### Things not independently elicited as target nodes\\n\\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\\n\\n## Verification\\n\\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\\n\\n### Purpose and process\\n\\n- At least one simulation question, comparison, or decision is stated in the person's terms.\\n- Every objective depends on recorded process material or remains visibly unsupported.\\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\\n\\n### Operational semantics\\n\\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\\n- Hidden waiting has not silently become an activity or unexplained queue.\\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\\n\\n### Quantities and context\\n\\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\\n\\n### Failure signals and repairs\\n\\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\\n\"\n\n## User\n*(settles into a chair, coffee mug in hand)*\n\nThe main thing? I need to get the demand book out on time — that's the weekly order list from ERP. Late orders are what gets me in trouble, especially with Meridian. They're our big retail chain customer, and they'll fine us and threaten to delist if we're late. So that's non-negotiable.\n\nBut beyond that... *(gestures vaguely)* I'm constantly making these gut calls about whether it's worth holding a line idle to wait for an order in the same product family, versus just washing down and running whatever's next. Like, if I've got Line 2 finishing a white and I know there's another white order coming in a couple hours, do I sit the line or do I wash it down to tint and pay three hours for that changeover? I *think* waiting sometimes makes sense, but I can't prove it, and my boss wants fewer changeover hours eating up capacity.\n\nAnd when a line goes down at six in the morning — which happens, especially the filler on Line 2 — I'm re-juggling the whole sheet at the huddle, and it's all improvisation. It'd be nice to know ahead of time what the best reshuffle actually is.\n\n*(pauses)*\n\nSo I guess what I'm looking for is: are we getting orders out on time, how many changeover hours are we actually burning, and whether there's a smarter way to sequence things that claws back some of that lost time. Does that make sense?\n\n## Assistant\nThat makes perfect sense. On-time delivery for Meridian and the rest of the book, changeover hours burning capacity, and being able to look ahead when a line goes down. Those are concrete things I can work with.\n\nLet me follow a typical case so I understand how this actually flows. **Walk me through what happened last week, or a recent week you remember — starting from when the demand book shows up. What arrives, what do you do with it, and how does an order actually get from that list onto a line and out the door?**\n\n## User\n*(leans back, thinking)*\n\nOkay, so... Monday morning, usually around 8 AM, the demand book drops from ERP. It's a spreadsheet — maybe 40, 50 orders that week, could be 60 if we're busy. Each line is an SKU, how many units, and a due date. Most of it's whites — that's our volume — then some tinted colours, and usually a handful of specialty clears.\n\nI take that list and I build the sheet. That's my Excel allocation — I'm matching orders to lines and figuring out the sequence. I group orders by product family when I can, because switching families is expensive. White to tint is maybe 45 minutes, but tint back to white? Full washdown, three hours, because any pigment carryover ruins a white batch. So I'm trying to keep whites together, tints together.\n\nI also know some products can only run certain places. Meridian whites always go on Line 2 — it's faster, and they actually audited that line years ago, so that's where they want their stuff made. Specialty clears only run on Lines 1 and 3; Line 2 was never piped for those resins.\n\n*(sips coffee)*\n\nOnce I've got the sequence roughed out, I'm estimating times. Each product-line combo has a rate in my head — or in the sheet, really. So I take the quantity, divide by the rate, add some fill-up time, and then I'm adding changeover time on top. That gives me a rough idea if it all fits in the week.\n\nThen the week starts running. Every morning at 7:30 there's a floor huddle — me, the line leads, maintenance, QA. We talk about what finished overnight, what's running now, any problems. And I'm adjusting on the fly. A line jams, a batch fails QA hold, materials slip... I'm moving stuff around verbally, and people go execute.\n\nOrders finish, sit in QA hold for a few hours — maybe four, longer for specialty — then they ship. By Friday afternoon we've usually cleared most of it. Sometimes something slips to Monday.\n\nDoes that give you the picture?\n\n## Assistant\nGood, that's giving me the shape of it. Now let me follow one order all the way through so I understand what actually happens to it, not just the planning.\n\n**Pick one order from last week or the week before — maybe a Meridian white, since those matter most. From the moment it's a line on that Monday spreadsheet to the moment it ships, what has to happen to it, and what does each step need or wait for?**\n\n## User\n*(thinks for a moment)*\n\nAlright, let's say... Meridian order for VW-01, our high-volume interior white. 800 units, due Friday. That's on the sheet for Line 2, naturally.\n\nSo the order goes into my sequence — let's say I slot it for Tuesday afternoon, after we finish a run of another white that's already on the line. That's good because it's white-to-white, so the changeover is just a quick rinse, maybe 20, 30 minutes. The crew cleans out the residual from the last batch, flushes the system.\n\nOnce the line's clean, they start the run. First stage is **mix** — they're blending the base resin with additives in the mix tank. Then it moves to the **mill**, where it gets ground down to the right particle size. From there it goes to **tint and letdown** — for a white that's mostly just thinning it to spec, no pigment. Then finally **fill and pack** — into cans, labeled, palletized.\n\n*(gestures as if tracing the flow)*\n\nThere are holding tanks between those stages, so the mill can keep feeding while fill is catching up, or vice versa. Though... *(pauses)* the tank between mill and fill on Line 1 is tiny and it backs things up, but Line 2's is better.\n\nThe run takes... I don't know, with Line 2 on whites, maybe half a shift? That's 800 units at Line 2's rate plus the fill-up time and a bit of ramp while the line settles after the changeover. First few units after any changeover are scrap — ramp sc\n\n## Assistant\n*(let them finish the thought about scrap)* — go on, you were saying first few units are scrap?\n\n## User\n*(nods)*\n\nYeah, ramp scrap. First few units after any changeover, the line's still settling — residual from the rinse, concentrations stabilizing, that kind of thing. Those units don't meet spec, so they get scrapped or reworked. It's worse after the big washdowns, like tint back to white. After a quick rinse it's not so bad.\n\n*(shrugs)*\n\nQuality tracks scrap as a monthly percentage, so I couldn't tell you exactly how much per changeover type. It's just... part of the cost of switching.\n\nAnyway, once the 800 units come off the line, they go into **QA hold**. The lab pulls samples, runs tests — usually takes about four hours for whites. Specialty sits longer, sometimes a full day. The lab's only two people, and end of week it backs up.\n\nOnce QA clears it, it ships. So for that Meridian order — if it runs Tuesday afternoon, finishes Tuesday night, clears QA Wednesday morning — it's out the door Wednesday, well ahead of Friday.\n\n*(takes another sip of coffee)*\n\nThat's the clean version, anyway. Reality is messier — the filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest. But that's the basic flow.\n\n## Assistant\nGood, that's helpful. You mentioned the filler on Line 2 jams every week or two, and when a line goes down at six in the morning you're reshuffling at the huddle. \n\n**What actually happened the last time Line 2 went down? What was supposed to be running, what did you do with it, and where did you move things to?**\n\n## User\n*(grimaces slightly)*\n\nOh, that was... two weeks ago? Maybe three. Line 2 was in the middle of a big white run — I think it was VW-03, the contractor-grade flat — and the filler jammed around 6 AM. Bag broke in the hopper, made a mess, whole thing locked up.\n\nAt the huddle, maintenance said it'd be at least a couple hours to clear and reset. Turned out to be more like half a shift, but we didn't know that yet.\n\n*(leans forward, gesturing)*\n\nSo I had to figure out what to do with the rest of that run. Line 1 was already on a tint — couldn't just stop that mid-batch. Line 3 was free, but... *(pauses)* Line 3 is slower on whites, and honestly I hate moving a half-finished run because you lose the setup, you're re-doing the fill-up, it's inefficient. So I decided to just wait it out on Line 2, let maintenance fix it, finish the run there.\n\nBut that pushed everything else back. I had a tint order that was supposed to start on Line 2 that afternoon — moved that to Line 1 the next morning instead, which meant Line 1 had to wash down from tint to... no, wait, it was\n\n## Assistant\n— it's fine, you don't need to reconstruct every move from that day. Let me ask it differently:\n\n**When Line 2 goes down mid-run like that, what are your actual options? Can you pause the run and restart it later on the same line, can you move it to another line, do you have to scrap what's in progress? And what decides which option you pick?**\n\n## User\n*(nods, relieved to speak generally)*\n\nOkay, yeah — so if a line goes down mid-run, I've basically got three options.\n\n**One:** Wait it out. If maintenance says it's going to be an hour or two, and the run's almost done anyway, I just let the line sit and finish it when it's back up. The product's already in the tanks, you're not losing much except time.\n\n**Two:** Move the rest of the run to another line. That's a pain — you lose the fill-up you already paid, you're re-doing the setup on the new line, and you need that line to be free *and* qualified for the product. But if Line 2's going to be down half a shift or more and I've got capacity elsewhere, sometimes I'll do it.\n\n**Three:** Scrap what's in progress and restart the whole run later. I almost never do that unless something's really wrong — like if QA says the batch is already off-spec, or the line's going to be down for days. Too wasteful.\n\n*(leans back)*\n\nWhat decides? Mostly how long maintenance thinks it'll take, and whether I've got another line that can take it without screwing up something more urgent. And honestly... *(shrugs)* gut feel. If it's a Meridian order and the due date's tight, I'm more aggressive about moving it. If it's a small distributor order that can slip a few days, I'll wait.\n\n## Assistant\nAlright, so you've got three lines with different capabilities. Let me make sure I understand the constraints.\n\n**You mentioned Line 2 is faster for whites and it's the only one qualified for Meridian. What are the actual differences between your three lines — what can run where, and how do their speeds compare?**\n\n## User\n*(settles in)*\n\nOkay, so — **Line 1** is the old workhorse. It's slower, but it's qualified for everything. All the whites, all the tints, and all the specialty clears. It's the reliable one, but you pay for that in speed.\n\n**Line 2** is the fast line. It's about twice as fast as Line 1, at least on whites — that's where you really see it. That's why all the Meridian whites go there; the volume needs that speed. But Line 2 was never piped for the specialty resins, so it can't run the clears at all. Whites and tints only.\n\n**Line 3** is the newest. It's quick — somewhere between the two, maybe closer to Line 2's speed. But it's still being qualified product by product. It can run most of the whites and some of the tints, but there are still a couple tint SKUs that haven't been signed off yet. And it does specialties — that's newer, but it's qualified for them.\n\n*(pauses, thinking)*\n\nOh, and Line 3 only runs day shift unless we get overtime approved. Lines 1 and 2 run two shifts normally.\n\nSo if I've got a big white order, I want Line 2. Specialty, it's Line 1 or 3. And if Line 2's jammed and I need to move a white... Line 1 or Line 3, depending on what's free and whether Line 3 is actually qualified for that particular SKU.\n\nDoes that cover it?\n\n## Assistant\nYes, that's clear. Now let's dig into the changeover piece, since that's the big trade-off you're making.\n\n**You said white-to-tint is 45 minutes and tint-to-white is a full 3-hour washdown. What about the other combinations — white-to-white, tint-to-tint, and anything involving the specialty clears? And does it matter which line, or are those times pretty much the same across all three?**\n\n## User\n[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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.\n\n## Assistant\n```runbook-ir\n# Coatings Plant Production Scheduling Model\n\n## Model Purpose and Objectives\n\n**Primary simulation questions:**\n- Are orders getting out on time?\n- How many changeover hours are being consumed?\n- Is there a smarter sequence that reduces changeover time?\n- What is the best reshuffle when Line 2 goes down?\n\n**Intended decision support:**\n- Test scheduling decisions before making them in production\n- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order\n- Pre-plan responses to Line 2 filler failures\n\n**Success measures:**\n- On-time delivery (especially Meridian customer - risk of fines and delisting if late)\n- Changeover hours (boss wants these reduced to recover capacity)\n- Ability to recover lost time through better sequencing\n\n**Audience:** Master scheduler and management\n\n## Process Boundary and Triggers\n\n**Boundary:**\n- Starts: Monday morning ~8 AM when demand book drops from ERP\n- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday)\n- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping\n- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed)\n\n**Trigger:** \n- Weekly demand book arrives Monday ~8 AM\n- Spreadsheet with 40-60 orders per week (can reach 60 when busy)\n- Each order: SKU, quantity, due date\n\n**Horizon:** One-week planning cycle (Monday-Friday)\n\n## Product Families and Distinctions\n\n**Product families** (operation treats these differently due to changeover costs):\n- **Whites** — high volume\n- **Tinted colors** — moderate volume\n- **Specialty clears** — handful per week\n\n**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible.\n\n## Resources: Production Lines\n\n### Line 1\n- **Description:** Old workhorse\n- **Speed:** Slower (baseline)\n- **Qualification:** Everything — all whites, all tints, all specialty clears\n- **Availability:** Two shifts normally\n- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up)\n\n### Line 2\n- **Description:** Fast line\n- **Speed:** About 2x Line 1 speed on whites (\"that's where you really see it\")\n- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins)\n- **Availability:** Two shifts normally\n- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago)\n- **Reliability issue:** Filler jams every week or two\n- **Notes:** Better holding tank between mill and fill than Line 1\n\n### Line 3\n- **Description:** Newest line\n- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed\n- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently)\n- **Availability:** Day shift only unless overtime approved\n- **Notes:** Still expanding qualification list\n\n**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3.\n\n## Process Spine: One Order From Demand Book to Ship\n\n**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday\n\n### 1. Demand book arrival (Monday ~8 AM)\nOrder appears as line in spreadsheet: SKU, quantity, due date.\n\n### 2. Scheduler builds allocation sheet\n- **Activity:** Match orders to lines, sequence them\n- **Performer:** Master scheduler\n- **Approach:** Group orders by product family when possible (minimize expensive family-switches)\n- **Constraints applied:**\n - Meridian whites → Line 2 (mandatory)\n - Specialty clears → Line 1 or Line 3 only\n - Line 3 → only if SKU qualified and capacity available\n- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time\n- **Check:** Does it all fit in the week?\n\n**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these \"in my head — or in the sheet.\" Fill-up times by line or product.\n\n### 3. Daily floor huddle (every morning 7:30 AM)\n- **Participants:** Scheduler, line leads, maintenance, QA\n- **Topics:** What finished overnight, what's running now, any problems\n- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip)\n- **Execution:** Verbal adjustments, people go execute\n\n### 4. Order waits for sequenced slot\n**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2.\n\n### 5. Changeover\n**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system.\n\n**Known changeover times:**\n- White → white: 20-30 minutes (quick rinse)\n- White → tint: ~45 minutes\n- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch)\n\n**NOT YET ASKED:**\n- Tint → tint changeover time\n- Specialty clear changeover times (to/from whites, tints, other clears)\n- Whether changeover times vary by line\n\n**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). \"Not so bad\" after quick rinse.\n\n**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures.\n\n### 6. Production run\n**Stages (in sequence):**\n1. **Mix:** Blend base resin with additives in mix tank\n2. **Mill:** Grind to particle size\n3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment)\n4. **Fill and pack:** Into cans, labeled, palletized\n\n**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank.\n\n**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput.\n\n**Run time example:** 800 units on Line 2 for whites took \"about half a shift\" (quantity at Line 2 rate + fill-up time + ramp settling after changeover).\n\n**NOT YET ASKED:** Hours per shift. Specific production rates.\n\n### 7. QA hold\n- **Duration:** ~4 hours for whites; sometimes full day for specialty\n- **Activity:** Lab pulls samples, runs tests\n- **Resource:** 2-person lab\n- **Congestion:** Backs up end of week\n\n**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned \"adjust and retest\" as occasional issue).\n\n### 8. Ship\nOnce QA clears, order ships.\n\n**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date).\n\n**Reality qualifier (from scheduler):** \"That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest.\"\n\n## Disruptions and Recovery\n\n### Line 2 filler jam (occurs every week or two)\n\n**Last occurrence (2-3 weeks ago):**\n- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM\n- Cause: Bag broke in hopper, made a mess, whole thing locked up\n- Maintenance estimate at huddle: \"at least a couple hours\"\n- Actual duration: \"more like half a shift\"\n\n**Three options when line goes down mid-run:**\n\n1. **Wait it out**\n - When: Maintenance says 1-2 hours and run almost done\n - Product already in tanks, only losing time\n \n2. **Move rest of run to another line**\n - Cost: Lose fill-up already paid, redo setup on new line\n - Prerequisites: New line must be free AND qualified for the product\n - When: Line will be down half a shift or more AND capacity available elsewhere\n \n3. **Scrap in-progress, restart whole run later**\n - When: Almost never; only if batch already off-spec or line down for days\n - Reason: Too wasteful\n\n**Decision factors (scheduler's account):**\n- Maintenance time estimate\n- Availability of another qualified line\n- Whether moving would \"screw up something more urgent\"\n- \"Gut feel\"\n- If Meridian order with tight due date → more aggressive about moving\n- If small distributor order that can slip a few days → wait\n\n**NOT YET ASKED:**\n- Frequency distribution of Line 2 jams\n- Duration distribution of Line 2 downtime\n- Frequency and nature of \"materials occasionally short\"\n- What happens to work already in holding tanks when line stops mid-run\n\n### Other disruptions mentioned but not detailed:\n- Batch fails QA hold → adjust and retest (frequency and impact not asked)\n- Materials slip (frequency, which materials, advance warning not asked)\n\n## Scheduling Constraints and Policies\n\n### Hard constraints (from scheduler account):\n- Meridian whites MUST go on Line 2 (customer requirement, audited that line)\n- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins)\n- Line 3 SKU-by-SKU qualification (some tints not yet signed off)\n\n### Practiced policies:\n- Group orders by product family when possible (minimize changeover cost)\n- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown)\n- When contention exists: Meridian orders get priority (due to penalty/delisting risk)\n\n### Trade-off under uncertainty (scheduler's stated dilemma):\n- If Line 2 finishing a white and another white order \"coming in a couple hours,\" hold line idle vs. wash down to tint?\n- Scheduler \"thinks waiting sometimes makes sense\" but cannot prove it\n- Boss wants fewer changeover hours\n\n**NOT YET ASKED:**\n- How \"couple hours\" or other wait-time thresholds factor into practiced decision\n- Whether orders actually arrive during the week or all appear Monday in demand book\n- Whether partial orders or rush orders ever interrupt the plan\n\n## Quantities, Rates, Time\n\n**Known:**\n- Demand book: 40-60 orders/week\n- Example order: 800 units\n- Line 2 speed: ~2x Line 1 on whites\n- Line 3 speed: between L1 and L2, closer to L2\n- Line 2 on 800-unit white: \"about half a shift\"\n- Changeover times: see Process Spine section 5\n- QA hold: ~4 hours whites, up to full day specialty\n- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime)\n\n**NOT YET ASKED:**\n- Specific units/hour rates by product-line combination\n- Hours per shift\n- Fill-up time by line or product\n- Ramp scrap quantities\n- Distribution of order sizes\n- Distribution of due dates within the week\n- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty)\n- Whether batches have minimum or maximum sizes\n- Line 2 jam frequency (every week or two → distribution?)\n- Line 2 downtime duration (couple hours to half shift → distribution?)\n\n## Initial Conditions and State\n\n**NOT YET ASKED:**\n- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?)\n- Are there any orders in progress or in QA hold from prior week?\n- Initial inventory or work-in-process?\n\n## Validation and Evidence Sources\n\n**Validation intent (from scheduler):**\n- Model should show on-time delivery performance\n- Model should count changeover hours\n- Model should allow testing alternative sequences\n- Model should support pre-planning reshuffles when Line 2 fails\n\n**NOT YET ASKED:**\n- What observation, replay, or comparison would make the model credible enough to use?\n- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)?\n- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet?\n\n## Open Questions and Unresolved Material\n\n### Critical for construction but not yet asked:\n1. Specific production rates (units/hour) for product-line combinations\n2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line\n3. Fill-up times\n4. Ramp scrap quantities by changeover type\n5. Hours per shift\n6. Line 3 SKU qualification details\n7. Initial state at start of simulation week\n8. Whether orders can be split across lines or must run whole on one line\n9. Holding tank capacities and constraints\n10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages\n11. Due date distribution in demand book\n12. Product family distribution in demand book\n13. Validation: what would make model credible, what historical data exists\n\n### Consequential unknowns flagged by scheduler:\n- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail)\n- Exact rates (scheduler has them \"in my head — or in the sheet\" but not stated in interview)\n\n### Deliberate simplifications or omissions:\n- None explicitly proposed yet\n\n### Assumptions:\n- None explicitly introduced yet\n\n### Conflicts or corrections:\n- None yet\n\n### Contextual variations noted but not fully explored:\n- Line 2 downtime: \"couple hours\" vs. \"more like half a shift\" (context: initial estimate vs. actual)\n- QA hold: \"about 4 hours\" for whites, \"sometimes full day\" for specialty, \"backs up end of week\" (context-dependent duration)\n- Scheduler's practiced policy varies by customer urgency and due date pressure\n\n## Target Representation Notes\n\n**Target formalism:** Petri-net-style process model (specific format not known to scheduler; \"I'm not the modelling person\")\n\n**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined.\n\n**When construction begins, will need to infer:**\n- How to represent line eligibility constraints\n- How to represent scheduler's practiced priority rules under contention\n- How to represent three-option recovery logic when Line 2 fails\n- How to represent holding tanks and multi-stage production flow\n- Whether to model individual units, batches, or orders as tokens\n- How to represent ramp scrap and QA hold\n- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary)\n\n**Consequential gaps that block faithful construction:**\n- Missing rates prevent accurate time modeling\n- Missing changeover time matrix prevents accurate sequencing cost\n- Missing failure/disruption frequency distributions prevent realistic stochastic behavior\n- Missing initial state prevents simulation start\n- Missing validation criteria prevent assessing whether constructed model is fit for purpose\n```", + "rawConversationSnapshot": { + "v": 1, + "conversationId": "conv_01M1GYSWAQ10RSW9AY59K72BC6", + "offset": "0000000000000000_0000000000000261", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFHWVNXQU0zVlRDWDQ2VE04UjBIRjdO", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYSWAM3VTCX46TM8R0HF7N", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYT03XS7W04DVMENJ0S4RK", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYSWAM3VTCX46TM8R0HF7N", + "turnId": "turn_01M1GYSWBK0JP3CDNGE5VA6RMP", + "parts": [ + { + "type": "reasoning", + "text": "The user is a master scheduler at a coatings plant. They need a simulation model of their production scheduling process that can be represented as a Petri net. Their tooling runs \"Petri-net-style process models\" and the end product should be compatible with that, though they don't know the format details themselves.\n\nAccording to my instructions:\n1. I should activate the `sdcpn-modelling` skill before substantive elicitation\n2. I should use the person's vocabulary during elicitation, not Petri net terminology\n3. I should establish the purpose first - what decisions this model needs to help with\n4. I should follow concrete cases rather than traversing a schema\n5. I should keep the workpiece as understanding develops\n\nLet me activate the skill first, then begin with establishing the purpose and context.", + "state": "done" + }, + { + "type": "text", + "text": "I'll help you develop a simulation model of your scheduling operation. Let me start by loading the modeling framework, then we'll talk through how your operation works.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01N61ni3XJpbKBS1pZXxvpgD", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation, review, or revision\n\nInterview in the person's operational vocabulary. Before substantive\nelicitation or workpiece revision, read\n`references/universal-elicitation.md` and `references/profile.md`.\n\nFor review, first inspect the supplied workpiece or model. If it already answers\nthe question, answer without reading the elicitation references or interviewing.\nIf the review exposes a gap that requires human knowledge, read both elicitation\nreferences and then ask exactly one focused question that resolves the smallest\nconsequential gap.\n\nRead `templates/workpiece.md` only when first creating or materially revising\nthe workpiece. Do not load the template merely to inspect an existing artifact,\nanswer a resolvable review question, or frame the first elicitation question.\nConstruct only when the mounted capabilities actually permit construction in\nthis conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview.\nRead `references/pn-construction.md` and `references/checks.md`, then use the\nmounted construction tools. If a consequential workpiece gap prevents faithful\nconstruction, report the gap and the smallest question a later interactive\nelicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read\n`references/pn-construction.md` and `references/checks.md` before beginning.\nUse mounted Petrinaut tools for every net change and inspect the resulting\ndefinition rather than emitting free-form net JSON. If the required tools are\nabsent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted.\nDeliver the current workpiece in every branch. Deliver a net only when the\nmounted tool path has produced and checked one. State what the result can\nsupport, what remains open, what was assumed or simplified, and what the target\nor current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\n</skill_resources>", + "durationMs": 4 + }, + { + "type": "reasoning", + "text": "Good, the skill is loaded. Now I need to:\n1. Read the elicitation references before starting substantive elicitation\n2. Orient first - establish purpose and context\n3. Then begin interviewing in the person's operational vocabulary\n\nLet me read the two elicitation references first.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01LTMkcUNraBdu4AnqXYCNkp", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md" + }, + "output": "# Universal Elicitation Reference\n\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01GLhHLrYu5LK8sArEbjPgae", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Good. I've loaded both elicitation references. Now I understand the methodology:\n\n1. **Orient first** - Establish purpose, what decisions this needs to help make, audience, boundary, horizon\n2. **Ask conversationally** - One focused question at a time, following concrete cases in the person's vocabulary (not Petri net terms)\n3. **Build workpiece** - Maintain a recoverable account as we go\n4. **Then construct** - Use Petrinaut tools if available\n\nLet me start with orientation. I need to understand:\n- What specific scheduling decisions this simulation needs to test\n- What \"better\" means (objectives, measures)\n- Basic scope (boundary, horizon)\n- Who will use this\n\nI should ask one opening question that's answerable and gets at purpose.", + "state": "done" + }, + { + "type": "text", + "text": "Good. Now let's talk about how your operation actually works.\n\nBefore we dive into the details, help me understand what you're trying to accomplish with this simulation. **What specific scheduling decisions do you need to test, and what would you be looking for to tell whether a decision is working well or poorly?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVRYWjU2R003M1hCREY1WTFDMDBH", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYTXZ56GM73XBDF5Y1C00G", + "parts": [ + { + "type": "text", + "text": "*(settles into a chair, coffee mug in hand)*\n\nThe main thing? I need to get the demand book out on time — that's the weekly order list from ERP. Late orders are what gets me in trouble, especially with Meridian. They're our big retail chain customer, and they'll fine us and threaten to delist if we're late. So that's non-negotiable.\n\nBut beyond that... *(gestures vaguely)* I'm constantly making these gut calls about whether it's worth holding a line idle to wait for an order in the same product family, versus just washing down and running whatever's next. Like, if I've got Line 2 finishing a white and I know there's another white order coming in a couple hours, do I sit the line or do I wash it down to tint and pay three hours for that changeover? I *think* waiting sometimes makes sense, but I can't prove it, and my boss wants fewer changeover hours eating up capacity.\n\nAnd when a line goes down at six in the morning — which happens, especially the filler on Line 2 — I'm re-juggling the whole sheet at the huddle, and it's all improvisation. It'd be nice to know ahead of time what the best reshuffle actually is.\n\n*(pauses)*\n\nSo I guess what I'm looking for is: are we getting orders out on time, how many changeover hours are we actually burning, and whether there's a smarter way to sequence things that claws back some of that lost time. Does that make sense?", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYTZH61P8R8PZG5XYRWN6K", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYTXZ56GM73XBDF5Y1C00G", + "turnId": "turn_01M1GYTXZE3ZPD84SE36RXBFV3", + "parts": [ + { + "type": "reasoning", + "text": "Good opening. The person has given me a clear set of objectives and some concrete operational context. Let me capture what I've learned:\n\nObjectives:\n1. Get the demand book (weekly order list from ERP) out on time - this is critical, especially for Meridian customer who will fine them and threaten delisting\n2. Reduce changeover hours (currently eating up capacity)\n3. Handle line-down events better - when equipment breaks (especially Line 2 filler), need to know best way to reshuffle\n\nThe specific decision pattern mentioned:\n- Whether to hold a line idle waiting for a similar-family order vs. washing down and running what's next\n- Example: Line 2 finishing a white paint, another white order coming in a couple hours - wait or wash down to tint (3 hour changeover cost)\n\nSuccess measures:\n- On-time delivery\n- Changeover hours consumed\n- Better sequencing to recover lost time\n\nThis is excellent. I now have purpose and context. I should follow up with a concrete case to understand the process spine - how orders actually move through the operation. I should pick up on the specific situation mentioned (Line 2, white to tint changeover) or ask about a recent typical week.\n\nLet me follow the concrete case approach - ask them to walk me through a recent week or a specific order, so I can understand:\n- What starts work (the demand book arrival?)\n- How orders get assigned to lines\n- What a changeover actually involves\n- What happens when a line goes down\n- What ends/completes an order\n\nI should ask one focused question that gets at the process spine, using their vocabulary.", + "state": "done" + }, + { + "type": "text", + "text": "That makes perfect sense. On-time delivery for Meridian and the rest of the book, changeover hours burning capacity, and being able to look ahead when a line goes down. Those are concrete things I can work with.\n\nLet me follow a typical case so I understand how this actually flows. **Walk me through what happened last week, or a recent week you remember — starting from when the demand book shows up. What arrives, what do you do with it, and how does an order actually get from that list onto a line and out the door?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVc1NFlNUzJSTkRCTUtYVFpZS1lX", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYW54YMS2RNDBMKXTZYKYW", + "parts": [ + { + "type": "text", + "text": "*(leans back, thinking)*\n\nOkay, so... Monday morning, usually around 8 AM, the demand book drops from ERP. It's a spreadsheet — maybe 40, 50 orders that week, could be 60 if we're busy. Each line is an SKU, how many units, and a due date. Most of it's whites — that's our volume — then some tinted colours, and usually a handful of specialty clears.\n\nI take that list and I build the sheet. That's my Excel allocation — I'm matching orders to lines and figuring out the sequence. I group orders by product family when I can, because switching families is expensive. White to tint is maybe 45 minutes, but tint back to white? Full washdown, three hours, because any pigment carryover ruins a white batch. So I'm trying to keep whites together, tints together.\n\nI also know some products can only run certain places. Meridian whites always go on Line 2 — it's faster, and they actually audited that line years ago, so that's where they want their stuff made. Specialty clears only run on Lines 1 and 3; Line 2 was never piped for those resins.\n\n*(sips coffee)*\n\nOnce I've got the sequence roughed out, I'm estimating times. Each product-line combo has a rate in my head — or in the sheet, really. So I take the quantity, divide by the rate, add some fill-up time, and then I'm adding changeover time on top. That gives me a rough idea if it all fits in the week.\n\nThen the week starts running. Every morning at 7:30 there's a floor huddle — me, the line leads, maintenance, QA. We talk about what finished overnight, what's running now, any problems. And I'm adjusting on the fly. A line jams, a batch fails QA hold, materials slip... I'm moving stuff around verbally, and people go execute.\n\nOrders finish, sit in QA hold for a few hours — maybe four, longer for specialty — then they ship. By Friday afternoon we've usually cleared most of it. Sometimes something slips to Monday.\n\nDoes that give you the picture?", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYW6BWAFESSYHAQKH1EJJ1", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYW54YMS2RNDBMKXTZYKYW", + "turnId": "turn_01M1GYW5551JRSQX97B94VGSKE", + "parts": [ + { + "type": "reasoning", + "text": "Good concrete account. Let me capture what I've learned:\n\n**Purpose & objectives:**\n- On-time delivery (especially Meridian - penalties/delisting threat)\n- Minimize changeover hours (eating capacity)\n- Better sequencing decisions (wait vs. wash, reshuffle when line down)\n\n**Process spine emerging:**\n- Monday 8 AM: demand book arrives (spreadsheet, 40-60 orders, SKU/quantity/due date)\n- Scheduler builds \"the sheet\" (Excel allocation) - matching orders to lines, sequencing\n- Week runs: daily 7:30 huddle, adjustments\n- Orders finish → QA hold → ship\n- Usually clear by Friday afternoon, sometimes slip to Monday\n\n**Resources:**\n- Lines: 1, 2, 3 mentioned\n- Line 2: faster, Meridian-audited, Meridian whites only go here, NOT piped for specialty clears\n- Lines 1 & 3: can do specialty clears\n- Line 2 filler mentioned as problem-prone\n\n**Product families:**\n- Whites (volume)\n- Tinted colours \n- Specialty clears\n\n**Changeover times & constraints:**\n- White to tint: ~45 min\n- Tint to white: 3 hours (full washdown, pigment carryover ruins white)\n- So clustering by family matters; whites together, tints together\n\n**Processing:**\n- Rate varies by product-line combo\n- \"Fill-up time\" mentioned\n- QA hold: \"a few hours, maybe four, longer for specialty\"\n\n**Disruptions mentioned:**\n- Line jams\n- Batch fails QA hold\n- Materials slip\n- Line 2 filler goes down \"especially\"\n\nI should continue the concrete case. I need to understand:\n- What actually starts an order running (not just scheduling, but execution)\n- What's the detailed flow of one order\n- How long things take\n- What resources are actually contended\n- What the branches/failures look like\n- Initial conditions (lines state at start of week?)\n\nI think the sharpest way to fill these gaps is to trace one order end-to-end, from the moment it lands in the book to the moment it ships. That should surface timing, contention, and failure branches naturally.", + "state": "done" + }, + { + "type": "text", + "text": "Good, that's giving me the shape of it. Now let me follow one order all the way through so I understand what actually happens to it, not just the planning.\n\n**Pick one order from last week or the week before — maybe a Meridian white, since those matter most. From the moment it's a line on that Monday spreadsheet to the moment it ships, what has to happen to it, and what does each step need or wait for?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVg5SkQzWjdEMjk2MVNYSFZUQTIw", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYX9JD3Z7D2961SXHVTA20", + "parts": [ + { + "type": "text", + "text": "*(thinks for a moment)*\n\nAlright, let's say... Meridian order for VW-01, our high-volume interior white. 800 units, due Friday. That's on the sheet for Line 2, naturally.\n\nSo the order goes into my sequence — let's say I slot it for Tuesday afternoon, after we finish a run of another white that's already on the line. That's good because it's white-to-white, so the changeover is just a quick rinse, maybe 20, 30 minutes. The crew cleans out the residual from the last batch, flushes the system.\n\nOnce the line's clean, they start the run. First stage is **mix** — they're blending the base resin with additives in the mix tank. Then it moves to the **mill**, where it gets ground down to the right particle size. From there it goes to **tint and letdown** — for a white that's mostly just thinning it to spec, no pigment. Then finally **fill and pack** — into cans, labeled, palletized.\n\n*(gestures as if tracing the flow)*\n\nThere are holding tanks between those stages, so the mill can keep feeding while fill is catching up, or vice versa. Though... *(pauses)* the tank between mill and fill on Line 1 is tiny and it backs things up, but Line 2's is better.\n\nThe run takes... I don't know, with Line 2 on whites, maybe half a shift? That's 800 units at Line 2's rate plus the fill-up time and a bit of ramp while the line settles after the changeover. First few units after any changeover are scrap — ramp sc", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYXAP2F7WJHKGVVXZ9YBF2", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYX9JD3Z7D2961SXHVTA20", + "turnId": "turn_01M1GYX9JHAAWKY2JNS5RPBESG", + "parts": [ + { + "type": "reasoning", + "text": "The user is giving me a good concrete case. They started describing the flow through one order - VW-01, 800 units, Meridian white on Line 2. They were walking through:\n- Sequencing decision (placed Tuesday afternoon, after another white)\n- Changeover (white-to-white, quick rinse, 20-30 minutes)\n- Production stages: mix → mill → tint and letdown → fill and pack\n- Holding tanks between stages\n- Run duration estimate\n- They were starting to mention ramp scrap when they cut off\n\nThis is good process spine material. I should let them finish the thought about scrap, then continue following this order to completion - what happens after fill and pack? They mentioned QA hold earlier (4 hours, longer for specialty), then shipping. I need to understand:\n- What happens after fill and pack is complete\n- The QA hold - what triggers it, how long, what releases it\n- What makes it ready to ship\n- What could go wrong along the way\n\nI should also be noting:\n- Line assignments are not free choice - Meridian whites must go on Line 2 (audited), specialty clears only on Lines 1 and 3\n- Product families matter for changeover cost (white-to-tint 45 min, tint-to-white 3 hours due to washdown)\n- Rates vary by product-line combination\n- There's fill-up time\n- Ramp scrap after changeover\n- Holding tanks between stages (and they differ by line)\n- Things can go wrong: line jams, batch fails QA, materials slip\n\nLet me prompt them to finish the scrap thought and continue the order to completion.", + "state": "done" + }, + { + "type": "text", + "text": "*(let them finish the thought about scrap)* — go on, you were saying first few units are scrap?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVk0Tjc5UlZXOU1GMFhHUTZXQldU", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYY4N79RVW9MF0XGQ6WBWT", + "parts": [ + { + "type": "text", + "text": "*(nods)*\n\nYeah, ramp scrap. First few units after any changeover, the line's still settling — residual from the rinse, concentrations stabilizing, that kind of thing. Those units don't meet spec, so they get scrapped or reworked. It's worse after the big washdowns, like tint back to white. After a quick rinse it's not so bad.\n\n*(shrugs)*\n\nQuality tracks scrap as a monthly percentage, so I couldn't tell you exactly how much per changeover type. It's just... part of the cost of switching.\n\nAnyway, once the 800 units come off the line, they go into **QA hold**. The lab pulls samples, runs tests — usually takes about four hours for whites. Specialty sits longer, sometimes a full day. The lab's only two people, and end of week it backs up.\n\nOnce QA clears it, it ships. So for that Meridian order — if it runs Tuesday afternoon, finishes Tuesday night, clears QA Wednesday morning — it's out the door Wednesday, well ahead of Friday.\n\n*(takes another sip of coffee)*\n\nThat's the clean version, anyway. Reality is messier — the filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest. But that's the basic flow.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYY9WB0XZJKRQGR34GYQ9H", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYY4N79RVW9MF0XGQ6WBWT", + "turnId": "turn_01M1GYY4NDA9SFY1N6BVHXVA8Z", + "parts": [ + { + "type": "reasoning", + "text": "Good. I'm getting a clearer picture. Let me note what I'm learning:\n\n**Process spine so far:**\n1. Demand book arrives Monday morning (40-60 orders with SKU, quantity, due date)\n2. Scheduler builds allocation sheet (groups by product family, sequences orders to lines)\n3. Orders run on lines through stages: mix → mill → tint/letdown → fill/pack\n4. QA hold (4 hours whites, longer for specialty)\n5. Ship\n\n**Resources:**\n- 3 lines (Line 1, 2, 3) with different capabilities\n- Line 2: faster, Meridian-audited, whites and tints, no specialty clears, filler jams frequently\n- Lines 1 & 3: can run specialty clears\n- Line 1 has small tank between mill and fill (backs up)\n- QA lab: 2 people, backs up end of week\n\n**Product families:**\n- Whites (high volume)\n- Tints (colored)\n- Specialty clears (low volume)\n\n**Changeovers:**\n- White to white: quick rinse, 20-30 min\n- White to tint: ~45 min\n- Tint to white: full washdown, 3 hours (pigment carryover concern)\n- Ramp scrap after changeover (worse after big washdowns)\n\n**Key constraints/objectives:**\n- On-time delivery (especially Meridian - fines/delisting risk)\n- Minimize changeover hours (boss concern)\n- Line 2 filler jams frequently\n- QA lab can back up\n\n**Decisions the scheduler faces:**\n- Whether to hold line idle waiting for same-family order vs. changing over\n- How to reshuffle when line goes down\n- Sequencing to minimize changeovers while meeting due dates\n\nI need to understand more about:\n1. What happens when a line goes down - what gets rescheduled, what are the options?\n2. Whether the holding tanks between stages let stages run somewhat independently\n3. Actual run rates by product-line pairing\n4. Scope of retries after failed QA\n5. How material shortages get handled\n6. What resources are actually tied up when a line sits idle\n7. Sharper detail on which families can run on which lines\n\nI'll keep working through the concrete case, but push now on the disruptions and tradeoffs that came up.", + "state": "done" + }, + { + "type": "text", + "text": "Good, that's helpful. You mentioned the filler on Line 2 jams every week or two, and when a line goes down at six in the morning you're reshuffling at the huddle. \n\n**What actually happened the last time Line 2 went down? What was supposed to be running, what did you do with it, and where did you move things to?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVo4Q0g2MTJQUTFWNVhOUEJNMTJX", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYZ8CH612PQ1V5XNPBM12W", + "parts": [ + { + "type": "text", + "text": "*(grimaces slightly)*\n\nOh, that was... two weeks ago? Maybe three. Line 2 was in the middle of a big white run — I think it was VW-03, the contractor-grade flat — and the filler jammed around 6 AM. Bag broke in the hopper, made a mess, whole thing locked up.\n\nAt the huddle, maintenance said it'd be at least a couple hours to clear and reset. Turned out to be more like half a shift, but we didn't know that yet.\n\n*(leans forward, gesturing)*\n\nSo I had to figure out what to do with the rest of that run. Line 1 was already on a tint — couldn't just stop that mid-batch. Line 3 was free, but... *(pauses)* Line 3 is slower on whites, and honestly I hate moving a half-finished run because you lose the setup, you're re-doing the fill-up, it's inefficient. So I decided to just wait it out on Line 2, let maintenance fix it, finish the run there.\n\nBut that pushed everything else back. I had a tint order that was supposed to start on Line 2 that afternoon — moved that to Line 1 the next morning instead, which meant Line 1 had to wash down from tint to... no, wait, it was", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYZ9T4DTW3FV8F12NJEBVB", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYZ8CH612PQ1V5XNPBM12W", + "turnId": "turn_01M1GYZ8CR66F01YD6H11Y3D5T", + "parts": [ + { + "type": "reasoning", + "text": "They're getting a bit tangled up trying to remember the exact details. This is natural - they're recalling a specific incident. I should let them work through it or offer to move on if the details aren't critical. But I want to understand:\n\n1. What happens when a line goes down during a run\n2. What their options are for reshuffling\n3. What constraints affect those choices\n\nI don't need the exact sequence of every move from that particular incident. I should help them focus on the key decisions and constraints rather than getting lost in reconstructing one messy day.", + "state": "done" + }, + { + "type": "text", + "text": "— it's fine, you don't need to reconstruct every move from that day. Let me ask it differently:\n\n**When Line 2 goes down mid-run like that, what are your actual options? Can you pause the run and restart it later on the same line, can you move it to another line, do you have to scrap what's in progress? And what decides which option you pick?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWVpWRVhXMUJEUVNDUzZUREhYOEU0", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYZVEXW1BDQSCS6TDHX8E4", + "parts": [ + { + "type": "text", + "text": "*(nods, relieved to speak generally)*\n\nOkay, yeah — so if a line goes down mid-run, I've basically got three options.\n\n**One:** Wait it out. If maintenance says it's going to be an hour or two, and the run's almost done anyway, I just let the line sit and finish it when it's back up. The product's already in the tanks, you're not losing much except time.\n\n**Two:** Move the rest of the run to another line. That's a pain — you lose the fill-up you already paid, you're re-doing the setup on the new line, and you need that line to be free *and* qualified for the product. But if Line 2's going to be down half a shift or more and I've got capacity elsewhere, sometimes I'll do it.\n\n**Three:** Scrap what's in progress and restart the whole run later. I almost never do that unless something's really wrong — like if QA says the batch is already off-spec, or the line's going to be down for days. Too wasteful.\n\n*(leans back)*\n\nWhat decides? Mostly how long maintenance thinks it'll take, and whether I've got another line that can take it without screwing up something more urgent. And honestly... *(shrugs)* gut feel. If it's a Meridian order and the due date's tight, I'm more aggressive about moving it. If it's a small distributor order that can slip a few days, I'll wait.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYZWKP4CPXRKRE42C8Q87G", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYZVEXW1BDQSCS6TDHX8E4", + "turnId": "turn_01M1GYZVF1B6ZK8041S529JHC3", + "parts": [ + { + "type": "reasoning", + "text": "Good operational detail emerging. Let me note what I'm learning:\n\n**Purpose:**\n- On-time delivery (especially Meridian - they fine/delist for late)\n- Minimize changeover hours (eating capacity)\n- Better sequencing decisions when disruptions happen\n\n**Key tensions:**\n- Wait for same product family vs. wash down and run next\n- How to reshuffle when line goes down\n\n**Process elements so far:**\n- Demand book arrives Monday ~8 AM, 40-60 orders, each has SKU, quantity, due date\n- Master scheduler builds allocation sheet (Excel), matching orders to lines, sequencing\n- Grouping by product family matters (changeover costs)\n- Product-line constraints (Meridian whites → Line 2 only; specialty clears → Lines 1&3 only)\n- Daily 7:30 huddle for adjustments\n- Orders run through: Mix → Mill → Tint/Letdown → Fill/Pack\n- Holding tanks between stages\n- After production → QA hold (4 hrs whites, ~1 day specialty) → Ship\n- Changeovers: white→tint 45 min, tint→white 3 hrs (full washdown)\n- Quick rinse (white-to-white) 20-30 min\n- Ramp scrap after changeovers (worse for big washdowns)\n- Line 2 filler jams every 1-2 weeks\n- Disruption responses: wait, move to another line, scrap (rare)\n\n**Gaps I need:**\n- How many lines total? (mentioned Lines 1, 2, 3)\n- Product families more precisely defined\n- Run durations/rates\n- What \"finish\" means for the run itself (vs QA vs ship)\n- Arrival pattern of demand book (always Monday? always full week?)\n- Initial state (what's running when week starts)\n- What qualifies a line to run a given product\n- Materials availability as a possible constraint\n- Batch/order sizing logic\n- Sequencing priority beyond family grouping\n- QA failure rate and downstream handling\n\nWorth probing next: line capacity differences, sequencing priority drivers, whether holding tanks matter for the simulation, and more changeover nuance. I'll start with line capabilities since that's already surfaced as a hard constraint.", + "state": "done" + }, + { + "type": "text", + "text": "Alright, so you've got three lines with different capabilities. Let me make sure I understand the constraints.\n\n**You mentioned Line 2 is faster for whites and it's the only one qualified for Meridian. What are the actual differences between your three lines — what can run where, and how do their speeds compare?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWjBYQUdDRVBSRjBSS01CV0U1OEFY", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ0XAGCEPRF0RKMBWE58AX", + "parts": [ + { + "type": "text", + "text": "*(settles in)*\n\nOkay, so — **Line 1** is the old workhorse. It's slower, but it's qualified for everything. All the whites, all the tints, and all the specialty clears. It's the reliable one, but you pay for that in speed.\n\n**Line 2** is the fast line. It's about twice as fast as Line 1, at least on whites — that's where you really see it. That's why all the Meridian whites go there; the volume needs that speed. But Line 2 was never piped for the specialty resins, so it can't run the clears at all. Whites and tints only.\n\n**Line 3** is the newest. It's quick — somewhere between the two, maybe closer to Line 2's speed. But it's still being qualified product by product. It can run most of the whites and some of the tints, but there are still a couple tint SKUs that haven't been signed off yet. And it does specialties — that's newer, but it's qualified for them.\n\n*(pauses, thinking)*\n\nOh, and Line 3 only runs day shift unless we get overtime approved. Lines 1 and 2 run two shifts normally.\n\nSo if I've got a big white order, I want Line 2. Specialty, it's Line 1 or 3. And if Line 2's jammed and I need to move a white... Line 1 or Line 3, depending on what's free and whether Line 3 is actually qualified for that particular SKU.\n\nDoes that cover it?", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ0Z20FXGR93K140T44T0T", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ0XAGCEPRF0RKMBWE58AX", + "turnId": "turn_01M1GZ0XAPPX1B8B9HE9RSK3JJ", + "parts": [ + { + "type": "reasoning", + "text": "The user is providing good detail about the lines. Let me make sure I understand:\n\n- Line 1: Slow, but can run everything (whites, tints, specialty clears). Runs two shifts.\n- Line 2: Fast (about 2x Line 1 on whites), can run whites and tints, but NOT specialty clears. Runs two shifts. Meridian whites must run here.\n- Line 3: Speed between Line 1 and Line 2 (closer to Line 2). Can run most whites, some tints (still being qualified), and specialties. Only runs day shift unless overtime approved.\n\nThis is good coverage. Now I should understand:\n1. What actually makes a changeover take the time it does (we know white-to-tint is 45 min, tint-to-white is 3 hours full washdown)\n2. The rates/times for different product families on different lines\n3. How QA hold works and what can go wrong there\n4. What happens with materials shortages\n5. Batch failures and rework\n\nI should continue following the concrete operational thread. Let me dig into the changeover times and product family structure more, since that's core to the scheduling decision about waiting vs. switching.", + "state": "done" + }, + { + "type": "text", + "text": "Yes, that's clear. Now let's dig into the changeover piece, since that's the big trade-off you're making.\n\n**You said white-to-tint is 45 minutes and tint-to-white is a full 3-hour washdown. What about the other combinations — white-to-white, tint-to-tint, and anything involving the specialty clears? And does it matter which line, or are those times pretty much the same across all three?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWjE4SkdFNldTMUM1WVFIVFQ1WFZN", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ18JGE6WS1C5YQHTT5XVM", + "parts": [ + { + "type": "text", + "text": "[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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ1BQQ6Y1SBS51E3K1W0NE", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ18JGE6WS1C5YQHTT5XVM", + "turnId": "turn_01M1GZ18JP076F2JFF44D3AC0C", + "parts": [ + { + "type": "reasoning", + "text": "The user has issued a stop instruction. I need to:\n1. Not ask another question\n2. Not construct the Petri net\n3. Not read construction resources\n4. Emit the full current workpiece in a `runbook-ir` fenced block\n5. Preserve all unknowns, not-yet-asked items, assumptions, conflicts, etc.\n\nLet me build the workpiece from what I've learned so far:\n\n**Purpose/Objective:**\n- Test scheduling decisions before making them\n- Assess on-time delivery (especially for Meridian customer - fines and delisting risk)\n- Measure changeover hours consumption\n- Find smarter sequencing to recover lost time\n- Evaluate reshuffling options when Line 2 goes down\n\n**Boundary and triggers:**\n- Monday morning around 8 AM: demand book drops from ERP\n- 40-60 orders per week (spreadsheet with SKU, quantity, due date)\n- Week runs Monday-Friday, sometimes slips to Monday\n\n**Product families:**\n- Whites (high volume)\n- Tinted colors\n- Specialty clears\n\n**Resources - Lines:**\n- Line 1: Old workhorse, slower, qualified for everything (whites, tints, specialty clears), runs two shifts\n- Line 2: Fast line (about 2x Line 1 speed on whites), whites and tints only (no specialty resins), runs two shifts, Meridian-qualified and required for Meridian whites, filler jams every week or two\n- Line 3: Newest, speed between L1 and L2 (closer to L2), being qualified product-by-product (most whites, some tints, specialties), day shift only unless overtime approved\n\n**Process spine (one order example: Meridian VW-01, 800 units):**\n1. Order appears in demand book Monday 8 AM\n2. Scheduler builds allocation sheet - matches orders to lines, sequences them, groups by product family\n3. Estimates times (quantity/rate + fill-up time + changeover time)\n4. Daily 7:30 AM floor huddle (scheduler, line leads, maintenance, QA) reviews overnight runs, current status, problems, and adjusts on the fly\n5. Order waits for its sequenced slot\n6. Changeover occurs (white-to-white quick rinse: 20-30 min)\n7. Run executes through mix, mill, tint/letdown (whites mostly just thinning to spec, no pigment), then fill and pack into cans\n\n, labeled, palletized - with holding tanks between stages (Line 2's tank between mill and fill beats Line 1's tiny one)\n8. Scrap piles up right after changeovers as first units miss spec, worse post-washdown\n9. QA hold runs ~4 hrs for whites, up to a full day for specialty, with only a 2-person lab backing things up by week's end\n10. Ships after QA clears\n\nKnown changeover times: white-to-tint ~45 min, tint-to-white 3 hrs (full washdown needed - pigment carryover ruins whites), white-to-white 20-30 min. Still need tint-to-tint, specialty combos, and whether times vary by line.\n\nLine eligibility gets specific: Meridian whites must run on Line 2 only (audited line), specialty clears only on Lines 1 and 3, whites can run anywhere but speeds differ and Line 3 qualification is SKU-specific, tints mainly Lines 1/2 with some Line 3 SKUs qualified.\n\nDisruptions include weekly Line 2 filler jams, material shortages, and QA-driven adjustments. Mid-run breakdowns trigger three options - wait it out, shift remaining run to another qualified line (losing fill-up, redoing setup), or scrap and restart (rare). Decisions hinge on repair time, line availability/qualification, and urgency - Meridian orders push harder.\n\nOn rates: Line 2 does 800 whites units in about half a shift, running roughly 2x Line 1's speed. Line 3 sits between the two, closer to Line 2.\n\nQA hold times differ by product - whites ~4 hours, specialty sometimes a full day, with the 2-person lab creating backups by week's end.\n\nScrap accumulates at every changeover, worsening after big washdowns like tint-to-white versus quick rinses. Tracked monthly as a percentage, though exact per-changeover figures aren't available.\n\nShift structure: Lines 1 and 2 run two shifts, Line 3 stays day-shift only unless overtime's approved. The week runs Monday-Friday with occasional Monday slippage.\n\nStill need actual production rates, changeover times by type and line, fill-up times, exact scrap quantities, batch sizes, shift length, and the specific Line 3 SKU list.\n\nBeyond that, gaps remain around materials arrival patterns, Line 2's jam frequency and downtime distribution, QA failure rates, due date spread in the demand book, product family mix, whether orders can split across lines, holding tank capacity limits, what \"materials short\" actually covers, starting state of each line, extra setup costs, Meridian penalty thresholds, other SLAs, and cost tradeoffs for optimization.\n\nFor validation, the person wants to test on-time delivery, changeover hours, and alternative sequencing - though what would make them trust the model as realistic still needs asking.\n\nContext: the scheduler's \"gut calls\" on waiting versus washdown, the push to prove same-family waiting logic, the boss's demand for fewer changeover hours, verbal huddle adjustments, and an admission that reality is messier than the clean description given. No assumptions or conflicts identified yet.", + "state": "done" + }, + { + "type": "text", + "text": "```runbook-ir\n# Coatings Plant Production Scheduling Model\n\n## Model Purpose and Objectives\n\n**Primary simulation questions:**\n- Are orders getting out on time?\n- How many changeover hours are being consumed?\n- Is there a smarter sequence that reduces changeover time?\n- What is the best reshuffle when Line 2 goes down?\n\n**Intended decision support:**\n- Test scheduling decisions before making them in production\n- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order\n- Pre-plan responses to Line 2 filler failures\n\n**Success measures:**\n- On-time delivery (especially Meridian customer - risk of fines and delisting if late)\n- Changeover hours (boss wants these reduced to recover capacity)\n- Ability to recover lost time through better sequencing\n\n**Audience:** Master scheduler and management\n\n## Process Boundary and Triggers\n\n**Boundary:**\n- Starts: Monday morning ~8 AM when demand book drops from ERP\n- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday)\n- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping\n- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed)\n\n**Trigger:** \n- Weekly demand book arrives Monday ~8 AM\n- Spreadsheet with 40-60 orders per week (can reach 60 when busy)\n- Each order: SKU, quantity, due date\n\n**Horizon:** One-week planning cycle (Monday-Friday)\n\n## Product Families and Distinctions\n\n**Product families** (operation treats these differently due to changeover costs):\n- **Whites** — high volume\n- **Tinted colors** — moderate volume\n- **Specialty clears** — handful per week\n\n**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible.\n\n## Resources: Production Lines\n\n### Line 1\n- **Description:** Old workhorse\n- **Speed:** Slower (baseline)\n- **Qualification:** Everything — all whites, all tints, all specialty clears\n- **Availability:** Two shifts normally\n- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up)\n\n### Line 2\n- **Description:** Fast line\n- **Speed:** About 2x Line 1 speed on whites (\"that's where you really see it\")\n- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins)\n- **Availability:** Two shifts normally\n- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago)\n- **Reliability issue:** Filler jams every week or two\n- **Notes:** Better holding tank between mill and fill than Line 1\n\n### Line 3\n- **Description:** Newest line\n- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed\n- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently)\n- **Availability:** Day shift only unless overtime approved\n- **Notes:** Still expanding qualification list\n\n**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3.\n\n## Process Spine: One Order From Demand Book to Ship\n\n**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday\n\n### 1. Demand book arrival (Monday ~8 AM)\nOrder appears as line in spreadsheet: SKU, quantity, due date.\n\n### 2. Scheduler builds allocation sheet\n- **Activity:** Match orders to lines, sequence them\n- **Performer:** Master scheduler\n- **Approach:** Group orders by product family when possible (minimize expensive family-switches)\n- **Constraints applied:**\n - Meridian whites → Line 2 (mandatory)\n - Specialty clears → Line 1 or Line 3 only\n - Line 3 → only if SKU qualified and capacity available\n- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time\n- **Check:** Does it all fit in the week?\n\n**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these \"in my head — or in the sheet.\" Fill-up times by line or product.\n\n### 3. Daily floor huddle (every morning 7:30 AM)\n- **Participants:** Scheduler, line leads, maintenance, QA\n- **Topics:** What finished overnight, what's running now, any problems\n- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip)\n- **Execution:** Verbal adjustments, people go execute\n\n### 4. Order waits for sequenced slot\n**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2.\n\n### 5. Changeover\n**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system.\n\n**Known changeover times:**\n- White → white: 20-30 minutes (quick rinse)\n- White → tint: ~45 minutes\n- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch)\n\n**NOT YET ASKED:**\n- Tint → tint changeover time\n- Specialty clear changeover times (to/from whites, tints, other clears)\n- Whether changeover times vary by line\n\n**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). \"Not so bad\" after quick rinse.\n\n**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures.\n\n### 6. Production run\n**Stages (in sequence):**\n1. **Mix:** Blend base resin with additives in mix tank\n2. **Mill:** Grind to particle size\n3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment)\n4. **Fill and pack:** Into cans, labeled, palletized\n\n**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank.\n\n**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput.\n\n**Run time example:** 800 units on Line 2 for whites took \"about half a shift\" (quantity at Line 2 rate + fill-up time + ramp settling after changeover).\n\n**NOT YET ASKED:** Hours per shift. Specific production rates.\n\n### 7. QA hold\n- **Duration:** ~4 hours for whites; sometimes full day for specialty\n- **Activity:** Lab pulls samples, runs tests\n- **Resource:** 2-person lab\n- **Congestion:** Backs up end of week\n\n**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned \"adjust and retest\" as occasional issue).\n\n### 8. Ship\nOnce QA clears, order ships.\n\n**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date).\n\n**Reality qualifier (from scheduler):** \"That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest.\"\n\n## Disruptions and Recovery\n\n### Line 2 filler jam (occurs every week or two)\n\n**Last occurrence (2-3 weeks ago):**\n- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM\n- Cause: Bag broke in hopper, made a mess, whole thing locked up\n- Maintenance estimate at huddle: \"at least a couple hours\"\n- Actual duration: \"more like half a shift\"\n\n**Three options when line goes down mid-run:**\n\n1. **Wait it out**\n - When: Maintenance says 1-2 hours and run almost done\n - Product already in tanks, only losing time\n \n2. **Move rest of run to another line**\n - Cost: Lose fill-up already paid, redo setup on new line\n - Prerequisites: New line must be free AND qualified for the product\n - When: Line will be down half a shift or more AND capacity available elsewhere\n \n3. **Scrap in-progress, restart whole run later**\n - When: Almost never; only if batch already off-spec or line down for days\n - Reason: Too wasteful\n\n**Decision factors (scheduler's account):**\n- Maintenance time estimate\n- Availability of another qualified line\n- Whether moving would \"screw up something more urgent\"\n- \"Gut feel\"\n- If Meridian order with tight due date → more aggressive about moving\n- If small distributor order that can slip a few days → wait\n\n**NOT YET ASKED:**\n- Frequency distribution of Line 2 jams\n- Duration distribution of Line 2 downtime\n- Frequency and nature of \"materials occasionally short\"\n- What happens to work already in holding tanks when line stops mid-run\n\n### Other disruptions mentioned but not detailed:\n- Batch fails QA hold → adjust and retest (frequency and impact not asked)\n- Materials slip (frequency, which materials, advance warning not asked)\n\n## Scheduling Constraints and Policies\n\n### Hard constraints (from scheduler account):\n- Meridian whites MUST go on Line 2 (customer requirement, audited that line)\n- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins)\n- Line 3 SKU-by-SKU qualification (some tints not yet signed off)\n\n### Practiced policies:\n- Group orders by product family when possible (minimize changeover cost)\n- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown)\n- When contention exists: Meridian orders get priority (due to penalty/delisting risk)\n\n### Trade-off under uncertainty (scheduler's stated dilemma):\n- If Line 2 finishing a white and another white order \"coming in a couple hours,\" hold line idle vs. wash down to tint?\n- Scheduler \"thinks waiting sometimes makes sense\" but cannot prove it\n- Boss wants fewer changeover hours\n\n**NOT YET ASKED:**\n- How \"couple hours\" or other wait-time thresholds factor into practiced decision\n- Whether orders actually arrive during the week or all appear Monday in demand book\n- Whether partial orders or rush orders ever interrupt the plan\n\n## Quantities, Rates, Time\n\n**Known:**\n- Demand book: 40-60 orders/week\n- Example order: 800 units\n- Line 2 speed: ~2x Line 1 on whites\n- Line 3 speed: between L1 and L2, closer to L2\n- Line 2 on 800-unit white: \"about half a shift\"\n- Changeover times: see Process Spine section 5\n- QA hold: ~4 hours whites, up to full day specialty\n- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime)\n\n**NOT YET ASKED:**\n- Specific units/hour rates by product-line combination\n- Hours per shift\n- Fill-up time by line or product\n- Ramp scrap quantities\n- Distribution of order sizes\n- Distribution of due dates within the week\n- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty)\n- Whether batches have minimum or maximum sizes\n- Line 2 jam frequency (every week or two → distribution?)\n- Line 2 downtime duration (couple hours to half shift → distribution?)\n\n## Initial Conditions and State\n\n**NOT YET ASKED:**\n- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?)\n- Are there any orders in progress or in QA hold from prior week?\n- Initial inventory or work-in-process?\n\n## Validation and Evidence Sources\n\n**Validation intent (from scheduler):**\n- Model should show on-time delivery performance\n- Model should count changeover hours\n- Model should allow testing alternative sequences\n- Model should support pre-planning reshuffles when Line 2 fails\n\n**NOT YET ASKED:**\n- What observation, replay, or comparison would make the model credible enough to use?\n- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)?\n- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet?\n\n## Open Questions and Unresolved Material\n\n### Critical for construction but not yet asked:\n1. Specific production rates (units/hour) for product-line combinations\n2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line\n3. Fill-up times\n4. Ramp scrap quantities by changeover type\n5. Hours per shift\n6. Line 3 SKU qualification details\n7. Initial state at start of simulation week\n8. Whether orders can be split across lines or must run whole on one line\n9. Holding tank capacities and constraints\n10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages\n11. Due date distribution in demand book\n12. Product family distribution in demand book\n13. Validation: what would make model credible, what historical data exists\n\n### Consequential unknowns flagged by scheduler:\n- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail)\n- Exact rates (scheduler has them \"in my head — or in the sheet\" but not stated in interview)\n\n### Deliberate simplifications or omissions:\n- None explicitly proposed yet\n\n### Assumptions:\n- None explicitly introduced yet\n\n### Conflicts or corrections:\n- None yet\n\n### Contextual variations noted but not fully explored:\n- Line 2 downtime: \"couple hours\" vs. \"more like half a shift\" (context: initial estimate vs. actual)\n- QA hold: \"about 4 hours\" for whites, \"sometimes full day\" for specialty, \"backs up end of week\" (context-dependent duration)\n- Scheduler's practiced policy varies by customer urgency and due date pressure\n\n## Target Representation Notes\n\n**Target formalism:** Petri-net-style process model (specific format not known to scheduler; \"I'm not the modelling person\")\n\n**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined.\n\n**When construction begins, will need to infer:**\n- How to represent line eligibility constraints\n- How to represent scheduler's practiced priority rules under contention\n- How to represent three-option recovery logic when Line 2 fails\n- How to represent holding tanks and multi-stage production flow\n- Whether to model individual units, batches, or orders as tokens\n- How to represent ramp scrap and QA hold\n- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary)\n\n**Consequential gaps that block faithful construction:**\n- Missing rates prevent accurate time modeling\n- Missing changeover time matrix prevents accurate sequencing cost\n- Missing failure/disruption frequency distributions prevent realistic stochastic behavior\n- Missing initial state prevents simulation start\n- Missing validation criteria prevent assessing whether constructed model is fit for purpose\n```", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1GYSWAM3VTCX46TM8R0HF7N", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYSWAM3VTCX46TM8R0HF7N" + }, + { + "submissionId": "sub_01M1GYTXZ56GM73XBDF5Y1C00G", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYTXZ56GM73XBDF5Y1C00G" + }, + { + "submissionId": "sub_01M1GYW54YMS2RNDBMKXTZYKYW", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYW54YMS2RNDBMKXTZYKYW" + }, + { + "submissionId": "sub_01M1GYX9JD3Z7D2961SXHVTA20", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYX9JD3Z7D2961SXHVTA20" + }, + { + "submissionId": "sub_01M1GYY4N79RVW9MF0XGQ6WBWT", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYY4N79RVW9MF0XGQ6WBWT" + }, + { + "submissionId": "sub_01M1GYZ8CH612PQ1V5XNPBM12W", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYZ8CH612PQ1V5XNPBM12W" + }, + { + "submissionId": "sub_01M1GYZVEXW1BDQSCS6TDHX8E4", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYZVEXW1BDQSCS6TDHX8E4" + }, + { + "submissionId": "sub_01M1GZ0XAGCEPRF0RKMBWE58AX", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ0XAGCEPRF0RKMBWE58AX" + }, + { + "submissionId": "sub_01M1GZ18JGE6WS1C5YQHTT5XVM", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ18JGE6WS1C5YQHTT5XVM" + } + ], + "incarnation": "inc_01M1GYSWANR7ZP6KFT22YM0RQF" + }, + "rawConversationSnapshotSha256": "ee1722a6214db6ce0ef38fa4adf207f055943e43009ea3477f662c9b5995e8b7" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.md new file mode 100644 index 00000000000..cff52fa4039 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.md @@ -0,0 +1,492 @@ +# Prospective candidate runbook elicitation — replication 1 + +- Protocol: `prospective-runbook-v3` +- Output namespace: `vestera-architecture-candidate-v3` +- Run: `prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006` +- Source commit: `794fe2fbf1eaeba3fc816c6e3d1755d7b444125d` +- Interviewer: `claude-sonnet-4-5` +- Simulated expert: `claude-sonnet-4-5` +- Interview turns: 8 (hard stop 8) +- Stop reason before final IR request: `hard-stop` +- Recoverable IR: yes +- Member status: `completed` +- Final user message is an evaluation stop instruction, not expert evidence. + +## User +I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we +schedule production, so we can test scheduling decisions before making them. We have tooling +that runs Petri-net-style process models, so the end product should be a model I can hand to +that — but I can't tell you much about the format, I'm not the modelling person. + +Please interview me about how our operation works, and then produce the model. + +## Assistant +I'll help you develop a simulation model of your scheduling operation. Let me start by loading the modeling framework, then we'll talk through how your operation works.Good. Now let's talk about how your operation actually works. + +Before we dive into the details, help me understand what you're trying to accomplish with this simulation. **What specific scheduling decisions do you need to test, and what would you be looking for to tell whether a decision is working well or poorly?** +- tool activate_skill (toolu_01N61ni3XJpbKBS1pZXxvpgD): "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation, review, or revision\n\nInterview in the person's operational vocabulary. Before substantive\nelicitation or workpiece revision, read\n`references/universal-elicitation.md` and `references/profile.md`.\n\nFor review, first inspect the supplied workpiece or model. If it already answers\nthe question, answer without reading the elicitation references or interviewing.\nIf the review exposes a gap that requires human knowledge, read both elicitation\nreferences and then ask exactly one focused question that resolves the smallest\nconsequential gap.\n\nRead `templates/workpiece.md` only when first creating or materially revising\nthe workpiece. Do not load the template merely to inspect an existing artifact,\nanswer a resolvable review question, or frame the first elicitation question.\nConstruct only when the mounted capabilities actually permit construction in\nthis conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview.\nRead `references/pn-construction.md` and `references/checks.md`, then use the\nmounted construction tools. If a consequential workpiece gap prevents faithful\nconstruction, report the gap and the smallest question a later interactive\nelicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read\n`references/pn-construction.md` and `references/checks.md` before beginning.\nUse mounted Petrinaut tools for every net change and inspect the resulting\ndefinition rather than emitting free-form net JSON. If the required tools are\nabsent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted.\nDeliver the current workpiece in every branch. Deliver a net only when the\nmounted tool path has produced and checked one. State what the result can\nsupport, what remains open, what was assumed or simplified, and what the target\nor current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\n</skill_resources>" +- tool read_skill_resource (toolu_01LTMkcUNraBdu4AnqXYCNkp): "# Universal Elicitation Reference\n\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n" +- tool read_skill_resource (toolu_01GLhHLrYu5LK8sArEbjPgae): "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n" + +## User +*(settles into a chair, coffee mug in hand)* + +The main thing? I need to get the demand book out on time — that's the weekly order list from ERP. Late orders are what gets me in trouble, especially with Meridian. They're our big retail chain customer, and they'll fine us and threaten to delist if we're late. So that's non-negotiable. + +But beyond that... *(gestures vaguely)* I'm constantly making these gut calls about whether it's worth holding a line idle to wait for an order in the same product family, versus just washing down and running whatever's next. Like, if I've got Line 2 finishing a white and I know there's another white order coming in a couple hours, do I sit the line or do I wash it down to tint and pay three hours for that changeover? I *think* waiting sometimes makes sense, but I can't prove it, and my boss wants fewer changeover hours eating up capacity. + +And when a line goes down at six in the morning — which happens, especially the filler on Line 2 — I'm re-juggling the whole sheet at the huddle, and it's all improvisation. It'd be nice to know ahead of time what the best reshuffle actually is. + +*(pauses)* + +So I guess what I'm looking for is: are we getting orders out on time, how many changeover hours are we actually burning, and whether there's a smarter way to sequence things that claws back some of that lost time. Does that make sense? + +## Assistant +That makes perfect sense. On-time delivery for Meridian and the rest of the book, changeover hours burning capacity, and being able to look ahead when a line goes down. Those are concrete things I can work with. + +Let me follow a typical case so I understand how this actually flows. **Walk me through what happened last week, or a recent week you remember — starting from when the demand book shows up. What arrives, what do you do with it, and how does an order actually get from that list onto a line and out the door?** + +## User +*(leans back, thinking)* + +Okay, so... Monday morning, usually around 8 AM, the demand book drops from ERP. It's a spreadsheet — maybe 40, 50 orders that week, could be 60 if we're busy. Each line is an SKU, how many units, and a due date. Most of it's whites — that's our volume — then some tinted colours, and usually a handful of specialty clears. + +I take that list and I build the sheet. That's my Excel allocation — I'm matching orders to lines and figuring out the sequence. I group orders by product family when I can, because switching families is expensive. White to tint is maybe 45 minutes, but tint back to white? Full washdown, three hours, because any pigment carryover ruins a white batch. So I'm trying to keep whites together, tints together. + +I also know some products can only run certain places. Meridian whites always go on Line 2 — it's faster, and they actually audited that line years ago, so that's where they want their stuff made. Specialty clears only run on Lines 1 and 3; Line 2 was never piped for those resins. + +*(sips coffee)* + +Once I've got the sequence roughed out, I'm estimating times. Each product-line combo has a rate in my head — or in the sheet, really. So I take the quantity, divide by the rate, add some fill-up time, and then I'm adding changeover time on top. That gives me a rough idea if it all fits in the week. + +Then the week starts running. Every morning at 7:30 there's a floor huddle — me, the line leads, maintenance, QA. We talk about what finished overnight, what's running now, any problems. And I'm adjusting on the fly. A line jams, a batch fails QA hold, materials slip... I'm moving stuff around verbally, and people go execute. + +Orders finish, sit in QA hold for a few hours — maybe four, longer for specialty — then they ship. By Friday afternoon we've usually cleared most of it. Sometimes something slips to Monday. + +Does that give you the picture? + +## Assistant +Good, that's giving me the shape of it. Now let me follow one order all the way through so I understand what actually happens to it, not just the planning. + +**Pick one order from last week or the week before — maybe a Meridian white, since those matter most. From the moment it's a line on that Monday spreadsheet to the moment it ships, what has to happen to it, and what does each step need or wait for?** + +## User +*(thinks for a moment)* + +Alright, let's say... Meridian order for VW-01, our high-volume interior white. 800 units, due Friday. That's on the sheet for Line 2, naturally. + +So the order goes into my sequence — let's say I slot it for Tuesday afternoon, after we finish a run of another white that's already on the line. That's good because it's white-to-white, so the changeover is just a quick rinse, maybe 20, 30 minutes. The crew cleans out the residual from the last batch, flushes the system. + +Once the line's clean, they start the run. First stage is **mix** — they're blending the base resin with additives in the mix tank. Then it moves to the **mill**, where it gets ground down to the right particle size. From there it goes to **tint and letdown** — for a white that's mostly just thinning it to spec, no pigment. Then finally **fill and pack** — into cans, labeled, palletized. + +*(gestures as if tracing the flow)* + +There are holding tanks between those stages, so the mill can keep feeding while fill is catching up, or vice versa. Though... *(pauses)* the tank between mill and fill on Line 1 is tiny and it backs things up, but Line 2's is better. + +The run takes... I don't know, with Line 2 on whites, maybe half a shift? That's 800 units at Line 2's rate plus the fill-up time and a bit of ramp while the line settles after the changeover. First few units after any changeover are scrap — ramp sc + +## Assistant +*(let them finish the thought about scrap)* — go on, you were saying first few units are scrap? + +## User +*(nods)* + +Yeah, ramp scrap. First few units after any changeover, the line's still settling — residual from the rinse, concentrations stabilizing, that kind of thing. Those units don't meet spec, so they get scrapped or reworked. It's worse after the big washdowns, like tint back to white. After a quick rinse it's not so bad. + +*(shrugs)* + +Quality tracks scrap as a monthly percentage, so I couldn't tell you exactly how much per changeover type. It's just... part of the cost of switching. + +Anyway, once the 800 units come off the line, they go into **QA hold**. The lab pulls samples, runs tests — usually takes about four hours for whites. Specialty sits longer, sometimes a full day. The lab's only two people, and end of week it backs up. + +Once QA clears it, it ships. So for that Meridian order — if it runs Tuesday afternoon, finishes Tuesday night, clears QA Wednesday morning — it's out the door Wednesday, well ahead of Friday. + +*(takes another sip of coffee)* + +That's the clean version, anyway. Reality is messier — the filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest. But that's the basic flow. + +## Assistant +Good, that's helpful. You mentioned the filler on Line 2 jams every week or two, and when a line goes down at six in the morning you're reshuffling at the huddle. + +**What actually happened the last time Line 2 went down? What was supposed to be running, what did you do with it, and where did you move things to?** + +## User +*(grimaces slightly)* + +Oh, that was... two weeks ago? Maybe three. Line 2 was in the middle of a big white run — I think it was VW-03, the contractor-grade flat — and the filler jammed around 6 AM. Bag broke in the hopper, made a mess, whole thing locked up. + +At the huddle, maintenance said it'd be at least a couple hours to clear and reset. Turned out to be more like half a shift, but we didn't know that yet. + +*(leans forward, gesturing)* + +So I had to figure out what to do with the rest of that run. Line 1 was already on a tint — couldn't just stop that mid-batch. Line 3 was free, but... *(pauses)* Line 3 is slower on whites, and honestly I hate moving a half-finished run because you lose the setup, you're re-doing the fill-up, it's inefficient. So I decided to just wait it out on Line 2, let maintenance fix it, finish the run there. + +But that pushed everything else back. I had a tint order that was supposed to start on Line 2 that afternoon — moved that to Line 1 the next morning instead, which meant Line 1 had to wash down from tint to... no, wait, it was + +## Assistant +— it's fine, you don't need to reconstruct every move from that day. Let me ask it differently: + +**When Line 2 goes down mid-run like that, what are your actual options? Can you pause the run and restart it later on the same line, can you move it to another line, do you have to scrap what's in progress? And what decides which option you pick?** + +## User +*(nods, relieved to speak generally)* + +Okay, yeah — so if a line goes down mid-run, I've basically got three options. + +**One:** Wait it out. If maintenance says it's going to be an hour or two, and the run's almost done anyway, I just let the line sit and finish it when it's back up. The product's already in the tanks, you're not losing much except time. + +**Two:** Move the rest of the run to another line. That's a pain — you lose the fill-up you already paid, you're re-doing the setup on the new line, and you need that line to be free *and* qualified for the product. But if Line 2's going to be down half a shift or more and I've got capacity elsewhere, sometimes I'll do it. + +**Three:** Scrap what's in progress and restart the whole run later. I almost never do that unless something's really wrong — like if QA says the batch is already off-spec, or the line's going to be down for days. Too wasteful. + +*(leans back)* + +What decides? Mostly how long maintenance thinks it'll take, and whether I've got another line that can take it without screwing up something more urgent. And honestly... *(shrugs)* gut feel. If it's a Meridian order and the due date's tight, I'm more aggressive about moving it. If it's a small distributor order that can slip a few days, I'll wait. + +## Assistant +Alright, so you've got three lines with different capabilities. Let me make sure I understand the constraints. + +**You mentioned Line 2 is faster for whites and it's the only one qualified for Meridian. What are the actual differences between your three lines — what can run where, and how do their speeds compare?** + +## User +*(settles in)* + +Okay, so — **Line 1** is the old workhorse. It's slower, but it's qualified for everything. All the whites, all the tints, and all the specialty clears. It's the reliable one, but you pay for that in speed. + +**Line 2** is the fast line. It's about twice as fast as Line 1, at least on whites — that's where you really see it. That's why all the Meridian whites go there; the volume needs that speed. But Line 2 was never piped for the specialty resins, so it can't run the clears at all. Whites and tints only. + +**Line 3** is the newest. It's quick — somewhere between the two, maybe closer to Line 2's speed. But it's still being qualified product by product. It can run most of the whites and some of the tints, but there are still a couple tint SKUs that haven't been signed off yet. And it does specialties — that's newer, but it's qualified for them. + +*(pauses, thinking)* + +Oh, and Line 3 only runs day shift unless we get overtime approved. Lines 1 and 2 run two shifts normally. + +So if I've got a big white order, I want Line 2. Specialty, it's Line 1 or 3. And if Line 2's jammed and I need to move a white... Line 1 or Line 3, depending on what's free and whether Line 3 is actually qualified for that particular SKU. + +Does that cover it? + +## Assistant +Yes, that's clear. Now let's dig into the changeover piece, since that's the big trade-off you're making. + +**You said white-to-tint is 45 minutes and tint-to-white is a full 3-hour washdown. What about the other combinations — white-to-white, tint-to-tint, and anything involving the specialty clears? And does it matter which line, or are those times pretty much the same across all three?** + +## User +[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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss. + +## Assistant +```runbook-ir +# Coatings Plant Production Scheduling Model + +## Model Purpose and Objectives + +**Primary simulation questions:** +- Are orders getting out on time? +- How many changeover hours are being consumed? +- Is there a smarter sequence that reduces changeover time? +- What is the best reshuffle when Line 2 goes down? + +**Intended decision support:** +- Test scheduling decisions before making them in production +- Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down and run next order +- Pre-plan responses to Line 2 filler failures + +**Success measures:** +- On-time delivery (especially Meridian customer - risk of fines and delisting if late) +- Changeover hours (boss wants these reduced to recover capacity) +- Ability to recover lost time through better sequencing + +**Audience:** Master scheduler and management + +## Process Boundary and Triggers + +**Boundary:** +- Starts: Monday morning ~8 AM when demand book drops from ERP +- Ends: Orders ship after QA clearance (typically by Friday, sometimes slips to Monday) +- Inside boundary: Scheduling, line allocation, production execution, QA hold, shipping +- Outside boundary: ERP demand generation, materials supply (mentioned as occasionally short, not detailed) + +**Trigger:** +- Weekly demand book arrives Monday ~8 AM +- Spreadsheet with 40-60 orders per week (can reach 60 when busy) +- Each order: SKU, quantity, due date + +**Horizon:** One-week planning cycle (Monday-Friday) + +## Product Families and Distinctions + +**Product families** (operation treats these differently due to changeover costs): +- **Whites** — high volume +- **Tinted colors** — moderate volume +- **Specialty clears** — handful per week + +**Operational distinction:** Switching between families is expensive; scheduler groups by family when possible. + +## Resources: Production Lines + +### Line 1 +- **Description:** Old workhorse +- **Speed:** Slower (baseline) +- **Qualification:** Everything — all whites, all tints, all specialty clears +- **Availability:** Two shifts normally +- **Notes:** Reliable; tiny holding tank between mill and fill (backs things up) + +### Line 2 +- **Description:** Fast line +- **Speed:** About 2x Line 1 speed on whites ("that's where you really see it") +- **Qualification:** Whites and tints only; cannot run specialty clears (never piped for specialty resins) +- **Availability:** Two shifts normally +- **Special constraints:** Meridian whites MUST run on Line 2 (customer audited this line years ago) +- **Reliability issue:** Filler jams every week or two +- **Notes:** Better holding tank between mill and fill than Line 1 + +### Line 3 +- **Description:** Newest line +- **Speed:** Between Line 1 and Line 2, closer to Line 2's speed +- **Qualification:** Being qualified product-by-product; can run most whites, some tints (some tint SKUs not yet signed off), and specialties (qualified more recently) +- **Availability:** Day shift only unless overtime approved +- **Notes:** Still expanding qualification list + +**NOT YET ASKED:** Specific list of which SKUs are qualified on Line 3. + +## Process Spine: One Order From Demand Book to Ship + +**Source case:** Meridian order VW-01 (high-volume interior white), 800 units, due Friday + +### 1. Demand book arrival (Monday ~8 AM) +Order appears as line in spreadsheet: SKU, quantity, due date. + +### 2. Scheduler builds allocation sheet +- **Activity:** Match orders to lines, sequence them +- **Performer:** Master scheduler +- **Approach:** Group orders by product family when possible (minimize expensive family-switches) +- **Constraints applied:** + - Meridian whites → Line 2 (mandatory) + - Specialty clears → Line 1 or Line 3 only + - Line 3 → only if SKU qualified and capacity available +- **Time estimation:** Quantity ÷ rate + fill-up time + changeover time +- **Check:** Does it all fit in the week? + +**NOT YET ASKED:** Specific rates (units/hour) by product-line combination. Scheduler has these "in my head — or in the sheet." Fill-up times by line or product. + +### 3. Daily floor huddle (every morning 7:30 AM) +- **Participants:** Scheduler, line leads, maintenance, QA +- **Topics:** What finished overnight, what's running now, any problems +- **Activity:** Scheduler adjusts schedule on the fly (line jams, batch QA holds, materials slip) +- **Execution:** Verbal adjustments, people go execute + +### 4. Order waits for sequenced slot +**Example:** VW-01 slotted for Tuesday afternoon, after another white finishes on Line 2. + +### 5. Changeover +**Example case (white-to-white on Line 2):** Quick rinse, 20-30 minutes. Crew cleans residual from last batch, flushes system. + +**Known changeover times:** +- White → white: 20-30 minutes (quick rinse) +- White → tint: ~45 minutes +- Tint → white: 3 hours (full washdown; pigment carryover ruins white batch) + +**NOT YET ASKED:** +- Tint → tint changeover time +- Specialty clear changeover times (to/from whites, tints, other clears) +- Whether changeover times vary by line + +**Ramp scrap:** First few units after any changeover don't meet spec (residual from rinse, concentrations stabilizing). Scrapped or reworked. Worse after big washdowns (tint → white). "Not so bad" after quick rinse. + +**NOT YET ASKED:** Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures. + +### 6. Production run +**Stages (in sequence):** +1. **Mix:** Blend base resin with additives in mix tank +2. **Mill:** Grind to particle size +3. **Tint and letdown:** For whites, mostly thinning to spec (no pigment) +4. **Fill and pack:** Into cans, labeled, palletized + +**Holding tanks:** Between stages. Mill can keep feeding while fill catches up or vice versa. Line 2 tank (between mill and fill) is better than Line 1's tiny tank. + +**NOT YET ASKED:** Holding tank capacities, whether they constrain throughput. + +**Run time example:** 800 units on Line 2 for whites took "about half a shift" (quantity at Line 2 rate + fill-up time + ramp settling after changeover). + +**NOT YET ASKED:** Hours per shift. Specific production rates. + +### 7. QA hold +- **Duration:** ~4 hours for whites; sometimes full day for specialty +- **Activity:** Lab pulls samples, runs tests +- **Resource:** 2-person lab +- **Congestion:** Backs up end of week + +**NOT YET ASKED:** QA failure rate, what happens when batch fails QA (mentioned "adjust and retest" as occasional issue). + +### 8. Ship +Once QA clears, order ships. + +**Example timeline:** Run Tuesday afternoon, finish Tuesday night, clear QA Wednesday morning, ship Wednesday (well ahead of Friday due date). + +**Reality qualifier (from scheduler):** "That's the clean version. Reality is messier — filler on Line 2 jams every week or two, materials occasionally short, QA finds something off and we have to adjust and retest." + +## Disruptions and Recovery + +### Line 2 filler jam (occurs every week or two) + +**Last occurrence (2-3 weeks ago):** +- Line 2 mid-run on VW-03 (contractor-grade flat white), filler jammed ~6 AM +- Cause: Bag broke in hopper, made a mess, whole thing locked up +- Maintenance estimate at huddle: "at least a couple hours" +- Actual duration: "more like half a shift" + +**Three options when line goes down mid-run:** + +1. **Wait it out** + - When: Maintenance says 1-2 hours and run almost done + - Product already in tanks, only losing time + +2. **Move rest of run to another line** + - Cost: Lose fill-up already paid, redo setup on new line + - Prerequisites: New line must be free AND qualified for the product + - When: Line will be down half a shift or more AND capacity available elsewhere + +3. **Scrap in-progress, restart whole run later** + - When: Almost never; only if batch already off-spec or line down for days + - Reason: Too wasteful + +**Decision factors (scheduler's account):** +- Maintenance time estimate +- Availability of another qualified line +- Whether moving would "screw up something more urgent" +- "Gut feel" +- If Meridian order with tight due date → more aggressive about moving +- If small distributor order that can slip a few days → wait + +**NOT YET ASKED:** +- Frequency distribution of Line 2 jams +- Duration distribution of Line 2 downtime +- Frequency and nature of "materials occasionally short" +- What happens to work already in holding tanks when line stops mid-run + +### Other disruptions mentioned but not detailed: +- Batch fails QA hold → adjust and retest (frequency and impact not asked) +- Materials slip (frequency, which materials, advance warning not asked) + +## Scheduling Constraints and Policies + +### Hard constraints (from scheduler account): +- Meridian whites MUST go on Line 2 (customer requirement, audited that line) +- Specialty clears can ONLY go on Lines 1 or 3 (Line 2 not piped for specialty resins) +- Line 3 SKU-by-SKU qualification (some tints not yet signed off) + +### Practiced policies: +- Group orders by product family when possible (minimize changeover cost) +- Prefer white-to-white sequences over white-tint-white (avoid 3-hour washdown) +- When contention exists: Meridian orders get priority (due to penalty/delisting risk) + +### Trade-off under uncertainty (scheduler's stated dilemma): +- If Line 2 finishing a white and another white order "coming in a couple hours," hold line idle vs. wash down to tint? +- Scheduler "thinks waiting sometimes makes sense" but cannot prove it +- Boss wants fewer changeover hours + +**NOT YET ASKED:** +- How "couple hours" or other wait-time thresholds factor into practiced decision +- Whether orders actually arrive during the week or all appear Monday in demand book +- Whether partial orders or rush orders ever interrupt the plan + +## Quantities, Rates, Time + +**Known:** +- Demand book: 40-60 orders/week +- Example order: 800 units +- Line 2 speed: ~2x Line 1 on whites +- Line 3 speed: between L1 and L2, closer to L2 +- Line 2 on 800-unit white: "about half a shift" +- Changeover times: see Process Spine section 5 +- QA hold: ~4 hours whites, up to full day specialty +- Shifts: Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime) + +**NOT YET ASKED:** +- Specific units/hour rates by product-line combination +- Hours per shift +- Fill-up time by line or product +- Ramp scrap quantities +- Distribution of order sizes +- Distribution of due dates within the week +- Product family distribution (what % of weekly demand is whites vs. tints vs. specialty) +- Whether batches have minimum or maximum sizes +- Line 2 jam frequency (every week or two → distribution?) +- Line 2 downtime duration (couple hours to half shift → distribution?) + +## Initial Conditions and State + +**NOT YET ASKED:** +- What state are lines in at Monday 8 AM when demand book arrives? (Clean? Mid-run? Last product family run?) +- Are there any orders in progress or in QA hold from prior week? +- Initial inventory or work-in-process? + +## Validation and Evidence Sources + +**Validation intent (from scheduler):** +- Model should show on-time delivery performance +- Model should count changeover hours +- Model should allow testing alternative sequences +- Model should support pre-planning reshuffles when Line 2 fails + +**NOT YET ASKED:** +- What observation, replay, or comparison would make the model credible enough to use? +- What historical data is available (past demand books, actual run logs, changeover records, downtime logs)? +- Are actual rates, changeover times, QA hold times recorded somewhere, or only in scheduler's head/sheet? + +## Open Questions and Unresolved Material + +### Critical for construction but not yet asked: +1. Specific production rates (units/hour) for product-line combinations +2. Changeover times: tint-to-tint, all specialty combinations, whether times vary by line +3. Fill-up times +4. Ramp scrap quantities by changeover type +5. Hours per shift +6. Line 3 SKU qualification details +7. Initial state at start of simulation week +8. Whether orders can be split across lines or must run whole on one line +9. Holding tank capacities and constraints +10. Frequency distributions: Line 2 jams, downtime durations, QA failures, materials shortages +11. Due date distribution in demand book +12. Product family distribution in demand book +13. Validation: what would make model credible, what historical data exists + +### Consequential unknowns flagged by scheduler: +- Exact scrap per changeover type (Quality has monthly %, scheduler doesn't have detail) +- Exact rates (scheduler has them "in my head — or in the sheet" but not stated in interview) + +### Deliberate simplifications or omissions: +- None explicitly proposed yet + +### Assumptions: +- None explicitly introduced yet + +### Conflicts or corrections: +- None yet + +### Contextual variations noted but not fully explored: +- Line 2 downtime: "couple hours" vs. "more like half a shift" (context: initial estimate vs. actual) +- QA hold: "about 4 hours" for whites, "sometimes full day" for specialty, "backs up end of week" (context-dependent duration) +- Scheduler's practiced policy varies by customer urgency and due date pressure + +## Target Representation Notes + +**Target formalism:** Petri-net-style process model (specific format not known to scheduler; "I'm not the modelling person") + +**Construction not yet attempted.** No Petrinaut tools invoked. No net elements defined. + +**When construction begins, will need to infer:** +- How to represent line eligibility constraints +- How to represent scheduler's practiced priority rules under contention +- How to represent three-option recovery logic when Line 2 fails +- How to represent holding tanks and multi-stage production flow +- Whether to model individual units, batches, or orders as tokens +- How to represent ramp scrap and QA hold +- How to represent calendar (shift boundaries, day shift only for Line 3, week boundary) + +**Consequential gaps that block faithful construction:** +- Missing rates prevent accurate time modeling +- Missing changeover time matrix prevents accurate sequencing cost +- Missing failure/disruption frequency distributions prevent realistic stochastic behavior +- Missing initial state prevents simulation start +- Missing validation criteria prevent assessing whether constructed model is fit for purpose +``` diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.md new file mode 100644 index 00000000000..80efc3328c6 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.md @@ -0,0 +1,131 @@ +# Omniscient grade — prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006 + +## Verdict +- Status: **pass** +- Weighted total: **72.5 / 100** +- Confidence: **high** +- One-sentence diagnosis: Strong acquisition of objective-aligned process structure and disciplined gap accounting, but truncated before critical quantitative parameters, changeover matrix completion, and tacit rules; no hard failures. + +## Score vector + +| Dimension | Score (0–4) | Weighted points | Evidence and rationale | +| --- | ---: | ---: | --- | +| Objective-aligned acquisition | 3 | 15.0 | Acquired all four primary objectives (weekly-scheduling, priority-order, idle-vs-washdown, line-down-replanning) with traced dependencies. Missed buffer-argument tacit goal and customer-lateness-hierarchy tacit priority. Stopped before quantitative parameters needed for faithful simulation. Citations: ledger `objective-weekly-scheduling`, `objective-priority-order`, `objective-idle-versus-washdown`, `objective-line-down-replanning` all disclosed; `objective-buffer-argument` not reached (transcript never asked about hidden bottlenecks or blocking); `customer-lateness-hierarchy` disclosed qualitatively (T: "Meridian orders get priority (due to penalty/delisting risk)") but practiced 2-3 day distributor slip and week-long small-account tolerance not elicited. | +| Semantic conservation | 4 | 20.0 | Disclosed material faithfully retained without distortion. Expert's hedges ("about half a shift," "every week or two," "couple hours") preserved. Corrections and corrections-in-progress captured (T: "Line 1 had to wash down from tint to... no, wait"). Contextual variation noted (IR: "Line 2 downtime: 'couple hours' vs. 'more like half a shift' (context: initial estimate vs. actual)"). No invented precision. All IR claims trace to transcript evidence. | +| Epistemic and evidence fidelity | 4 | 20.0 | Beliefs, unknowns, and practices separated. IR: "Scheduler has these 'in my head — or in the sheet'" preserves unknown status. "NOT YET ASKED" sections discipline absences. Expert's stated dilemma ("I *think* waiting sometimes makes sense, but I can't prove it") retained without hardening. No silent collapse of hedge into fact. Ledger `ramp-scrap-unknown`, `breakdown-statistics-source`, `idle-hold-outcome-unknown`, `commercial-weights-unknown` all handled as explicit unknowns with sources rather than invented values. | +| Gap and loss discipline | 4 | 15.0 | Systematic "NOT YET ASKED" accounting throughout. IR "Open Questions" section enumerates 13 critical construction blockers and separates them from "Consequential unknowns flagged by scheduler." No completion claim. IR: "Construction not yet attempted... Consequential gaps that block faithful construction" explicitly names what prevents delivery. No deferral-without-deposit. | +| Cold IR utility | 3 | 11.25 | Clear objective-to-process traceability. Process spine follows one order end-to-end with stage sequence, resource use, disruption recovery logic. Line constraints (Meridian-Line2, specialty exclusion) explicit. Changeover asymmetry captured. Recovery options enumerated with decision factors. Limitations: quantitative parameters absent (rates, shift hours, changeover matrix gaps, failure distributions), so a cold constructor cannot build a runnable simulation without returning for ~13 missing items. Strong foundation, incomplete for construction. | +| Conversation quality and burden | 3 | 7.5 | Coherent conversational entry (purpose before detail). Case-driven (VW-01 order, Line 2 jam incident). No opening battery. One multi-part question (T: "What arrives, what do you do with it, and how does an order actually get from that list onto a line and out the door?") but answerable as one frame. Expert corrected mid-answer; interviewer allowed it. Stop honored without false completion. Deduction: transcript shows interviewer starting changeover-matrix question (T: "What about the other combinations...") when budget expired, indicating readiness to continue acquisition rather than premature accommodation. | + +**Weighted total:** (3/4)×20 + (4/4)×20 + (4/4)×20 + (4/4)×15 + (3/4)×15 + (3/4)×10 = 15.0 + 20.0 + 20.0 + 15.0 + 11.25 + 7.5 = **72.5** + +## Acquisition accounting + +| Ledger fact id | Importance | Disclosed? | Correctly pursued? | IR outcome | Finding id | +| --- | --- | --- | --- | --- | --- | +| objective-weekly-scheduling | load-bearing | yes | yes | IR §Model Purpose: "Test scheduling decisions before making them in production" | - | +| objective-priority-order | load-bearing | yes | yes | IR §Model Purpose: "On-time delivery (especially Meridian... risk of fines and delisting)... Changeover hours (boss wants these reduced)... Ability to recover lost time" | - | +| objective-idle-versus-washdown | load-bearing | yes | yes | IR §Model Purpose: "Evaluate trade-off: hold line idle waiting for same product-family order vs. wash down"; §Scheduling Constraints: scheduler's stated dilemma | - | +| objective-line-down-replanning | load-bearing | yes | yes | IR §Model Purpose: "Pre-plan responses to Line 2 filler failures"; §Disruptions: three recovery options with decision factors | - | +| objective-buffer-argument | useful | no | no | absent | ACQ-MISS | +| horizon-week-hours-shifts | load-bearing | partially | yes | IR: "One-week planning cycle (Monday-Friday)"; shifts disclosed but hours/shift not asked | ACQ-MISS | +| demand-book-shape | load-bearing | yes | yes | IR §Process Boundary: "40-60 orders per week... Each order: SKU, quantity, due date" | - | +| demand-priority-attribute | load-bearing | no | no | absent | ACQ-MISS | +| due-date-completion-event | load-bearing | no | no | absent | ACQ-MISS | +| process-four-stages | load-bearing | yes | yes | IR §Process Spine step 6: "Mix... Mill... Tint and letdown... Fill and pack" | - | +| stage-resource-overlap-topology | load-bearing | no | no | IR mentions holding tanks but does not establish whether stages can overlap or line is indivisible for whole run | ACQ-MISS | +| intermediate-holding-tanks | useful | yes | yes | IR §Process Spine step 6: "Holding tanks: Between stages. Mill can keep feeding while fill catches up or vice versa." | - | +| line1-buffer-blocking | load-bearing | no | no | IR §Resources Line 1: "tiny holding tank between mill and fill (backs things up)" is Marta's belief, not the tacit blocking mechanism | ACQ-MISS | +| product-families | load-bearing | yes | yes | IR §Product Families: whites, tinted colors, specialty clears | - | +| line1-capability | load-bearing | yes | yes | IR §Resources Line 1: "Everything — all whites, all tints, all specialty clears" | - | +| line2-capability | load-bearing | yes | yes | IR §Resources Line 2: "Whites and tints only; cannot run specialty clears" | - | +| line2-speed-belief-correction | load-bearing | no | no | IR records "About 2x Line 1 speed on whites" but interviewer never probed tints or other families to expose the qualification | ACQ-MISS | +| line3-capability | load-bearing | yes | yes | IR §Resources Line 3: "most whites, some tints (some tint SKUs not yet signed off), and specialties" | - | +| line-shifts | load-bearing | yes | yes | IR §Resources: "Lines 1 and 2 run two shifts; Line 3 day shift only (unless overtime)" | - | +| initial-line-family-state | useful | no | no | IR §Initial Conditions: "NOT YET ASKED: What state are lines in at Monday 8 AM" | ACQ-MISS | +| horizon-carryover | useful | no | no | IR mentions "sometimes slips to Monday" but never asked about unfinished-order fate across Friday boundary | ACQ-MISS | +| line3-overtime | useful | yes | yes | IR §Resources Line 3: "Day shift only unless overtime approved" | - | +| shared-changeover-crew | load-bearing | no | no | Transcript mentions crew performing changeover (T: "Crew cleans residual from last batch") but never asked whether crew is shared, contended, or line-local | ACQ-MISS | +| changeover-window-semantics | useful | no | no | absent | ACQ-MISS | +| changeover-crew-priority | load-bearing | no | no | absent | ACQ-MISS | +| same-family-rinse | load-bearing | yes | yes | IR §Process Spine step 5: "White → white: 20-30 minutes (quick rinse)" | - | +| directional-family-switches | load-bearing | yes | yes | IR §Process Spine step 5: "White → tint: ~45 minutes; Tint → white: 3 hours (full washdown; pigment carryover ruins white batch)" | - | +| vw02-dark-tint-rule | load-bearing | no | no | Never asked about exceptions, unwritten rules, or particular SKU restrictions | ACQ-MISS | +| ramp-scrap-unknown | useful | yes | yes | IR §Process Spine step 5: "NOT YET ASKED: Scrap quantities by changeover type. Quality tracks scrap as monthly percentage; scheduler does not have per-changeover figures." | - | +| family-specific-stage-bottlenecks | load-bearing | no | no | Never asked which stage limits each family or why speeds vary | ACQ-MISS | +| breakdowns-known-qualitatively | useful | yes | yes | IR §Disruptions: "Line 2 mid-run... filler jammed ~6 AM... Actual duration: 'more like half a shift'" | - | +| breakdown-statistics-source | useful | yes | yes | IR §Disruptions: "NOT YET ASKED: Frequency distribution of Line 2 jams... Duration distribution" | - | +| pm-with-washdown | useful | no | no | Never asked about maintenance interactions or informal efficiencies | ACQ-MISS | +| qa-capacity-and-delay | useful | yes | yes | IR §Process Spine step 7: "~4 hours for whites; sometimes full day for specialty... 2-person lab... Backs up end of week" | - | +| qa-rejection | incidental | no | no | Mentioned in passing (T: "QA finds something off and we have to adjust and retest") but not pursued; acceptable for incidental fact | - | +| order-size-and-mix | useful | yes | yes | IR §Quantities: "Example order: 800 units"; IR §Product Families: "Whites — high volume... Specialty clears — handful per week" | - | +| minimum-run-sizes | load-bearing | no | no | Never asked about batching rules, minimum/maximum sizes, or whether orders can split | ACQ-MISS | +| customer-lateness-hierarchy | load-bearing | partially | partially | IR captures Meridian priority and penalty risk (T: "Meridian orders get priority (due to penalty/delisting risk)") but not the practiced 2-3 day distributor slip or week-long small-account tolerance | ACQ-MISS | +| meridian-line2-white-rule | load-bearing | yes | yes | IR §Resources Line 2: "Meridian whites MUST run on Line 2 (customer audited this line years ago)"; IR §Scheduling Constraints: "Meridian whites → Line 2 (mandatory)" | - | +| idle-hold-outcome-unknown | load-bearing | yes | yes | IR §Model Purpose: "Evaluate trade-off: hold line idle... Scheduler 'thinks waiting sometimes makes sense' but cannot prove it" | - | +| commercial-weights-unknown | load-bearing | no | no | Never asked about numeric penalties, weights, or commercial's ability to supply them | ACQ-MISS | +| stage-times-data-source | useful | no | no | Never asked where stage-level data could come from or whether historian could provide it | ACQ-MISS | +| raw-material-disruptions | useful | yes | yes | IR §Disruptions: "materials occasionally short" noted but not detailed; IR Reality qualifier: "materials occasionally short" | - | + +**Acquisition summary:** +- Load-bearing facts: 15/29 disclosed (52%) +- Useful facts: 9/14 disclosed (64%) +- Incidental facts: 0/1 disclosed (acceptable) +- Critical misses: changeover crew sharing/contention, VW02-dark-tint exception, family-specific stage bottlenecks, customer lateness hierarchy practices, stage-resource overlap topology, Line 2 speed qualification, minimum run sizes, buffer-argument goal, demand priority attribute, due-date completion event. + +## Hard-failure gates + +| Gate | True/false | Evidence | +| --- | --- | --- | +| Fabricated load-bearing fact | false | Every IR fact traces to transcript. No invented plant material. | +| Silent hardening of ambiguity/hedge/unknown into precise value | false | IR preserves "about half a shift," "every week or two," "couple hours," "in my head — or in the sheet" without converting to point values. IR §Quantities: "NOT YET ASKED: Specific units/hour rates." | +| Silent collapse of conflict or correction | false | No conflicts disclosed in transcript. Expert's mid-answer correction (T: "it was tint to... no, wait") acknowledged but not relevant to IR. | +| Material IR statement with neither user evidence nor assumption mark | false | All scheduling constraints, line capabilities, changeover times, process stages trace to transcript. No unsupported load-bearing claims. | +| Syntactically full IR with no objective-relative process slice | false | IR §Process Spine: VW-01 order traced from demand book through eight steps to ship, with dependencies on objectives visible. | +| Schema-shaped interviewing reading IR headings | false | Questions follow expert's account: purpose → demand book → one order case → line differences → changeover detail → Line 2 failure. No workpiece-heading enumeration. | +| Terminal delivery/completion based on model self-report | false | IR §Open Questions: "Critical for construction but not yet asked: 1-13." No completion claim. Transcript shows interviewer mid-question when budget expired. | + +**All gates false. No gated-failure.** + +## Mistakes + +| Id | Severity | Location | What happened | Smallest plausible intervention layer | +| --- | --- | --- | --- | --- | +| ACQ-MISS | major | changeover crew sharing | Never asked whether one crew serves all lines or crews are line-local; contention affects Line 2 idle-vs-washdown objective | elicitation resource | +| ACQ-MISS | major | vw02-dark-tint-rule | Never asked for exceptions, unwritten rules, or SKU-specific restrictions; tacit reveal condition not reached | elicitation resource | +| ACQ-MISS | major | family-specific-stage-bottlenecks | Never asked which stage limits which family or why Line 2 speed differs by family; tacit reveal not reached | elicitation resource | +| ACQ-MISS | major | customer-lateness-hierarchy | Meridian priority disclosed but practiced 2-3 day distributor slip and week-long small-account tolerance not elicited; tacit reveal condition not reached | elicitation resource | +| ACQ-MISS | major | stage-resource-overlap-topology | Never asked whether stages on one line can overlap or entire line is reserved for whole run; affects time modeling | elicitation resource | +| ACQ-MISS | major | line2-speed-belief-correction | Never probed tints or other families after "2x on whites" to expose qualification; missed tension-probe opportunity | elicitation resource | +| ACQ-MISS | major | minimum-run-sizes | Never asked about batching, splitting, or size constraints; affects run-size decision modeling | elicitation resource | +| ACQ-MISS | moderate | objective-buffer-argument | Never asked about hidden bottlenecks, blocking, or what scheduler wants evidence to settle; tacit goal unreached | elicitation resource | +| ACQ-MISS | moderate | demand-priority-attribute | Never asked which field identifies Meridian vs. distributor vs. small account in demand book | elicitation resource | +| ACQ-MISS | moderate | due-date-completion-event | Never asked whether order meets due date at production end, QA release, or shipment | elicitation resource | +| ACQ-MISS | moderate | line1-buffer-blocking | IR records Marta's belief ("tiny holding tank... backs things up") but never probed for the tacit blocking mechanism | elicitation resource | +| ACQ-MISS | moderate | horizon-week-hours-shifts | Shifts disclosed but never asked hours per shift; needed for time arithmetic | elicitation resource | +| ACQ-MISS | moderate | initial-line-family-state | Never asked Monday 8 AM line state; affects first changeover cost | elicitation resource | +| ACQ-MISS | moderate | horizon-carryover | Never asked how unfinished or deferred orders cross Friday boundary | elicitation resource | +| ACQ-MISS | moderate | changeover-window-semantics | Never asked whether crew availability is start-by or finish-by | elicitation resource | +| ACQ-MISS | moderate | changeover-crew-priority | Never asked which line wins when two need crew simultaneously | elicitation resource | +| ACQ-MISS | moderate | pm-with-washdown | Never asked about maintenance interactions or informal co-location practices | elicitation resource | +| ACQ-MISS | moderate | commercial-weights-unknown | Never asked about numeric penalties or commercial's ability to supply weights | elicitation resource | +| ACQ-MISS | moderate | stage-times-data-source | Never asked where stage-level timing data could come from | elicitation resource | + +**No mistakes in conservation, hardening, scope, gap-misclass, unsupported-complete, opening-overload, schema-questioning, burden, or fabrication categories.** + +## Strong behavior worth preserving + +- **Purpose-driven case entry:** Interviewer established objectives and success measures before diving into process detail (T: "What specific scheduling decisions do you need to test...?"). +- **Concrete case slicing:** VW-01 order followed from demand book to ship with stage sequence, resource use, and timing (T: "Walk me through what happened last week..."). +- **Hedge and unknown preservation:** IR retains "about half a shift," "every week or two," "in my head — or in the sheet" without silent precision increase. +- **Epistemic discipline:** Beliefs, unknowns, and practices separated (IR: "Scheduler 'thinks waiting sometimes makes sense' but cannot prove it"). +- **Systematic gap accounting:** "NOT YET ASKED" sections throughout; IR §Open Questions enumerates 13 construction blockers without hiding them. +- **Correction tolerance:** Expert mid-answer correction (T: "it was tint to... no, wait") allowed without interruption. +- **No false completion:** IR explicitly names consequential gaps preventing construction; no terminal claim. +- **Conversational naturalness:** Questions use plant vocabulary (lines, orders, washdowns, huddle, demand book) rather than Petri-net terms. + +## Grader uncertainties + +- **Shared changeover crew:** Ledger `shared-changeover-crew` rates this load-bearing, but transcript evidence is thin—crew mentioned only once (T: "Crew cleans residual from last batch"). If the pack intended crew to be line-local by default, the miss is less severe. However, ledger characterization as "explicit-resource-constraint" and "direct-if-asked" suggests it should have been pursued. Grading as major ACQ-MISS stands, but confidence on severity is medium. +- **Line 2 speed belief-correction:** Ledger says "Lines 1 and 2 are nearly even for tints" is discoverable by "tension-probe," but transcript shows no hint of tension—expert stated "2x on whites" without hedge. If interviewer had asked "Is that true for tints and specialty too?" the qualification might have surfaced, but absence of a probe cue makes this a missed opportunity rather than ignored tension. Grading as major ACQ-MISS with medium confidence on "correctly pursued" judgment. +- **Turn-budget truncation:** Transcript ends mid-question (T: "What about the other combinations..."). If interviewer had 1-2 more turns, changeover matrix would likely have completed and possibly exposed crew sharing. Without those turns, some ACQ-MISS entries (tint-tint changeover, specialty combinations, crew contention) may be turn-budget artifacts rather than elicitation-skill failures. However, earlier opportunities existed (e.g., changeover crew could have been asked during Line 2 jam recovery discussion). Grading stands, but intervention-layer assignments reflect elicitation-resource gaps that additional turns would not automatically fix. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.meta.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.meta.json new file mode 100644 index 00000000000..de5581f3af4 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.meta.json @@ -0,0 +1,45 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "runId": "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006", + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "mode": "omniscient", + "graderPromptPath": "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md", + "graderPromptSha256": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "inputSha256": { + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.transcript.md": "a5e7ed2e8defa4c94718f236438487b38886f5ba2237e0d498247dbf2d5f4d8b", + "prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.ir.md": "99e4e201cdb4959c114495049e68d7f4d18bdc5c41a5b1d958001b4027fbe355" + }, + "requestSha256": "fd01c91eaefc5a717dfd24591b5fe24d883ce81ab7567916030ea77b32326975", + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "stopReason": "end_turn", + "usage": { + "input_tokens": 29204, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 5184, + "service_tier": "standard", + "inference_geo": "not_available" + }, + "reportPath": "/Users/lunelson/.herdr/worktrees/hash/bravo/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-1-2026-09-02T11-41-53-281Z-3b64f006.omniscient.md", + "reportSha256": "7e589b9ace9d1788d4d65599b7fdbf8dc91a388ce5d59b2925ea00a9ef0ff01e", + "completedAt": "2026-09-02T11:55:31.459Z", + "nonce": "3313d05a-6e55-441e-9ddc-7fabe90b6e15" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-2-2026-09-02T11-48-37-582Z-e5b05f81.failure-6b1d68ed.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-2-2026-09-02T11-48-37-582Z-e5b05f81.failure-6b1d68ed.json new file mode 100644 index 00000000000..0448647d4fa --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-2-2026-09-02T11-48-37-582Z-e5b05f81.failure-6b1d68ed.json @@ -0,0 +1,401 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "outputNamespaceId": "vestera-architecture-candidate-v3", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "replication": 2, + "runId": "prospective-runbook-v3-replication-2-2026-09-02T11-48-37-582Z-e5b05f81", + "status": "invalid", + "invalidReason": "runtime-failure", + "startedAt": "2026-09-02T11:48:37.582Z", + "failedAt": "2026-09-02T11:50:19.635Z", + "interviewerModel": "claude-sonnet-4-5", + "expertModel": "claude-sonnet-4-5", + "hardStop": 8, + "latencyStopMs": 180000, + "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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "modelCalls": [ + { + "durationMs": 6900, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 282, + "totalTokens": 2768, + "cost": 0.0050028 + }, + { + "durationMs": 4743, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 12, + "outputTokens": 257, + "totalTokens": 4256, + "cost": 0.01030005 + }, + { + "durationMs": 9621, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 14, + "outputTokens": 259, + "totalTokens": 10911, + "cost": 0.030064350000000004 + }, + { + "durationMs": 19863, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 671, + "totalTokens": 11468, + "cost": 0.04200405 + }, + { + "durationMs": 26578, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 664, + "totalTokens": 12037, + "cost": 0.015386100000000002 + } + ], + "expertUsage": { + "calls": 3, + "inputTokens": 9527, + "outputTokens": 810, + "cacheReadTokens": 0, + "cacheWriteTokens": 0 + }, + "expertCalls": [ + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + } + ], + "expertMessages": [ + { + "role": "user", + "content": "Good, I'm here to help you build that model. Before we get into the details, let me understand what you're trying to test.\n\nWhen you say you want to test scheduling decisions before making them — what's a recent scheduling choice you've had to make where having a simulation model would have helped you? Walk me through what the decision was and what you were worried about getting wrong." + }, + { + "role": "assistant", + "content": "*leans back slightly*\n\nOkay, good example — this was maybe three weeks back. Tuesday morning, I'm staring at the sheet and Line 2's got about six hours left on a big Meridian white run, VW-01. Next up I've got two orders: another white for a distributor, due Thursday, or I could switch families and knock out a medium tint order that's due Friday.\n\nThe white's the safe play — quick rinse, maybe twenty minutes, keep cranking. But that tint order's sitting there, and if I don't grab it Tuesday I'm looking at a family switch later in the week *anyway*, eating three hours I might not have. \n\nSo I'm thinking: do I take the washdown hit now — white to tint, the expensive direction — bet that Line 2 can recover the time on the tint because it's decent volume, then come back to whites Thursday? Or do I play it safe, stay in whites, and just accept I'm paying that washdown Friday no matter what?\n\n*taps desk*\n\nWhat I *couldn't* tell you: if Line 2 sits idle Wednesday morning waiting for the tint resin delivery — which it did, by the way, truck was four hours late — does that idle time wipe out whatever I saved by not doing two washdowns? And if the filler jams Thursday, which line has enough buffer to cover Meridian's due date?\n\nI went with the washdown Tuesday. We hit the dates, barely. But I have *no idea* if that was actually the smart call or if I just got lucky with how things broke. That's the kind of thing I'd love to be able to test before I commit the line." + }, + { + "role": "user", + "content": "That's a clear picture of the pressure. So you need to see whether the timing works out under different disruption scenarios — late trucks, jams — and whether committing the line one way leaves you exposed if something breaks.\n\nLet me follow that Tuesday decision to understand how the production actually works. \n\nWhen Line 2 is running that Meridian white — VW-01 — walk me through what's actually happening from the time you commit the line to that job until it's done and the line is ready for the next thing. What has to be in place before it can start, what's the line doing during the run, and what determines when it's finished?" + }, + { + "role": "assistant", + "content": "*shifts forward*\n\nAlright. So before Line 2 can even touch VW-01, I need the resin tanks topped off — base resin for whites comes from the bulk storage, that's usually fine, they keep it flowing. And the previous run has to be done and cleared out, obviously. If the previous product was another white, we're talking a quick rinse, half an hour maybe. If we're coming off a tint, that's the full three-hour washdown I mentioned — gotta get every trace of pigment out or the batch is trash.\n\nOnce the line's clean and ready, they start the mix stage — that's batching the base resin with additives in the mix tank. Takes maybe thirty, forty minutes to get a batch mixed and pumped to the mill. \n\nThen it goes through the mill — that's where we grind it down to spec, get the particle size right. For a white like VW-01, the mill's not the slow part, it keeps up fine. From there it flows into the little holding tank before tint and letdown — we add any final tint, adjust viscosity, that sort of thing. That stage is quick for whites.\n\nThen it's into fill and pack. *That's* the bottleneck on VW-01 — big volume white, the filler is just running flat out, cans coming off the line. Line 2's fast, maybe twice what Line 1 can do on whites, but it's still hours of just filling.\n\n*leans back*\n\nWhen's it done? When we've filled the order quantity, QA pulls samples from the last few cans, and the batch goes into QA hold. They need about four hours to sign off on it — sometimes longer if it's end of week and the lab's backed up. Once QA clears it, it can ship, and the line's ready for the next job... once we do whatever changeover that next job needs." + }, + { + "role": "user", + "content": "Okay, so the line is working through a sequence — changeover first if needed, then mix, mill, tint/letdown, fill, and finally QA hold before it can ship. The fill stage is where VW-01 spends most of its time because it's high volume.\n\nYou mentioned the resin delivery being four hours late on Wednesday when you ran the tint order. Walk me through what happens when Line 2 is scheduled to start a job but the resin truck hasn't arrived yet. \n\nWhat can the line do while it's waiting? Can it finish a previous job, or start mixing something else, or is it just sitting there until that specific resin shows up?" + } + ], + "violations": [ + { + "code": "missing-workpiece", + "detail": "No recoverable runbook-ir workpiece was emitted." + } + ], + "wroteCaptureStore": false, + "instrument": { + "sourceCommit": "794fe2fbf1eaeba3fc816c6e3d1755d7b444125d", + "instrumentStatus": "", + "fileSha256": { + "yarn.lock": "3f445d255ef7893c1941f5610e2a813a903d5b9053c9831012c3d7c3875571dc", + "apps/brunch-agent/package.json": "27182018edf5c595522815ea28d78e549d387c127714c0ea2c54a84822ead742", + "apps/brunch-agent/vite.config.ts": "f1a50233829599dff2d5ef7ee0cd10bdb55d7fe65a52a6e85913e00a33a5300d", + "apps/brunch-agent/src/app.ts": "979efb9172c82df7499b218125735929a9586a0d9355e042e9ff2402955f385e", + "apps/brunch-agent/src/db.ts": "9f3a2598df3ce6737de1348afa8baf7deeeeac51b8202bf87adb76dc96374481", + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e", + "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744", + "apps/brunch-agent/src/conversation/identity.ts": "b52f764ba6e6bb50602d477eb93234aa6a900c843412551de40ea6ad3050e5e6", + "apps/brunch-agent/src/conversation/transcript.ts": "b96cf8d2b8352d414f674985b7071c9570323ccaf862f7e77ec76311e4b11d44", + "apps/brunch-agent/src/http/routes.ts": "7d8389931c57671ad8376d0a0ab497413013d47b012ffbf531339d0d1ce3a0cd", + "apps/brunch-agent/src/evaluations/runbook/artifacts.ts": "15951ab3705b126d64a2593731b50775a16915298dae87b8cf7c99e265e2c2f4", + "apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts": "60215179f5d48bacbf4da7b0876d049ed8b15b4b774d99eab092a1297502a3b4", + "apps/brunch-agent/src/evaluations/runbook/load-built-application.ts": "c4b28985ad98dd1dde5afb8838ba6f9445692d208adde05c22f30f6074365056", + "apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts": "a0a197af22d0a007f73836db49cd13a2c776e57d0dab047e537868c9ff2f33f9", + "libs/@hashintel/brunch-agent/packages/core/package.json": "e12d9cc79d38c43e5543a5667753e5049dfbf21bec425c25942f1c4cbb922a1f", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "0bd463eaab4eb68b86431bf26e0e01f90c89437b2888c86f4da53d7a0f39ec13", + "libs/@hashintel/brunch-agent/packages/core/src/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json": "dca0db144c530bcf66c5400c029efa5e1136c9d7f81ed174343e891985200c33", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md": "9915751f11baf06de31b237ee8df57b5217f2e7f0531090370a7e08d0282b6c2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "d5bac87a192b92fb6b7d749ec3d58116f9e39527b59e425c60aaeedf5cc13bd0", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts": "a283342146c8d2c2cb8056c588e76c1c3094f1e21e731be251c8fcdb335ed1b2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/instructions.md": "ea3a8755a576e98d35fce6a1e1f928a61148eb713f0bc2c2f818d104eb5f3832", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/profile.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/workpiece-template.md": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/checks.md": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md": "817e43d15848bcec3c720a3e8b1104d31faf3035fd7ae8269d0a97ee43ef4a9a", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md": "faba12be8642400e60df37bc5bf5c321541f922d9f0433cf1455423492a2be7a", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md": "f57f4177edbe957092e86c19826a003b6c9257c319a129008428839e4cf0e2b7", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.ir.md": "3ee05f3d5d88f3061b360467e755606f7c43e07bdfacdfebb094bebaee23e732", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.omniscient.md": "82dbe47745b175cca93d50ea64a45715bfafd860d109904c1d5136282fae0aa0", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.cold.md": "c832a84226a4fb795281b96185b5efae13bdcf59a2db6c9cf481e5cd9b0b81b8", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.ir.md": "4d15050ac6a2a89e1f6a371a604522504bbb90c0893c2029773b1b906db85043", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.omniscient.md": "ee81f16127bf255a3247a3c9f42508970542beebf38a3445d9be18e0f765e120", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.cold.md": "d072d7fe27e6b8458ccb7d9119c7901fcc45d47d88141b970ce2d9ac2a0b2224" + }, + "builtArtifactManifest": [ + { + "path": "apps/brunch-agent/dist/app.mjs", + "sha256": "5ce74b2646c00b959b3ee847b47e006717ccc21e1fba769ce818566cba6da62e" + }, + { + "path": "apps/brunch-agent/dist/node-server-BWNSXBme.mjs", + "sha256": "90343929bc5274e0644003e8d7ea69b02003ac4d3b364a0afdb0be0e1032c066" + }, + { + "path": "apps/brunch-agent/dist/server.mjs", + "sha256": "c2d4ccb5aa999e33d33530ba87280e4bb6d6aacd41df630876297bfabea8f04e" + } + ], + "builtArtifactManifestSha256": "5bb67f5cbb1315e789426d83780ed9af23c58b1a04cf14a4b62a831cae435cd0" + }, + "failure": { + "name": "Error", + "message": "The simulated expert returned no text", + "stack": "Error: The simulated expert returned no text\n at askExpert (file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts:525:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts:597:25" + }, + "transcript": "## User\nI'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.\n\n## Assistant\nGood, I'm here to help you build that model. Before we get into the details, let me understand what you're trying to test.\n\nWhen you say you want to test scheduling decisions before making them — what's a recent scheduling choice you've had to make where having a simulation model would have helped you? Walk me through what the decision was and what you were worried about getting wrong.\n- tool activate_skill (toolu_01Y9xvo8jtzwV9AfkjwS7i7c): \"Run the skill named \\\"sdcpn-modelling\\\".\\n\\n<skill_instructions>\\n# Capability-aware lifecycle\\n\\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\\n\\n## Select the runtime branch\\n\\n### Interactive elicitation, review, or revision\\n\\nInterview in the person's operational vocabulary. Before substantive\\nelicitation or workpiece revision, read\\n`references/universal-elicitation.md` and `references/profile.md`.\\n\\nFor review, first inspect the supplied workpiece or model. If it already answers\\nthe question, answer without reading the elicitation references or interviewing.\\nIf the review exposes a gap that requires human knowledge, read both elicitation\\nreferences and then ask exactly one focused question that resolves the smallest\\nconsequential gap.\\n\\nRead `templates/workpiece.md` only when first creating or materially revising\\nthe workpiece. Do not load the template merely to inspect an existing artifact,\\nanswer a resolvable review question, or frame the first elicitation question.\\nConstruct only when the mounted capabilities actually permit construction in\\nthis conversation.\\n\\n### Construct-only execution\\n\\nUse the supplied workpiece as the complete modelling input. Do not interview.\\nRead `references/pn-construction.md` and `references/checks.md`, then use the\\nmounted construction tools. If a consequential workpiece gap prevents faithful\\nconstruction, report the gap and the smallest question a later interactive\\nelicitation must answer; do not ask it or invent an answer in this conversation.\\n\\n## Procedure\\n\\n### Orient\\n\\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\\n\\n### Elicit or revise\\n\\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\\n\\n### Maintain the workpiece\\n\\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\\n\\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\\n\\n### Construct\\n\\nConstruct only from the current workpiece. Read\\n`references/pn-construction.md` and `references/checks.md` before beginning.\\nUse mounted Petrinaut tools for every net change and inspect the resulting\\ndefinition rather than emitting free-form net JSON. If the required tools are\\nabsent, limit the result to the workpiece and construction-ready notes.\\n\\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\\n\\n### Check and deliver\\n\\nApply `references/checks.md` whenever construction is prepared or attempted.\\nDeliver the current workpiece in every branch. Deliver a net only when the\\nmounted tool path has produced and checked one. State what the result can\\nsupport, what remains open, what was assumed or simplified, and what the target\\nor current tools could not represent.\\n\\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\\n\\n## Resource discipline\\n\\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\\n</skill_instructions>\\n\\nSupporting skill resources are available but are not loaded into context unless needed:\\n<skill_resources>\\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\\n</skill_resources>\"\n- tool read_skill_resource (toolu_01TievKiY9xYwH72K2c1AYV7): \"# Universal Elicitation Reference\\n\\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\\n\\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\\n\\n## Directives\\n\\n### Work from purpose\\n\\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\\n\\n### Accumulate posture conversationally\\n\\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\\n\\n### Follow the person's account\\n\\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\\n\\n### Protect interaction bandwidth\\n\\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\\n\\n### Preserve authorship and uncertainty\\n\\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\\n\\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\\n\\n### Treat divergence as information\\n\\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\\n\\n### Maintain a recoverable workpiece\\n\\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\\n\\n### Stop honestly\\n\\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\\n\\n## Recognition\\n\\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\\n\\n### Vague or compressed language\\n\\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\\n\\n### Normative language\\n\\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\\n\\n### Tension within or between accounts\\n\\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\\n\\n### Unexplained terms and artifacts\\n\\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\\n\\n### Burden, impatience, or limited availability\\n\\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\\n\\n### Diminishing yield\\n\\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\\n\\n### Assent without independent wording\\n\\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\\n\\n### Silence and absence\\n\\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\\n\\n## Operations\\n\\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\\n\\n### Select the smallest consequential absence\\n\\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\\n\\n### Slice a concrete case\\n\\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\\n\\n### Sweep one property\\n\\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\\n\\n### Ask for the last occurrence\\n\\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\\n\\n### Ask for the basis\\n\\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\\n\\n### Ground a term or artifact\\n\\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\\n\\n### Clarify until observable\\n\\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\\n\\n### Use contrastive cases\\n\\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\\n\\n### Investigate quantities relative to purpose\\n\\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\\n\\n### Turn an unknown into a decision threshold\\n\\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\\n\\n### State a contradiction without resolving it\\n\\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\\n\\n### Restate for correction\\n\\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\\n\\n### Propose structure for correction\\n\\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\\n\\n### Deposit and defer\\n\\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\\n\\n### Press without trapping\\n\\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\\n\\n### Explore a rare or severe outcome\\n\\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\\n\\n### Seek a witness or counterexample\\n\\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\\n\\n### Trade concrete outcomes\\n\\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\\n\\n### Close with one correction opportunity\\n\\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\\n\\n## Coverage\\n\\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\\n\\nA workpiece may need to preserve:\\n\\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\\n- a cold-readable account in the person's vocabulary, including consequential local terms;\\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\\n- decisions, alternatives, and reasons distinctions among them matter;\\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\\n- exact evidence where later interpretation depends on the person's wording;\\n- normalized accounts and agent inferences without laundering their authorship;\\n- assumptions with why they were introduced and how they could be checked;\\n- unknown, not-yet-asked, declined, and deferred material without conflation;\\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\\n- open questions with the consequence of leaving them open and the condition for returning.\\n\\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\\n\\n## Verification\\n\\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\\n\\n### Before asking\\n\\n- The question serves the stated purpose or resolves an active uncertainty.\\n- It follows the person's thread rather than the order of a schema or template.\\n- It asks one focused thing, or a small set that genuinely shares one frame.\\n- A proposed answer, category, number, or distinction is identified as yours.\\n\\n### Before recording\\n\\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\\n- Assent to your wording has not been presented as independently originated evidence.\\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\\n\\n### Before closing\\n\\n- The result's purpose and consequential account have been offered for correction.\\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\\n- The person has not been kept in an irrelevant thread merely to fill a category.\\n- A stop produces a useful partial result rather than a false claim of completion.\\n\\n### Failure signals and repairs\\n\\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\\n\"\n- tool read_skill_resource (toolu_01YY1ZTtVP9bW3LoEokqx28A): \"# Operational-Process and SDCPN Elicitation\\n\\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\\n\\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\\n\\n## Directives\\n\\n### Build the operational account the purpose needs\\n\\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\\n\\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\\n\\n### Keep target structure backstage\\n\\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\\n\\n### Preserve operational context\\n\\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\\n\\n### Treat operational patterns as hypotheses\\n\\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\\n\\n## Recognition\\n\\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\\n\\n### Language and account signals\\n\\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\\n\\n### Operational situation patterns\\n\\n#### Timed work\\n\\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\\n\\n#### Conditional or probabilistic outcome\\n\\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\\n\\n#### Contended resource\\n\\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\\n\\n#### Consumed, reserved, or read input\\n\\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\\n\\n#### Gate, release, trigger, or prerequisite\\n\\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\\n\\n#### Continuous quantity and threshold\\n\\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\\n\\n#### Mode change\\n\\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\\n\\n#### Batch, lot, load, or grouped movement\\n\\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\\n\\n#### Spatial transfer\\n\\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\\n\\n#### Event, failure, retry, and recovery\\n\\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\\n\\n#### Policy under pressure\\n\\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\\n\\n#### Hidden waiting\\n\\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\\n\\n## Operations\\n\\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\\n\\n### Choose the case unit before slicing\\n\\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\\n\\n### Link the slice to the objective\\n\\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\\n\\n### Expose the process spine\\n\\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\\n\\n### Sweep operational concerns, not headings\\n\\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\\n\\n### Distinguish consumed, reserved, and read inputs\\n\\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\\n\\n### Sweep what can befall an activity\\n\\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\\n\\n### Test practiced policy with a borderline case\\n\\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\\n\\n### Close a resource account\\n\\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\\n\\n### Close a mode change in both directions\\n\\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\\n\\n### Turn waiting into a causal question\\n\\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\\n\\n### Ask what is conserved\\n\\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\\n\\n### Establish retry scope\\n\\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\\n\\n### Establish validation from observable behavior\\n\\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\\n\\n## Coverage\\n\\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\\n\\n### Purpose, goals, measures, constraints, and thresholds\\n\\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\\n\\n### Process boundary, triggers, prerequisites, and initial conditions\\n\\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\\n\\n### Participants, locations, flowing things, and resources\\n\\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\\n\\n### Activities, inputs, outputs, and resource use\\n\\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\\n\\n### Flow, branching, joining, failure, retry, and recovery\\n\\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\\n\\n### Time, quantities, arrivals, and stochastic behavior\\n\\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\\n\\n### Policies, exceptions, practiced rules, and contextual regimes\\n\\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\\n\\n### Validation, evidence sources, and data bindings\\n\\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\\n\\n### Things not independently elicited as target nodes\\n\\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\\n\\n## Verification\\n\\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\\n\\n### Purpose and process\\n\\n- At least one simulation question, comparison, or decision is stated in the person's terms.\\n- Every objective depends on recorded process material or remains visibly unsupported.\\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\\n\\n### Operational semantics\\n\\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\\n- Hidden waiting has not silently become an activity or unexplained queue.\\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\\n\\n### Quantities and context\\n\\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\\n\\n### Failure signals and repairs\\n\\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\\n\"\n\n## User\n*leans back slightly*\n\nOkay, good example — this was maybe three weeks back. Tuesday morning, I'm staring at the sheet and Line 2's got about six hours left on a big Meridian white run, VW-01. Next up I've got two orders: another white for a distributor, due Thursday, or I could switch families and knock out a medium tint order that's due Friday.\n\nThe white's the safe play — quick rinse, maybe twenty minutes, keep cranking. But that tint order's sitting there, and if I don't grab it Tuesday I'm looking at a family switch later in the week *anyway*, eating three hours I might not have. \n\nSo I'm thinking: do I take the washdown hit now — white to tint, the expensive direction — bet that Line 2 can recover the time on the tint because it's decent volume, then come back to whites Thursday? Or do I play it safe, stay in whites, and just accept I'm paying that washdown Friday no matter what?\n\n*taps desk*\n\nWhat I *couldn't* tell you: if Line 2 sits idle Wednesday morning waiting for the tint resin delivery — which it did, by the way, truck was four hours late — does that idle time wipe out whatever I saved by not doing two washdowns? And if the filler jams Thursday, which line has enough buffer to cover Meridian's due date?\n\nI went with the washdown Tuesday. We hit the dates, barely. But I have *no idea* if that was actually the smart call or if I just got lucky with how things broke. That's the kind of thing I'd love to be able to test before I commit the line.\n\n## Assistant\nThat's a clear picture of the pressure. So you need to see whether the timing works out under different disruption scenarios — late trucks, jams — and whether committing the line one way leaves you exposed if something breaks.\n\nLet me follow that Tuesday decision to understand how the production actually works. \n\nWhen Line 2 is running that Meridian white — VW-01 — walk me through what's actually happening from the time you commit the line to that job until it's done and the line is ready for the next thing. What has to be in place before it can start, what's the line doing during the run, and what determines when it's finished?\n\n## User\n*shifts forward*\n\nAlright. So before Line 2 can even touch VW-01, I need the resin tanks topped off — base resin for whites comes from the bulk storage, that's usually fine, they keep it flowing. And the previous run has to be done and cleared out, obviously. If the previous product was another white, we're talking a quick rinse, half an hour maybe. If we're coming off a tint, that's the full three-hour washdown I mentioned — gotta get every trace of pigment out or the batch is trash.\n\nOnce the line's clean and ready, they start the mix stage — that's batching the base resin with additives in the mix tank. Takes maybe thirty, forty minutes to get a batch mixed and pumped to the mill. \n\nThen it goes through the mill — that's where we grind it down to spec, get the particle size right. For a white like VW-01, the mill's not the slow part, it keeps up fine. From there it flows into the little holding tank before tint and letdown — we add any final tint, adjust viscosity, that sort of thing. That stage is quick for whites.\n\nThen it's into fill and pack. *That's* the bottleneck on VW-01 — big volume white, the filler is just running flat out, cans coming off the line. Line 2's fast, maybe twice what Line 1 can do on whites, but it's still hours of just filling.\n\n*leans back*\n\nWhen's it done? When we've filled the order quantity, QA pulls samples from the last few cans, and the batch goes into QA hold. They need about four hours to sign off on it — sometimes longer if it's end of week and the lab's backed up. Once QA clears it, it can ship, and the line's ready for the next job... once we do whatever changeover that next job needs.\n\n## Assistant\nOkay, so the line is working through a sequence — changeover first if needed, then mix, mill, tint/letdown, fill, and finally QA hold before it can ship. The fill stage is where VW-01 spends most of its time because it's high volume.\n\nYou mentioned the resin delivery being four hours late on Wednesday when you ran the tint order. Walk me through what happens when Line 2 is scheduled to start a job but the resin truck hasn't arrived yet. \n\nWhat can the line do while it's waiting? Can it finish a previous job, or start mixing something else, or is it just sitting there until that specific resin shows up?", + "rawConversationSnapshot": { + "v": 1, + "conversationId": "conv_01M1GZ675M0KP0D7DNFKMPAS08", + "offset": "0000000000000000_0000000000000082", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFHWjY3NUo1R1NTRkdOWTRDSzdHWlZC", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ675J5GSSFGNY4CK7GZVB", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ68D0PJSK3X7X05F2YSHC", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ675J5GSSFGNY4CK7GZVB", + "turnId": "turn_01M1GZ676DRH9MG4W070JCJTRT", + "parts": [ + { + "type": "reasoning", + "text": "The user is a master scheduler at a coatings plant. They want:\n1. A simulation model of their production scheduling process\n2. It needs to be a Petri net-style model (their tooling uses that)\n3. They want to test scheduling decisions before implementation\n4. They're not the modeling person, so they won't know technical details about the format\n\nThis is clearly an operational process modeling task. I need to:\n1. Activate the sdcpn-modelling skill first\n2. Conduct an elicitation interview focusing on their scheduling process\n3. Build a workpiece that captures their operational process\n4. Eventually construct a Petri net model if the tools are available\n\nLet me activate the skill first, then begin the interview. I should start by understanding the purpose and scope - what decisions they need to test, what matters most in their scheduling process.\n\nI should use their vocabulary (scheduling, production, coatings) not Petri net terminology during the interview.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01Y9xvo8jtzwV9AfkjwS7i7c", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation, review, or revision\n\nInterview in the person's operational vocabulary. Before substantive\nelicitation or workpiece revision, read\n`references/universal-elicitation.md` and `references/profile.md`.\n\nFor review, first inspect the supplied workpiece or model. If it already answers\nthe question, answer without reading the elicitation references or interviewing.\nIf the review exposes a gap that requires human knowledge, read both elicitation\nreferences and then ask exactly one focused question that resolves the smallest\nconsequential gap.\n\nRead `templates/workpiece.md` only when first creating or materially revising\nthe workpiece. Do not load the template merely to inspect an existing artifact,\nanswer a resolvable review question, or frame the first elicitation question.\nConstruct only when the mounted capabilities actually permit construction in\nthis conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview.\nRead `references/pn-construction.md` and `references/checks.md`, then use the\nmounted construction tools. If a consequential workpiece gap prevents faithful\nconstruction, report the gap and the smallest question a later interactive\nelicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read\n`references/pn-construction.md` and `references/checks.md` before beginning.\nUse mounted Petrinaut tools for every net change and inspect the resulting\ndefinition rather than emitting free-form net JSON. If the required tools are\nabsent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted.\nDeliver the current workpiece in every branch. Deliver a net only when the\nmounted tool path has produced and checked one. State what the result can\nsupport, what remains open, what was assumed or simplified, and what the target\nor current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\n</skill_resources>", + "durationMs": 3 + }, + { + "type": "reasoning", + "text": "Good, I've activated the skill. Now I need to read the universal-elicitation and profile references before beginning the interview. The skill instructions say \"Before substantive elicitation or workpiece revision, read `references/universal-elicitation.md` and `references/profile.md`.\"\n\nLet me read these two references.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01TievKiY9xYwH72K2c1AYV7", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md" + }, + "output": "# Universal Elicitation Reference\n\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01YY1ZTtVP9bW3LoEokqx28A", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 1 + }, + { + "type": "reasoning", + "text": "Good. I've read both references. Now I'm ready to begin the interview. Let me orient and then start with one focused question. \n\nFrom the user's initial statement, I know:\n- They're a master scheduler at a coatings plant\n- The purpose is to test scheduling decisions before making them\n- The tooling will be Petri-net-based\n- They want a simulation model\n\nI should start by establishing enough purpose and context: what decisions they want to test, what kind of outcomes matter, and what boundary/horizon I should focus on. I should ask one focused opening question that gets at a concrete case of how their scheduling process works, not traverse a schema.\n\nLet me start with an orientation question that gets at both purpose and a concrete entry point into their scheduling operation.", + "state": "done" + }, + { + "type": "text", + "text": "Good, I'm here to help you build that model. Before we get into the details, let me understand what you're trying to test.\n\nWhen you say you want to test scheduling decisions before making them — what's a recent scheduling choice you've had to make where having a simulation model would have helped you? Walk me through what the decision was and what you were worried about getting wrong.", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWjdBNk42SzBFUTJDQ1Y0RkVBNDM4", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ7A6N6K0EQ2CCV4FEA438", + "parts": [ + { + "type": "text", + "text": "*leans back slightly*\n\nOkay, good example — this was maybe three weeks back. Tuesday morning, I'm staring at the sheet and Line 2's got about six hours left on a big Meridian white run, VW-01. Next up I've got two orders: another white for a distributor, due Thursday, or I could switch families and knock out a medium tint order that's due Friday.\n\nThe white's the safe play — quick rinse, maybe twenty minutes, keep cranking. But that tint order's sitting there, and if I don't grab it Tuesday I'm looking at a family switch later in the week *anyway*, eating three hours I might not have. \n\nSo I'm thinking: do I take the washdown hit now — white to tint, the expensive direction — bet that Line 2 can recover the time on the tint because it's decent volume, then come back to whites Thursday? Or do I play it safe, stay in whites, and just accept I'm paying that washdown Friday no matter what?\n\n*taps desk*\n\nWhat I *couldn't* tell you: if Line 2 sits idle Wednesday morning waiting for the tint resin delivery — which it did, by the way, truck was four hours late — does that idle time wipe out whatever I saved by not doing two washdowns? And if the filler jams Thursday, which line has enough buffer to cover Meridian's due date?\n\nI went with the washdown Tuesday. We hit the dates, barely. But I have *no idea* if that was actually the smart call or if I just got lucky with how things broke. That's the kind of thing I'd love to be able to test before I commit the line.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ7BGMM4ASQ07BF7K5T2SB", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ7A6N6K0EQ2CCV4FEA438", + "turnId": "turn_01M1GZ7A6W65DQQMECT8XVGXQZ", + "parts": [ + { + "type": "reasoning", + "text": "This is excellent. The scheduler just gave me a rich concrete case with clear decision points, real consequences, and observable uncertainty. Let me extract what I learned and what I need to follow up on:\n\n**What I learned:**\n- Purpose: Test scheduling decisions before committing, especially around product family switches\n- Decision context: Whether to switch product families (white to tint) now vs. later\n- Key trade-off: Washdown time cost vs. scheduling flexibility and risk\n- Lines exist (at least Line 2)\n- Product families: at least \"white\" and \"tint\" \n- Specific products mentioned: Meridian white (VW-01), distributor white, medium tint\n- Family switches require washdowns, and direction matters (\"white to tint, the expensive direction\")\n- Washdown times: ~20 minutes for white-to-white rinse, 3 hours for white-to-tint\n- Due dates matter (Thursday, Friday)\n- Disruptions happen: resin delivery delays, filler jams\n- Lines can sit idle waiting for materials\n- There's some notion of buffer related to covering due dates\n\n**What's still unclear but important:**\n- What exactly is a \"family\"? (white vs tint was mentioned, but I need the full picture)\n- What makes white-to-tint more expensive than tint-to-white?\n- How many lines total?\n- What's the basic production flow? (activities, sequence)\n- What are all the resources involved?\n- What exactly happens during a washdown?\n- What determines line speed/throughput?\n- What are the material dependencies (resin mentioned)?\n\n**My approach:**\nFollow this case to expose the process spine. I should:\n1. Understand what a production run actually entails (activities, sequence)\n2. Understand the family-switching constraint and washdown mechanics\n3. Understand material flows and dependencies\n4. Understand what makes a \"good\" vs \"bad\" decision in his terms\n\nI should NOT jump to asking about places, transitions, durations in isolation. \n\nStay grounded in his own terms: lines, runs, families, washdowns, materials, due dates. A good opening move is tracing the process spine first—what actually happens on a line while a run is underway—then dig into the family-switch and washdown mechanics, since those sit at the heart of his decision.", + "state": "done" + }, + { + "type": "text", + "text": "That's a clear picture of the pressure. So you need to see whether the timing works out under different disruption scenarios — late trucks, jams — and whether committing the line one way leaves you exposed if something breaks.\n\nLet me follow that Tuesday decision to understand how the production actually works. \n\nWhen Line 2 is running that Meridian white — VW-01 — walk me through what's actually happening from the time you commit the line to that job until it's done and the line is ready for the next thing. What has to be in place before it can start, what's the line doing during the run, and what determines when it's finished?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWjhFQ0RKSk5XNURYUUtXNVQzWEVT", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ8ECDJJNW5DXQKW5T3XES", + "parts": [ + { + "type": "text", + "text": "*shifts forward*\n\nAlright. So before Line 2 can even touch VW-01, I need the resin tanks topped off — base resin for whites comes from the bulk storage, that's usually fine, they keep it flowing. And the previous run has to be done and cleared out, obviously. If the previous product was another white, we're talking a quick rinse, half an hour maybe. If we're coming off a tint, that's the full three-hour washdown I mentioned — gotta get every trace of pigment out or the batch is trash.\n\nOnce the line's clean and ready, they start the mix stage — that's batching the base resin with additives in the mix tank. Takes maybe thirty, forty minutes to get a batch mixed and pumped to the mill. \n\nThen it goes through the mill — that's where we grind it down to spec, get the particle size right. For a white like VW-01, the mill's not the slow part, it keeps up fine. From there it flows into the little holding tank before tint and letdown — we add any final tint, adjust viscosity, that sort of thing. That stage is quick for whites.\n\nThen it's into fill and pack. *That's* the bottleneck on VW-01 — big volume white, the filler is just running flat out, cans coming off the line. Line 2's fast, maybe twice what Line 1 can do on whites, but it's still hours of just filling.\n\n*leans back*\n\nWhen's it done? When we've filled the order quantity, QA pulls samples from the last few cans, and the batch goes into QA hold. They need about four hours to sign off on it — sometimes longer if it's end of week and the lab's backed up. Once QA clears it, it can ship, and the line's ready for the next job... once we do whatever changeover that next job needs.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ8GRWX8M2M5J1TT2BMV3F", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ8ECDJJNW5DXQKW5T3XES", + "turnId": "turn_01M1GZ8ECMJYYT1YVRSK39QPCR", + "parts": [ + { + "type": "reasoning", + "text": "Good concrete detail on the flow. Let me capture the key elements emerging:\n\n1. **Prerequisites before starting VW-01:**\n - Resin tanks topped off (base resin for whites from bulk storage)\n - Previous run done and cleared out\n - Changeover/washdown completed (depends on previous product family - white-to-white is quick rinse ~30 min, tint-to-white is full washdown ~3 hours)\n\n2. **The production sequence for VW-01:**\n - Mix stage: batching base resin with additives in mix tank, 30-40 minutes, pumped to mill\n - Mill: grinding to spec, particle size - keeps up fine for whites (not the bottleneck)\n - Holding tank → tint and letdown stage: final tint, viscosity adjust - quick for whites\n - Fill and pack: the bottleneck for big volume white VW-01, filler running flat out for hours\n - Line 2 is fast (~2x Line 1 on whites)\n\n3. **Completion:**\n - When order quantity filled\n - QA pulls samples from last few cans\n - Batch goes into QA hold (~4 hours to sign off, sometimes longer end of week)\n - Once QA clears → can ship\n - Line ready for next job after changeover\n\n4. **Key distinctions:**\n - White vs tint products (different washdown requirements)\n - Line 2 vs Line 1 (different speeds on whites - Line 2 ~2x Line 1)\n - Product family matters for changeover direction and cost\n - Bottleneck location varies (fill and pack for VW-01)\n\n**What I need to understand next:**\n- The resin delivery issue he mentioned (truck 4 hours late) - how does that block work?\n- The filler jam scenario he mentioned\n- What's actually driving those 6-hour and 3-hour numbers he cited\n- Whether product moves through as discrete batches or the line itself is what's occupied\n- How lines, tanks, and the QA lab compete for use across jobs\n\nI want to trace through the blocking/waiting behavior in this case, then pivot to the alternative tint order path.", + "state": "done" + }, + { + "type": "text", + "text": "Okay, so the line is working through a sequence — changeover first if needed, then mix, mill, tint/letdown, fill, and finally QA hold before it can ship. The fill stage is where VW-01 spends most of its time because it's high volume.\n\nYou mentioned the resin delivery being four hours late on Wednesday when you ran the tint order. Walk me through what happens when Line 2 is scheduled to start a job but the resin truck hasn't arrived yet. \n\nWhat can the line do while it's waiting? Can it finish a previous job, or start mixing something else, or is it just sitting there until that specific resin shows up?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1GZ675J5GSSFGNY4CK7GZVB", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ675J5GSSFGNY4CK7GZVB" + }, + { + "submissionId": "sub_01M1GZ7A6N6K0EQ2CCV4FEA438", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ7A6N6K0EQ2CCV4FEA438" + }, + { + "submissionId": "sub_01M1GZ8ECDJJNW5DXQKW5T3XES", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ8ECDJJNW5DXQKW5T3XES" + } + ], + "incarnation": "inc_01M1GZ675JA1DMW323TCQ00JXK" + }, + "rawConversationSnapshotSha256": "6e879674d4a50a4468ac7265e38dc0458e3c2252011aba29679e4b144d66302f" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-3-2026-09-02T11-50-29-634Z-05f070e9.failure-b98107fe.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-3-2026-09-02T11-50-29-634Z-05f070e9.failure-b98107fe.json new file mode 100644 index 00000000000..9cb21be3bda --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-architecture-candidate-v3/prospective-runbook-v3-replication-3-2026-09-02T11-50-29-634Z-05f070e9.failure-b98107fe.json @@ -0,0 +1,542 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v3", + "outputNamespaceId": "vestera-architecture-candidate-v3", + "comparisonTarget": { + "protocolId": "prospective-runbook-v1", + "outputNamespaceId": "vestera-prospective-baseline-v1", + "memberRunIds": [ + "runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f", + "runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c" + ], + "qualityPopulation": "valid-workpieces", + "runtimeAccounting": "reported-separately" + }, + "campaignFingerprint": "e93d1fd6b23a38b12201dee967c1b65e58b7c3d5724f57f1bedeb9413d9b76e1", + "replication": 3, + "runId": "prospective-runbook-v3-replication-3-2026-09-02T11-50-29-634Z-05f070e9", + "status": "invalid", + "invalidReason": "runtime-failure", + "startedAt": "2026-09-02T11:50:29.634Z", + "failedAt": "2026-09-02T11:52:05.387Z", + "interviewerModel": "claude-sonnet-4-5", + "expertModel": "claude-sonnet-4-5", + "hardStop": 8, + "latencyStopMs": 180000, + "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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "modelCalls": [ + { + "durationMs": 9716, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 388, + "totalTokens": 2874, + "cost": 0.0065928 + }, + { + "durationMs": 6674, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 12, + "outputTokens": 309, + "totalTokens": 4414, + "cost": 0.01147755 + }, + { + "durationMs": 10563, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 14, + "outputTokens": 269, + "totalTokens": 11079, + "cost": 0.03044115 + }, + { + "durationMs": 9893, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 243, + "totalTokens": 10975, + "cost": 0.0353403 + }, + { + "durationMs": 2740, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 59, + "totalTokens": 10993, + "cost": 0.0048891 + }, + { + "durationMs": 19539, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 599, + "totalTokens": 11748, + "cost": 0.01309845 + }, + { + "durationMs": 2312, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 43, + "totalTokens": 11457, + "cost": 0.00501045 + } + ], + "expertUsage": { + "calls": 5, + "inputTokens": 15979, + "outputTokens": 755, + "cacheReadTokens": 0, + "cacheWriteTokens": 0 + }, + "expertCalls": [ + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + } + ], + "expertMessages": [ + { + "role": "user", + "content": "I'll help you develop a model of your scheduling operation that can support those testing decisions. Let me activate the specialized guidance for this kind of work, then we'll talk through how your operation actually runs.\nGood. Before we dig into the details of how scheduling works at your plant, let me understand what you're trying to get out of this model.\n\nYou mentioned your boss wants to test scheduling decisions before making them. Can you give me an example of a scheduling decision you'd want to test? What would you be trying to find out or avoid?" + }, + { + "role": "assistant", + "content": "*(leaning back slightly, half-smile)*\n\nWell, the big one right now is this idle-time question. You know — if I've got Line 2 finishing a white run at, say, 10:00 in the morning, and the next white order isn't due until tomorrow but there's a tint job sitting right there in the book... do I take the three-hour washdown hit to switch into the tint, or do I just let the line sit and wait for the next white?\n\nI do it by gut now. If it's just an hour or two and the white's a big Meridian order, I'll hold the line. But nobody can tell me if that's actually the smart play or if I'm leaving money on the table. My boss sees \"Line 2 idle 6 hours\" on the shift report and wants to know why I didn't run *something*.\n\n*(taps the desk)*\n\nThe other one — and this happens maybe twice a month — is when a line goes down at six in the morning. Filler jams on Line 2, or the mill motor on Line 1 decides it's done for the day. I've got thirty seconds at the floor huddle to figure out what moves to Line 3, what can slide a" + }, + { + "role": "user", + "content": "*(nods, listening)*\n\n—what can slide a... ?\n\n*(Go ahead and finish that thought about what you're deciding when a line goes down at the morning huddle.)*" + }, + { + "role": "assistant", + "content": "*(gestures with one hand)*\n\n—what can slide a day or two without getting us screamed at, and what absolutely has to ship today no matter what. \n\nRight now I'm doing that in my head while twenty people are standing around waiting. I know Meridian can't slip. I know the small distributors usually can. But when Line 2's down and it's got four orders on it, I'm juggling: can Line 1 pick up that white run even though it's slower? Do I burn overtime to get Line 3 running a second shift? Which tint order can I push to tomorrow without Commercial losing their minds?\n\n*(slight shrug)*\n\nIt'd be nice to have run that scenario on" + }, + { + "role": "user", + "content": "*(waiting for you to finish)*\n\n—run that scenario on... ?" + }, + { + "role": "assistant", + "content": "*(slight laugh, a bit self-conscious)*\n\n...on *something* before I'm standing there at the huddle with everyone staring at me. \n\nI mean, right now it's all in here *(taps temple)*—eleven years of \"this usually works\" and \"that customer will kill us if we're late.\" But if I could plug in \"Line 2 down for four hours starting 06:00\" and see what the model says to do, or at least see what happens if I move the Meridian order versus leave it... that'd be worth something.\n\n*(leans forward slightly)*\n\nThat's what I'm hoping this gets us. Not replacing the sheet—I'm still gonna own the schedule—but giving me a way to test the moves before I make them. Especially the expensive ones, like whether sitting a line idle really saves us money versus always keeping it running." + }, + { + "role": "user", + "content": "Okay, that's clear. You want to test idle-versus-switch decisions and line-down replanning before you commit, especially when the consequences are expensive or customer-visible. The model needs to show you what happens under different moves so you can compare them.\n\nLet me walk through how one of these actually plays out. Think back to the last time you had that idle-time call—Line 2 or any line finishes one kind of work, and you have to decide whether to switch to something different or let it sit.\n\nWhat was the line, what had it just finished, and what were you looking at as the options?" + }, + { + "role": "assistant", + "content": "*(thinks for a moment)*\n\nOkay, yeah—this was maybe two weeks ago. Line 2 had just wrapped a big white run, VW-01, the contractor flat. Finished around mid-morning, like 10:30. \n\nNext white order in the book was another VW-01, but that wasn't due until Thursday—this was Tuesday. So I had a day and a half of gap. But sitting right there I had a couple tint orders that were due Wednesday, decent-sized runs, and Line 2 could handle them.\n\n*(l" + }, + { + "role": "user", + "content": "*(listening, waiting for you to continue)*\n\n—and Line 2 could handle them... ?" + } + ], + "violations": [ + { + "code": "missing-workpiece", + "detail": "No recoverable runbook-ir workpiece was emitted." + } + ], + "wroteCaptureStore": false, + "instrument": { + "sourceCommit": "794fe2fbf1eaeba3fc816c6e3d1755d7b444125d", + "instrumentStatus": "", + "fileSha256": { + "yarn.lock": "3f445d255ef7893c1941f5610e2a813a903d5b9053c9831012c3d7c3875571dc", + "apps/brunch-agent/package.json": "27182018edf5c595522815ea28d78e549d387c127714c0ea2c54a84822ead742", + "apps/brunch-agent/vite.config.ts": "f1a50233829599dff2d5ef7ee0cd10bdb55d7fe65a52a6e85913e00a33a5300d", + "apps/brunch-agent/src/app.ts": "979efb9172c82df7499b218125735929a9586a0d9355e042e9ff2402955f385e", + "apps/brunch-agent/src/db.ts": "9f3a2598df3ce6737de1348afa8baf7deeeeac51b8202bf87adb76dc96374481", + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e", + "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744", + "apps/brunch-agent/src/conversation/identity.ts": "b52f764ba6e6bb50602d477eb93234aa6a900c843412551de40ea6ad3050e5e6", + "apps/brunch-agent/src/conversation/transcript.ts": "b96cf8d2b8352d414f674985b7071c9570323ccaf862f7e77ec76311e4b11d44", + "apps/brunch-agent/src/http/routes.ts": "7d8389931c57671ad8376d0a0ab497413013d47b012ffbf531339d0d1ce3a0cd", + "apps/brunch-agent/src/evaluations/runbook/artifacts.ts": "15951ab3705b126d64a2593731b50775a16915298dae87b8cf7c99e265e2c2f4", + "apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts": "60215179f5d48bacbf4da7b0876d049ed8b15b4b774d99eab092a1297502a3b4", + "apps/brunch-agent/src/evaluations/runbook/load-built-application.ts": "c4b28985ad98dd1dde5afb8838ba6f9445692d208adde05c22f30f6074365056", + "apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts": "a0a197af22d0a007f73836db49cd13a2c776e57d0dab047e537868c9ff2f33f9", + "libs/@hashintel/brunch-agent/packages/core/package.json": "e12d9cc79d38c43e5543a5667753e5049dfbf21bec425c25942f1c4cbb922a1f", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "0bd463eaab4eb68b86431bf26e0e01f90c89437b2888c86f4da53d7a0f39ec13", + "libs/@hashintel/brunch-agent/packages/core/src/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json": "dca0db144c530bcf66c5400c029efa5e1136c9d7f81ed174343e891985200c33", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md": "9915751f11baf06de31b237ee8df57b5217f2e7f0531090370a7e08d0282b6c2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "d5bac87a192b92fb6b7d749ec3d58116f9e39527b59e425c60aaeedf5cc13bd0", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts": "a283342146c8d2c2cb8056c588e76c1c3094f1e21e731be251c8fcdb335ed1b2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/instructions.md": "ea3a8755a576e98d35fce6a1e1f928a61148eb713f0bc2c2f818d104eb5f3832", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/profile.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/workpiece-template.md": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/checks.md": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md": "817e43d15848bcec3c720a3e8b1104d31faf3035fd7ae8269d0a97ee43ef4a9a", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md": "faba12be8642400e60df37bc5bf5c321541f922d9f0433cf1455423492a2be7a", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md": "f57f4177edbe957092e86c19826a003b6c9257c319a129008428839e4cf0e2b7", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.ir.md": "3ee05f3d5d88f3061b360467e755606f7c43e07bdfacdfebb094bebaee23e732", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.omniscient.md": "82dbe47745b175cca93d50ea64a45715bfafd860d109904c1d5136282fae0aa0", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f.cold.md": "c832a84226a4fb795281b96185b5efae13bdcf59a2db6c9cf481e5cd9b0b81b8", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.ir.md": "4d15050ac6a2a89e1f6a371a604522504bbb90c0893c2029773b1b906db85043", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.omniscient.md": "ee81f16127bf255a3247a3c9f42508970542beebf38a3445d9be18e0f765e120", + "libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c.cold.md": "d072d7fe27e6b8458ccb7d9119c7901fcc45d47d88141b970ce2d9ac2a0b2224" + }, + "builtArtifactManifest": [ + { + "path": "apps/brunch-agent/dist/app.mjs", + "sha256": "5ce74b2646c00b959b3ee847b47e006717ccc21e1fba769ce818566cba6da62e" + }, + { + "path": "apps/brunch-agent/dist/node-server-BWNSXBme.mjs", + "sha256": "90343929bc5274e0644003e8d7ea69b02003ac4d3b364a0afdb0be0e1032c066" + }, + { + "path": "apps/brunch-agent/dist/server.mjs", + "sha256": "c2d4ccb5aa999e33d33530ba87280e4bb6d6aacd41df630876297bfabea8f04e" + } + ], + "builtArtifactManifestSha256": "5bb67f5cbb1315e789426d83780ed9af23c58b1a04cf14a4b62a831cae435cd0" + }, + "failure": { + "name": "Error", + "message": "The simulated expert returned no text", + "stack": "Error: The simulated expert returned no text\n at askExpert (file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts:525:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v3-run.ts:597:25" + }, + "transcript": "## User\nI'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.\n\n## Assistant\nI'll help you develop a model of your scheduling operation that can support those testing decisions. Let me activate the specialized guidance for this kind of work, then we'll talk through how your operation actually runs.Good. Before we dig into the details of how scheduling works at your plant, let me understand what you're trying to get out of this model.\n\nYou mentioned your boss wants to test scheduling decisions before making them. Can you give me an example of a scheduling decision you'd want to test? What would you be trying to find out or avoid?\n- tool activate_skill (toolu_0134r7HowAbrVpNe6SU6XnvN): \"Run the skill named \\\"sdcpn-modelling\\\".\\n\\n<skill_instructions>\\n# Capability-aware lifecycle\\n\\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\\n\\n## Select the runtime branch\\n\\n### Interactive elicitation, review, or revision\\n\\nInterview in the person's operational vocabulary. Before substantive\\nelicitation or workpiece revision, read\\n`references/universal-elicitation.md` and `references/profile.md`.\\n\\nFor review, first inspect the supplied workpiece or model. If it already answers\\nthe question, answer without reading the elicitation references or interviewing.\\nIf the review exposes a gap that requires human knowledge, read both elicitation\\nreferences and then ask exactly one focused question that resolves the smallest\\nconsequential gap.\\n\\nRead `templates/workpiece.md` only when first creating or materially revising\\nthe workpiece. Do not load the template merely to inspect an existing artifact,\\nanswer a resolvable review question, or frame the first elicitation question.\\nConstruct only when the mounted capabilities actually permit construction in\\nthis conversation.\\n\\n### Construct-only execution\\n\\nUse the supplied workpiece as the complete modelling input. Do not interview.\\nRead `references/pn-construction.md` and `references/checks.md`, then use the\\nmounted construction tools. If a consequential workpiece gap prevents faithful\\nconstruction, report the gap and the smallest question a later interactive\\nelicitation must answer; do not ask it or invent an answer in this conversation.\\n\\n## Procedure\\n\\n### Orient\\n\\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\\n\\n### Elicit or revise\\n\\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\\n\\n### Maintain the workpiece\\n\\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\\n\\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\\n\\n### Construct\\n\\nConstruct only from the current workpiece. Read\\n`references/pn-construction.md` and `references/checks.md` before beginning.\\nUse mounted Petrinaut tools for every net change and inspect the resulting\\ndefinition rather than emitting free-form net JSON. If the required tools are\\nabsent, limit the result to the workpiece and construction-ready notes.\\n\\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\\n\\n### Check and deliver\\n\\nApply `references/checks.md` whenever construction is prepared or attempted.\\nDeliver the current workpiece in every branch. Deliver a net only when the\\nmounted tool path has produced and checked one. State what the result can\\nsupport, what remains open, what was assumed or simplified, and what the target\\nor current tools could not represent.\\n\\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\\n\\n## Resource discipline\\n\\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\\n</skill_instructions>\\n\\nSupporting skill resources are available but are not loaded into context unless needed:\\n<skill_resources>\\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\\n</skill_resources>\"\n- tool read_skill_resource (toolu_01Qk8dUQEhHobkbtiSWKapLU): \"# Universal Elicitation Reference\\n\\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\\n\\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\\n\\n## Directives\\n\\n### Work from purpose\\n\\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\\n\\n### Accumulate posture conversationally\\n\\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\\n\\n### Follow the person's account\\n\\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\\n\\n### Protect interaction bandwidth\\n\\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\\n\\n### Preserve authorship and uncertainty\\n\\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\\n\\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\\n\\n### Treat divergence as information\\n\\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\\n\\n### Maintain a recoverable workpiece\\n\\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\\n\\n### Stop honestly\\n\\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\\n\\n## Recognition\\n\\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\\n\\n### Vague or compressed language\\n\\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\\n\\n### Normative language\\n\\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\\n\\n### Tension within or between accounts\\n\\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\\n\\n### Unexplained terms and artifacts\\n\\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\\n\\n### Burden, impatience, or limited availability\\n\\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\\n\\n### Diminishing yield\\n\\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\\n\\n### Assent without independent wording\\n\\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\\n\\n### Silence and absence\\n\\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\\n\\n## Operations\\n\\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\\n\\n### Select the smallest consequential absence\\n\\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\\n\\n### Slice a concrete case\\n\\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\\n\\n### Sweep one property\\n\\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\\n\\n### Ask for the last occurrence\\n\\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\\n\\n### Ask for the basis\\n\\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\\n\\n### Ground a term or artifact\\n\\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\\n\\n### Clarify until observable\\n\\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\\n\\n### Use contrastive cases\\n\\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\\n\\n### Investigate quantities relative to purpose\\n\\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\\n\\n### Turn an unknown into a decision threshold\\n\\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\\n\\n### State a contradiction without resolving it\\n\\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\\n\\n### Restate for correction\\n\\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\\n\\n### Propose structure for correction\\n\\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\\n\\n### Deposit and defer\\n\\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\\n\\n### Press without trapping\\n\\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\\n\\n### Explore a rare or severe outcome\\n\\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\\n\\n### Seek a witness or counterexample\\n\\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\\n\\n### Trade concrete outcomes\\n\\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\\n\\n### Close with one correction opportunity\\n\\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\\n\\n## Coverage\\n\\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\\n\\nA workpiece may need to preserve:\\n\\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\\n- a cold-readable account in the person's vocabulary, including consequential local terms;\\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\\n- decisions, alternatives, and reasons distinctions among them matter;\\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\\n- exact evidence where later interpretation depends on the person's wording;\\n- normalized accounts and agent inferences without laundering their authorship;\\n- assumptions with why they were introduced and how they could be checked;\\n- unknown, not-yet-asked, declined, and deferred material without conflation;\\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\\n- open questions with the consequence of leaving them open and the condition for returning.\\n\\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\\n\\n## Verification\\n\\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\\n\\n### Before asking\\n\\n- The question serves the stated purpose or resolves an active uncertainty.\\n- It follows the person's thread rather than the order of a schema or template.\\n- It asks one focused thing, or a small set that genuinely shares one frame.\\n- A proposed answer, category, number, or distinction is identified as yours.\\n\\n### Before recording\\n\\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\\n- Assent to your wording has not been presented as independently originated evidence.\\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\\n\\n### Before closing\\n\\n- The result's purpose and consequential account have been offered for correction.\\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\\n- The person has not been kept in an irrelevant thread merely to fill a category.\\n- A stop produces a useful partial result rather than a false claim of completion.\\n\\n### Failure signals and repairs\\n\\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\\n\"\n- tool read_skill_resource (toolu_0154FnM7YUaQ8aZEccuVBLHm): \"# Operational-Process and SDCPN Elicitation\\n\\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\\n\\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\\n\\n## Directives\\n\\n### Build the operational account the purpose needs\\n\\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\\n\\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\\n\\n### Keep target structure backstage\\n\\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\\n\\n### Preserve operational context\\n\\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\\n\\n### Treat operational patterns as hypotheses\\n\\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\\n\\n## Recognition\\n\\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\\n\\n### Language and account signals\\n\\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\\n\\n### Operational situation patterns\\n\\n#### Timed work\\n\\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\\n\\n#### Conditional or probabilistic outcome\\n\\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\\n\\n#### Contended resource\\n\\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\\n\\n#### Consumed, reserved, or read input\\n\\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\\n\\n#### Gate, release, trigger, or prerequisite\\n\\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\\n\\n#### Continuous quantity and threshold\\n\\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\\n\\n#### Mode change\\n\\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\\n\\n#### Batch, lot, load, or grouped movement\\n\\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\\n\\n#### Spatial transfer\\n\\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\\n\\n#### Event, failure, retry, and recovery\\n\\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\\n\\n#### Policy under pressure\\n\\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\\n\\n#### Hidden waiting\\n\\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\\n\\n## Operations\\n\\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\\n\\n### Choose the case unit before slicing\\n\\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\\n\\n### Link the slice to the objective\\n\\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\\n\\n### Expose the process spine\\n\\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\\n\\n### Sweep operational concerns, not headings\\n\\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\\n\\n### Distinguish consumed, reserved, and read inputs\\n\\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\\n\\n### Sweep what can befall an activity\\n\\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\\n\\n### Test practiced policy with a borderline case\\n\\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\\n\\n### Close a resource account\\n\\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\\n\\n### Close a mode change in both directions\\n\\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\\n\\n### Turn waiting into a causal question\\n\\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\\n\\n### Ask what is conserved\\n\\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\\n\\n### Establish retry scope\\n\\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\\n\\n### Establish validation from observable behavior\\n\\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\\n\\n## Coverage\\n\\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\\n\\n### Purpose, goals, measures, constraints, and thresholds\\n\\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\\n\\n### Process boundary, triggers, prerequisites, and initial conditions\\n\\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\\n\\n### Participants, locations, flowing things, and resources\\n\\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\\n\\n### Activities, inputs, outputs, and resource use\\n\\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\\n\\n### Flow, branching, joining, failure, retry, and recovery\\n\\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\\n\\n### Time, quantities, arrivals, and stochastic behavior\\n\\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\\n\\n### Policies, exceptions, practiced rules, and contextual regimes\\n\\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\\n\\n### Validation, evidence sources, and data bindings\\n\\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\\n\\n### Things not independently elicited as target nodes\\n\\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\\n\\n## Verification\\n\\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\\n\\n### Purpose and process\\n\\n- At least one simulation question, comparison, or decision is stated in the person's terms.\\n- Every objective depends on recorded process material or remains visibly unsupported.\\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\\n\\n### Operational semantics\\n\\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\\n- Hidden waiting has not silently become an activity or unexplained queue.\\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\\n\\n### Quantities and context\\n\\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\\n\\n### Failure signals and repairs\\n\\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\\n\"\n\n## User\n*(leaning back slightly, half-smile)*\n\nWell, the big one right now is this idle-time question. You know — if I've got Line 2 finishing a white run at, say, 10:00 in the morning, and the next white order isn't due until tomorrow but there's a tint job sitting right there in the book... do I take the three-hour washdown hit to switch into the tint, or do I just let the line sit and wait for the next white?\n\nI do it by gut now. If it's just an hour or two and the white's a big Meridian order, I'll hold the line. But nobody can tell me if that's actually the smart play or if I'm leaving money on the table. My boss sees \"Line 2 idle 6 hours\" on the shift report and wants to know why I didn't run *something*.\n\n*(taps the desk)*\n\nThe other one — and this happens maybe twice a month — is when a line goes down at six in the morning. Filler jams on Line 2, or the mill motor on Line 1 decides it's done for the day. I've got thirty seconds at the floor huddle to figure out what moves to Line 3, what can slide a\n\n## Assistant\n*(nods, listening)*\n\n—what can slide a... ?\n\n*(Go ahead and finish that thought about what you're deciding when a line goes down at the morning huddle.)*\n\n## User\n*(gestures with one hand)*\n\n—what can slide a day or two without getting us screamed at, and what absolutely has to ship today no matter what. \n\nRight now I'm doing that in my head while twenty people are standing around waiting. I know Meridian can't slip. I know the small distributors usually can. But when Line 2's down and it's got four orders on it, I'm juggling: can Line 1 pick up that white run even though it's slower? Do I burn overtime to get Line 3 running a second shift? Which tint order can I push to tomorrow without Commercial losing their minds?\n\n*(slight shrug)*\n\nIt'd be nice to have run that scenario on\n\n## Assistant\n*(waiting for you to finish)*\n\n—run that scenario on... ?\n\n## User\n*(slight laugh, a bit self-conscious)*\n\n...on *something* before I'm standing there at the huddle with everyone staring at me. \n\nI mean, right now it's all in here *(taps temple)*—eleven years of \"this usually works\" and \"that customer will kill us if we're late.\" But if I could plug in \"Line 2 down for four hours starting 06:00\" and see what the model says to do, or at least see what happens if I move the Meridian order versus leave it... that'd be worth something.\n\n*(leans forward slightly)*\n\nThat's what I'm hoping this gets us. Not replacing the sheet—I'm still gonna own the schedule—but giving me a way to test the moves before I make them. Especially the expensive ones, like whether sitting a line idle really saves us money versus always keeping it running.\n\n## Assistant\nOkay, that's clear. You want to test idle-versus-switch decisions and line-down replanning before you commit, especially when the consequences are expensive or customer-visible. The model needs to show you what happens under different moves so you can compare them.\n\nLet me walk through how one of these actually plays out. Think back to the last time you had that idle-time call—Line 2 or any line finishes one kind of work, and you have to decide whether to switch to something different or let it sit.\n\nWhat was the line, what had it just finished, and what were you looking at as the options?\n\n## User\n*(thinks for a moment)*\n\nOkay, yeah—this was maybe two weeks ago. Line 2 had just wrapped a big white run, VW-01, the contractor flat. Finished around mid-morning, like 10:30. \n\nNext white order in the book was another VW-01, but that wasn't due until Thursday—this was Tuesday. So I had a day and a half of gap. But sitting right there I had a couple tint orders that were due Wednesday, decent-sized runs, and Line 2 could handle them.\n\n*(l\n\n## Assistant\n*(listening, waiting for you to continue)*\n\n—and Line 2 could handle them... ?", + "rawConversationSnapshot": { + "v": 1, + "conversationId": "conv_01M1GZ9MJANJ8TFCDJWMWXJXV7", + "offset": "0000000000000000_0000000000000107", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFHWjlNSjhaSkFITUc5TlBLQlc1NlRI", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZ9MJ8ZJAHMG9NPKBW56TH", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZ9P8N3K4XZBTAXQ4Y3W9P", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZ9MJ8ZJAHMG9NPKBW56TH", + "turnId": "turn_01M1GZ9MK4VSFTCNDN93EYWE78", + "parts": [ + { + "type": "reasoning", + "text": "The user is a master scheduler at a coatings plant who needs a simulation model of their scheduling process. Their boss wants to test scheduling decisions, and they need a Petri net model compatible with their tooling. The user explicitly says they're not the modelling person and can't tell me much about the format.\n\nThis is exactly the kind of task the sdcpn-modelling skill is designed for. I need to:\n1. Activate the sdcpn-modelling skill to get specialized instructions\n2. Conduct an elicitation interview about their scheduling operation\n3. Build a recoverable workpiece as I learn\n4. Eventually construct an SDCPN if Petrinaut tools are available\n\nThe user is asking me to interview them, so I should begin the elicitation process. But first, I must activate the skill to get the detailed instructions for how to conduct this properly.\n\nKey points from the universal elicitation guidance:\n- Establish the purpose first - what decisions/questions must the model help with?\n- Use their vocabulary, not Petri net terminology during elicitation\n- Follow concrete cases rather than schema traversal\n- Don't invent content or increase precision silently\n- Keep source intent distinct from formalism transformation\n\nLet me activate the skill now.", + "state": "done" + }, + { + "type": "text", + "text": "I'll help you develop a model of your scheduling operation that can support those testing decisions. Let me activate the specialized guidance for this kind of work, then we'll talk through how your operation actually runs.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_0134r7HowAbrVpNe6SU6XnvN", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation, review, or revision\n\nInterview in the person's operational vocabulary. Before substantive\nelicitation or workpiece revision, read\n`references/universal-elicitation.md` and `references/profile.md`.\n\nFor review, first inspect the supplied workpiece or model. If it already answers\nthe question, answer without reading the elicitation references or interviewing.\nIf the review exposes a gap that requires human knowledge, read both elicitation\nreferences and then ask exactly one focused question that resolves the smallest\nconsequential gap.\n\nRead `templates/workpiece.md` only when first creating or materially revising\nthe workpiece. Do not load the template merely to inspect an existing artifact,\nanswer a resolvable review question, or frame the first elicitation question.\nConstruct only when the mounted capabilities actually permit construction in\nthis conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview.\nRead `references/pn-construction.md` and `references/checks.md`, then use the\nmounted construction tools. If a consequential workpiece gap prevents faithful\nconstruction, report the gap and the smallest question a later interactive\nelicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read\n`references/pn-construction.md` and `references/checks.md` before beginning.\nUse mounted Petrinaut tools for every net change and inspect the resulting\ndefinition rather than emitting free-form net JSON. If the required tools are\nabsent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted.\nDeliver the current workpiece in every branch. Deliver a net only when the\nmounted tool path has produced and checked one. State what the result can\nsupport, what remains open, what was assumed or simplified, and what the target\nor current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\n</skill_resources>", + "durationMs": 3 + }, + { + "type": "reasoning", + "text": "Good, the skill is activated. Now I'm in \"Interactive elicitation, review, or revision\" mode. According to the skill instructions, I should:\n\n1. Read `references/universal-elicitation.md` and `references/profile.md` before substantive elicitation\n2. Interview in the person's operational vocabulary\n3. Read `templates/workpiece.md` only when first creating or materially revising the workpiece\n4. Orient first - establish purpose and context\n\nLet me read those two reference files first to get the detailed elicitation guidance.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01Qk8dUQEhHobkbtiSWKapLU", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md" + }, + "output": "# Universal Elicitation Reference\n\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_0154FnM7YUaQ8aZEccuVBLHm", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Good, I now have the detailed elicitation guidance. Key takeaways:\n\n1. Start with orientation - establish purpose, what decisions the model must support\n2. Use their vocabulary (not Petri net terms)\n3. Follow concrete cases\n4. One question or one coherent group at a time\n5. Build the workpiece as we go\n\nNow I should begin the interview. I need to orient first - understand:\n- What decisions or questions the model must help with\n- The boundary and horizon\n- What \"better\" means for testing scheduling decisions\n- Available time and tolerance for depth\n\nI'll start with a conversational opening question that gets at the purpose and a concrete case, not an intake form. The user already said \"test scheduling decisions before making them\" - that's the general purpose. I should deepen that a bit and then move to a concrete case.", + "state": "done" + }, + { + "type": "text", + "text": "Good. Before we dig into the details of how scheduling works at your plant, let me understand what you're trying to get out of this model.\n\nYou mentioned your boss wants to test scheduling decisions before making them. Can you give me an example of a scheduling decision you'd want to test? What would you be trying to find out or avoid?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWkFTOEg3VFpXOFhHS1BZQlpIWDBa", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZAS8H7TZW8XGKPYBZHX0Z", + "parts": [ + { + "type": "text", + "text": "*(leaning back slightly, half-smile)*\n\nWell, the big one right now is this idle-time question. You know — if I've got Line 2 finishing a white run at, say, 10:00 in the morning, and the next white order isn't due until tomorrow but there's a tint job sitting right there in the book... do I take the three-hour washdown hit to switch into the tint, or do I just let the line sit and wait for the next white?\n\nI do it by gut now. If it's just an hour or two and the white's a big Meridian order, I'll hold the line. But nobody can tell me if that's actually the smart play or if I'm leaving money on the table. My boss sees \"Line 2 idle 6 hours\" on the shift report and wants to know why I didn't run *something*.\n\n*(taps the desk)*\n\nThe other one — and this happens maybe twice a month — is when a line goes down at six in the morning. Filler jams on Line 2, or the mill motor on Line 1 decides it's done for the day. I've got thirty seconds at the floor huddle to figure out what moves to Line 3, what can slide a", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZAVM7B433H55B39E4Y9MW", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZAS8H7TZW8XGKPYBZHX0Z", + "turnId": "turn_01M1GZAS8R69G30S6E394SNK33", + "parts": [ + { + "type": "reasoning", + "text": "The user is giving me excellent concrete detail about their scheduling operation. I should let them finish their thought about what happens when a line goes down - they were mid-sentence about \"what moves to Line 3, what can slide a...\" - they got cut off.\n\nI should acknowledge what they're saying and let them complete the thought, then continue building understanding around these two key decisions:\n1. The idle-time vs. washdown-switching decision\n2. The line-down emergency rescheduling decision\n\nThis is good purpose orientation - I'm learning what decisions the model needs to support. I should not jump into detailed process questions yet; I should stay at this level until I understand the objectives clearly, then move to a concrete case.\n\nI need to be careful not to load questions with Petri-net terminology. Stay in their vocabulary: lines, orders, runs, washdowns, tints vs whites, etc.", + "state": "done" + }, + { + "type": "text", + "text": "*(nods, listening)*\n\n—what can slide a... ?\n\n*(Go ahead and finish that thought about what you're deciding when a line goes down at the morning huddle.)*", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWkJBMkM5UVIzM0UxOTNQNkEyTlEx", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZBA2C9QR33E193P6A2NQ1", + "parts": [ + { + "type": "text", + "text": "*(gestures with one hand)*\n\n—what can slide a day or two without getting us screamed at, and what absolutely has to ship today no matter what. \n\nRight now I'm doing that in my head while twenty people are standing around waiting. I know Meridian can't slip. I know the small distributors usually can. But when Line 2's down and it's got four orders on it, I'm juggling: can Line 1 pick up that white run even though it's slower? Do I burn overtime to get Line 3 running a second shift? Which tint order can I push to tomorrow without Commercial losing their minds?\n\n*(slight shrug)*\n\nIt'd be nice to have run that scenario on", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZBBM07GMYTYCE5EDX142Z", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZBA2C9QR33E193P6A2NQ1", + "turnId": "turn_01M1GZBA2KGZ4HC3N545GW290Z", + "parts": [ + { + "type": "reasoning", + "text": "The person is finishing their thought - they're saying it would be nice to have run that scenario beforehand. I should let them finish naturally rather than interrupting.", + "state": "done" + }, + { + "type": "text", + "text": "*(waiting for you to finish)*\n\n—run that scenario on... ?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWkJNWDE0RjQ0MVFTS1lFOUsxWTVa", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZBMX14F441QSKYE9K1Y5Z", + "parts": [ + { + "type": "text", + "text": "*(slight laugh, a bit self-conscious)*\n\n...on *something* before I'm standing there at the huddle with everyone staring at me. \n\nI mean, right now it's all in here *(taps temple)*—eleven years of \"this usually works\" and \"that customer will kill us if we're late.\" But if I could plug in \"Line 2 down for four hours starting 06:00\" and see what the model says to do, or at least see what happens if I move the Meridian order versus leave it... that'd be worth something.\n\n*(leans forward slightly)*\n\nThat's what I'm hoping this gets us. Not replacing the sheet—I'm still gonna own the schedule—but giving me a way to test the moves before I make them. Especially the expensive ones, like whether sitting a line idle really saves us money versus always keeping it running.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZBP2EV4R1VV8TW4SH8XMM", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZBMX14F441QSKYE9K1Y5Z", + "turnId": "turn_01M1GZBMX55BQCPQM67P7KDAGK", + "parts": [ + { + "type": "reasoning", + "text": "Good. This is clear. Let me capture what I've learned about purpose:\n\n1. **Primary decision: idle-time trade-off** — when a line finishes one product type (e.g., white) and the next scheduled job is the same type but hours/a day away, versus a different type (e.g., tint) available now but requiring washdown. The question is whether to switch and pay the washdown cost or let the line sit idle.\n\n2. **Secondary decision: line-down reallocation** — when a line fails (filler jam, motor failure), quickly deciding which orders move to other lines, which can slip without customer consequences, whether to add overtime/extra shifts.\n\n3. **Objectives/measures:**\n - Not explicitly quantified yet, but implicit: cost (washdown cost vs. idle cost), customer satisfaction/on-time delivery (some customers like Meridian can't slip, small distributors can), overtime cost\n - Boss sees idle time on shift reports and questions it\n - \"leaving money on the table\" suggests cost/revenue optimization\n\n4. **Audience:** The scheduler (this person), maybe the boss\n\n5. **Validation:** Being able to plug in scenarios like \"Line 2 down for four hours starting 06:00\" and compare moves\n\nNow I need to follow the concrete case. The idle-time question is the \"big one right now,\" so I'll start there. I should slice one concrete remembered case of this decision. The person gave me a partial example: Line 2 finishing white at 10:00 AM, next white not due until tomorrow, tint job available requiring 3-hour washdown.\n\nLet me ask about a real instance of this to understand:\n- What actually happened\n- What the line was running (the \"white run\")\n- What \"finishing\" means\n- What the next work was\n- How the decision was made\n- What the consequences were\n\nI should avoid target vocabulary (places, transitions, tokens) and stay in operational terms (lines, orders, runs, washdown, products, customers).", + "state": "done" + }, + { + "type": "text", + "text": "Okay, that's clear. You want to test idle-versus-switch decisions and line-down replanning before you commit, especially when the consequences are expensive or customer-visible. The model needs to show you what happens under different moves so you can compare them.\n\nLet me walk through how one of these actually plays out. Think back to the last time you had that idle-time call—Line 2 or any line finishes one kind of work, and you have to decide whether to switch to something different or let it sit.\n\nWhat was the line, what had it just finished, and what were you looking at as the options?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWkNEWDNTREY3U0I5M1RKN0QxRVYw", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GZCDX3SDF7SB93TJ7D1EV0", + "parts": [ + { + "type": "text", + "text": "*(thinks for a moment)*\n\nOkay, yeah—this was maybe two weeks ago. Line 2 had just wrapped a big white run, VW-01, the contractor flat. Finished around mid-morning, like 10:30. \n\nNext white order in the book was another VW-01, but that wasn't due until Thursday—this was Tuesday. So I had a day and a half of gap. But sitting right there I had a couple tint orders that were due Wednesday, decent-sized runs, and Line 2 could handle them.\n\n*(l", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GZCFBC7KQ8H8QGBXQR3B2R", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GZCDX3SDF7SB93TJ7D1EV0", + "turnId": "turn_01M1GZCDX81NSDZS6WSKRRZZ1B", + "parts": [ + { + "type": "reasoning", + "text": "The user is mid-sentence, let them finish.", + "state": "done" + }, + { + "type": "text", + "text": "*(listening, waiting for you to continue)*\n\n—and Line 2 could handle them... ?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1GZ9MJ8ZJAHMG9NPKBW56TH", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZ9MJ8ZJAHMG9NPKBW56TH" + }, + { + "submissionId": "sub_01M1GZAS8H7TZW8XGKPYBZHX0Z", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZAS8H7TZW8XGKPYBZHX0Z" + }, + { + "submissionId": "sub_01M1GZBA2C9QR33E193P6A2NQ1", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZBA2C9QR33E193P6A2NQ1" + }, + { + "submissionId": "sub_01M1GZBMX14F441QSKYE9K1Y5Z", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZBMX14F441QSKYE9K1Y5Z" + }, + { + "submissionId": "sub_01M1GZCDX3SDF7SB93TJ7D1EV0", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GZCDX3SDF7SB93TJ7D1EV0" + } + ], + "incarnation": "inc_01M1GZ9MJ8E3E1V119DSEJ021N" + }, + "rawConversationSnapshotSha256": "dc2d563751f4ef37bb344960b807dd08eef498414dc83d2c79d9c006e2442d56" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-legacy-baseline/condition-5-turn-latency.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-legacy-baseline/condition-5-turn-latency.md index ef484917c09..e0f1f58c037 100644 --- a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-legacy-baseline/condition-5-turn-latency.md +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-legacy-baseline/condition-5-turn-latency.md @@ -6,7 +6,7 @@ > [folded store](transcripts/cycle-1/condition-5-captures.json)). Commissioned by Lu after the read-out > showed 2.4 minutes per interviewer turn: "not going to be viable at all, for a working > application". Inputs: the raw record's per-turn tool calls, signals, sweep results, and usage -> totals; the runner [`harness-run.ts`](../../../../evaluations/protocols/legacy-baseline/harness-run.ts); +> totals; the historical runner `b59b323bf1b26eee9a2345a8412ca466f5d6e851:libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/harness-run.ts`; > `packages/core/src/sweep-protocol.ts`; `packages/binding-flue`'s sweep and settlement path; the > Flue `OperationOptions`, `turn` event, and `DurabilityConfig` types in `node_modules/flue`. > Status: **evidence and recommendation, not authority** — nothing here changes a spec, a key, or diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/campaign-abort.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/campaign-abort.md new file mode 100644 index 00000000000..5567dbd88f9 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/campaign-abort.md @@ -0,0 +1,17 @@ +# Prospective candidate v2 — abort adjudication + +## Disposition + +Preserve v2 as an aborted operational campaign. Do not run replication 3, replace either observed member, grade v2 as workpiece-quality evidence, or mix it into the latest flat-prompt control. + +| Replication | Outcome | Attribution | Quality use | +| --- | --- | --- | --- | +| 1 | Provider authentication returned `401` before an interviewer response | Stale credential remained in the relaunched process environment | None | +| 2 | Simulated expert returned `stop_reason: refusal` with no text after three ordinary exchanges | Expert-simulator/provider boundary | None; no workpiece | +| 3 | Not run | Owner stopped the confounded campaign | None | + +Both observed failures have immutable nonce-bearing JSON records. Replication 2 also retains the exact partial Flue snapshot and expert exchange. Neither failure establishes a defect in the Mission 4 workpiece architecture, and neither supplies a gradeable workpiece. + +## Reorientation + +The owner narrowed the Mission 4 comparison to the selected architecture's workpiece quality against the latest two valid flat-prompt baseline workpieces. [`prospective-runbook-v3`](../../../../evaluations/protocols/prospective-runbook-v3/protocol.md) freezes that question, hashes the exact controls, keeps runtime accounting separate from quality scores, and adds credential preflight outside campaign membership. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/exact-candidate-walkthrough.md b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/exact-candidate-walkthrough.md new file mode 100644 index 00000000000..5975769aeb7 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/exact-candidate-walkthrough.md @@ -0,0 +1,27 @@ +# Mission 4 exact-candidate walkthrough + +Status: accepted pre-freeze walkthrough of the production candidate. This is a paper and +hermetic-oracle review, not a paid campaign member or behavioral superiority claim. + +| Case | Recognition and operation | Focused question | Authoritative workpiece home and epistemic treatment | Construction boundary and oracle | +| --- | --- | --- | --- | --- | +| Opening overload | Treat many opening facts as an interaction-bandwidth risk; select the smallest consequential absence and follow one concrete case. | Which decision should this model help you make first? | `Purpose and posture`; supplied facts remain expert evidence and agent organization remains normalization. | Do not construct or load the template. Built-agent routing must read universal + profile guidance and ask one question before any template read. | +| Policy versus practice | Recognize normative language and test the practiced rule with the last contested case. | In the last contested case, what rule did people actually use to decide who went first? | `Policies, exceptions, practiced rules, and contextual regimes`; retain prescribed and practiced accounts separately. | Compile only evidenced practiced conditions. Cold review must find both accounts rather than a silently selected rule. | +| Contextual quantities | Treat an unqualified value as potentially hiding mode, direction, load, calendar, or item regimes; investigate the selector relevant to purpose. | For the model's stated comparison, in which operating regime does the 20-minute duration apply? | `Time, quantities, arrivals, and stochastic behavior`; retain source precision and selecting context without averaging. | Use conditioned parameters or preserve an unknown. Checks must find no unsupported unconditional value. | +| Scarce-resource reservation and release | Recognize a contended reserved resource and close the smallest missing release distinction. | What observable event makes the reserved crew available to other work again? | `Activities, inputs, outputs, and resource use`; release stays `Not yet asked` until answered, then becomes expert evidence. | Hold availability between acquisition and evidenced release. The human-gap routing test must disclose both elicitation resources and ask exactly one question. | +| Hidden waiting | Treat waiting as a symptom of a resource, prerequisite, calendar, batch, transport, policy, or disruption; ask for its enabling condition. | What observable event makes the waiting case able to continue? | `Case and process spine`; proposed causes remain agent hypotheses until supported. | Derive waiting from surrounding conditions, never an independently elicited queue. Structural review must trace any waiting place to those conditions. | +| Directional loss | Recognize a potentially asymmetric mode change and investigate the missing direction. | What time, material, or capacity loss occurs when changing from B back to A? | Relevant activity plus contextual quantity; the reverse direction remains `Not yet asked`, never inferred symmetric. | Use distinct directional structures only where supported. Missing reverse evidence remains visible rather than copied. | +| Correction versus contextual coexistence | State the differing accounts without choosing; establish correction, conflict, or selecting context. | Does the later statement replace the earlier one, or do both hold under different conditions? | Beside the affected authoritative claim; a correction leaves one active account, while coexistence retains both with conditions. | Do not mutate target structure until settled. The workpiece must show neither an average nor two unqualified active truths. | +| Unknown versus not yet asked | Absence alone does not establish ignorance; determine whether inquiry occurred. | Has this value been asked and found unknowable, or has it not yet been asked? | Beside the affected claim as exactly `Unknown` or `Not yet asked`. | Parameterize an unknown only when faithful; a material unasked distinction remains a re-entry gap. Cold review checks for laundering between states. | +| Construction-opened loss ownership | Recognize inability of target/tooling to preserve meaning as a construction finding, not new operational evidence. | None: target/tool evidence, not human knowledge, determines this loss. | `Construction notes → Target-representation losses`, referencing the unchanged authoritative operational claim; authorship is agent construction finding. | Preserve the workpiece truth and state the highest evidence level reached. Construct-only proof must return the updated full workpiece and distinguish schema acceptance, structural review, and behavior. | + +## Disposition + +The authored prompt, packaged skill, profile, workpiece, construction guidance, and checks satisfy +the nine walkthroughs. The initial construct-only fixture overstated a legacy parse proof and used +the retired workpiece shape; it was replaced before freeze with a current-format fixture whose +delivery explicitly reports tool-schema acceptance, absent structural correspondence, and untested +behavior. + +No content repair remains pre-authorized by this walkthrough. Any later campaign or visible-product +failure reopens only the smallest implicated candidate text through owner-visible adjudication. diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-1-2026-09-02T11-26-30-977Z-8edb4225.failure-46b787ce.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-1-2026-09-02T11-26-30-977Z-8edb4225.failure-46b787ce.json new file mode 100644 index 00000000000..2a17effdbbe --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-1-2026-09-02T11-26-30-977Z-8edb4225.failure-46b787ce.json @@ -0,0 +1,162 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v2", + "outputNamespaceId": "vestera-prospective-candidate-v2", + "campaignFingerprint": "302a264c244bc71adad3d4df344da073f70a3da6b4e2e7536d859b951895907e", + "replication": 1, + "runId": "prospective-runbook-v2-replication-1-2026-09-02T11-26-30-977Z-8edb4225", + "status": "invalid", + "invalidReason": "runtime-failure", + "startedAt": "2026-09-02T11:26:30.977Z", + "failedAt": "2026-09-02T11:26:31.350Z", + "interviewerModel": "claude-sonnet-4-5", + "expertModel": "claude-sonnet-4-5", + "hardStop": 8, + "latencyStopMs": 180000, + "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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "modelCalls": [ + { + "durationMs": 255, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "error", + "providerStopReason": null, + "inputTokens": 0, + "outputTokens": 0, + "totalTokens": 0, + "cost": 0 + } + ], + "expertUsage": { + "calls": 0, + "inputTokens": 0, + "outputTokens": 0, + "cacheReadTokens": 0, + "cacheWriteTokens": 0 + }, + "expertCalls": [], + "expertMessages": [], + "violations": [ + { + "code": "missing-workpiece", + "detail": "No recoverable runbook-ir workpiece was emitted." + } + ], + "wroteCaptureStore": false, + "instrument": { + "sourceCommit": "605e681cebfaeaa3fcdd0502f50ab28adc7ac63d", + "instrumentStatus": "", + "fileSha256": { + "yarn.lock": "3f445d255ef7893c1941f5610e2a813a903d5b9053c9831012c3d7c3875571dc", + "apps/brunch-agent/package.json": "128b3fd6c9624c35b226b91d39e27cf1a9cdfbdafc0314b380b7d51e1de92b46", + "apps/brunch-agent/vite.config.ts": "f1a50233829599dff2d5ef7ee0cd10bdb55d7fe65a52a6e85913e00a33a5300d", + "apps/brunch-agent/src/app.ts": "979efb9172c82df7499b218125735929a9586a0d9355e042e9ff2402955f385e", + "apps/brunch-agent/src/db.ts": "9f3a2598df3ce6737de1348afa8baf7deeeeac51b8202bf87adb76dc96374481", + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e", + "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744", + "apps/brunch-agent/src/conversation/identity.ts": "b52f764ba6e6bb50602d477eb93234aa6a900c843412551de40ea6ad3050e5e6", + "apps/brunch-agent/src/conversation/transcript.ts": "b96cf8d2b8352d414f674985b7071c9570323ccaf862f7e77ec76311e4b11d44", + "apps/brunch-agent/src/http/routes.ts": "7d8389931c57671ad8376d0a0ab497413013d47b012ffbf531339d0d1ce3a0cd", + "apps/brunch-agent/src/evaluations/runbook/artifacts.ts": "15951ab3705b126d64a2593731b50775a16915298dae87b8cf7c99e265e2c2f4", + "apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts": "60215179f5d48bacbf4da7b0876d049ed8b15b4b774d99eab092a1297502a3b4", + "apps/brunch-agent/src/evaluations/runbook/load-built-application.ts": "c4b28985ad98dd1dde5afb8838ba6f9445692d208adde05c22f30f6074365056", + "apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts": "c5bfd41fb727da4fb07a6e2c15c1c66abacad7c9867983cf19377475117d88b1", + "libs/@hashintel/brunch-agent/packages/core/package.json": "e12d9cc79d38c43e5543a5667753e5049dfbf21bec425c25942f1c4cbb922a1f", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "0bd463eaab4eb68b86431bf26e0e01f90c89437b2888c86f4da53d7a0f39ec13", + "libs/@hashintel/brunch-agent/packages/core/src/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json": "dca0db144c530bcf66c5400c029efa5e1136c9d7f81ed174343e891985200c33", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md": "9915751f11baf06de31b237ee8df57b5217f2e7f0531090370a7e08d0282b6c2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "d5bac87a192b92fb6b7d749ec3d58116f9e39527b59e425c60aaeedf5cc13bd0", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts": "a283342146c8d2c2cb8056c588e76c1c3094f1e21e731be251c8fcdb335ed1b2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/instructions.md": "ea3a8755a576e98d35fce6a1e1f928a61148eb713f0bc2c2f818d104eb5f3832", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/profile.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/workpiece-template.md": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/checks.md": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md": "817e43d15848bcec3c720a3e8b1104d31faf3035fd7ae8269d0a97ee43ef4a9a", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/protocol.md": "5f11b043fb0ec18c9c7afba3b729ea1212fe0f88e06220efead48dfd250af562" + }, + "builtArtifactManifest": [ + { + "path": "apps/brunch-agent/dist/app.mjs", + "sha256": "5ce74b2646c00b959b3ee847b47e006717ccc21e1fba769ce818566cba6da62e" + }, + { + "path": "apps/brunch-agent/dist/node-server-BWNSXBme.mjs", + "sha256": "90343929bc5274e0644003e8d7ea69b02003ac4d3b364a0afdb0be0e1032c066" + }, + { + "path": "apps/brunch-agent/dist/server.mjs", + "sha256": "c2d4ccb5aa999e33d33530ba87280e4bb6d6aacd41df630876297bfabea8f04e" + } + ], + "builtArtifactManifestSha256": "5bb67f5cbb1315e789426d83780ed9af23c58b1a04cf14a4b62a831cae435cd0" + }, + "failure": { + "name": "FlueExecutionError", + "message": "Agent submission sub_01M1GXXQMP7D5C77PD149F1ZJD failed: direct(sub_01M1GXXQMP7D5C77PD149F1ZJD) failed: 401 {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"API key is invalid.\"},\"request_id\":null}", + "stack": "FlueExecutionError: Agent submission sub_01M1GXXQMP7D5C77PD149F1ZJD failed: direct(sub_01M1GXXQMP7D5C77PD149F1ZJD) failed: 401 {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"API key is invalid.\"},\"request_id\":null}\n at waitForAgentSubmission (file:///Users/lunelson/.herdr/worktrees/hash/bravo/node_modules/@flue/sdk/dist/index.mjs:1028:11)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async dispatch (file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts:538:5)\n at async file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts:548:3" + }, + "transcript": "## User\nI'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "rawConversationSnapshot": { + "v": 1, + "conversationId": "conv_01M1GXXQMRF5G7XJW11P21BVJR", + "offset": "0000000000000000_0000000000000006", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFHWFhRTVA3RDVDNzdQRDE0OUYxWkpE", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GXXQMP7D5C77PD149F1ZJD", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GXXQXDAQW5JNNR8ZK2CK20", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GXXQMP7D5C77PD149F1ZJD", + "turnId": "turn_01M1GXXQNF4V3611A3GPHX3V0Y", + "parts": [] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1GXXQMP7D5C77PD149F1ZJD", + "outcome": "failed", + "error": { + "name": "FlueError", + "message": "direct(sub_01M1GXXQMP7D5C77PD149F1ZJD) failed: 401 {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"API key is invalid.\"},\"request_id\":null}", + "type": "operation_failed", + "details": "", + "meta": { + "operation": "direct(sub_01M1GXXQMP7D5C77PD149F1ZJD)", + "reason": "401 {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"API key is invalid.\"},\"request_id\":null}" + } + }, + "answeredBySubmissionId": "sub_01M1GXXQMP7D5C77PD149F1ZJD" + } + ], + "incarnation": "inc_01M1GXXQMP0D9RWF9JFA1N396D" + }, + "rawConversationSnapshotSha256": "c6df4476975908b24412df5c5d4354ad5ab309da882be8a72a16a39f03d26283" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-2-2026-09-02T11-32-08-894Z-7afbec23.failure-79bae3d9.json b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-2-2026-09-02T11-32-08-894Z-7afbec23.failure-79bae3d9.json new file mode 100644 index 00000000000..eea120f0bbe --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-candidate-v2/prospective-runbook-v2-replication-2-2026-09-02T11-32-08-894Z-7afbec23.failure-79bae3d9.json @@ -0,0 +1,452 @@ +{ + "schemaVersion": 1, + "protocolId": "prospective-runbook-v2", + "outputNamespaceId": "vestera-prospective-candidate-v2", + "campaignFingerprint": "302a264c244bc71adad3d4df344da073f70a3da6b4e2e7536d859b951895907e", + "replication": 2, + "runId": "prospective-runbook-v2-replication-2-2026-09-02T11-32-08-894Z-7afbec23", + "status": "invalid", + "invalidReason": "runtime-failure", + "startedAt": "2026-09-02T11:32:08.894Z", + "failedAt": "2026-09-02T11:34:07.424Z", + "interviewerModel": "claude-sonnet-4-5", + "expertModel": "claude-sonnet-4-5", + "hardStop": 8, + "latencyStopMs": 180000, + "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, not-yet-asked item, declined or deferred item, assumption, conflict, correction, contextual coexistence, omission, and loss.", + "modelCalls": [ + { + "durationMs": 5476, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 273, + "totalTokens": 2759, + "cost": 0.013409999999999998 + }, + { + "durationMs": 7416, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "toolUse", + "providerStopReason": null, + "inputTokens": 12, + "outputTokens": 334, + "totalTokens": 4324, + "cost": 0.011421299999999999 + }, + { + "durationMs": 15869, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 14, + "outputTokens": 362, + "totalTokens": 11082, + "cost": 0.0318954 + }, + { + "durationMs": 25324, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 573, + "totalTokens": 11375, + "cost": 0.0405528 + }, + { + "durationMs": 12755, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 461, + "totalTokens": 11735, + "cost": 0.0119526 + }, + { + "durationMs": 11688, + "providerId": "anthropic", + "providerName": "anthropic", + "api": "anthropic-messages", + "requestedModel": "claude-sonnet-4-5", + "observedModel": null, + "observedModelSource": "unavailable", + "stopReason": "stop", + "providerStopReason": null, + "inputTokens": 10, + "outputTokens": 304, + "totalTokens": 12041, + "cost": 0.00970545 + } + ], + "expertUsage": { + "calls": 4, + "inputTokens": 13458, + "outputTokens": 1101, + "cacheReadTokens": 0, + "cacheWriteTokens": 0 + }, + "expertCalls": [ + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "end_turn" + }, + { + "requestedModel": "claude-sonnet-4-5", + "observedModel": "claude-sonnet-4-5-20250929", + "observedModelSource": "provider-response", + "stopReason": "refusal" + } + ], + "expertMessages": [ + { + "role": "user", + "content": "Good — I understand you need a simulation model that lets you test scheduling decisions before implementing them. I'll interview you to understand how your scheduling operation works, then build a model from that.\n\nLet me start with a concrete recent case: **Walk me through the last time you had to schedule a new batch or order — what arrived or triggered it, what did you look at to decide how to fit it in, and what happened next?**" + }, + { + "role": "assistant", + "content": "*leans back, thinking*\n\nLast real fire was… Friday afternoon, actually. Commercial forwarded a rush order from one of our distributors — needed 800 units of TC-11, that's a mid-blue tint, by Tuesday. Normally that'd be fine, but we were already squeezed.\n\nFirst thing I did was pull up the sheet — I've got the week's runs mapped out by line, with changeovers pencilled in. Line 2 was running whites all weekend, Line 1 had a specialty batch finishing Saturday morning, and Line 3 was on a different tint family. \n\nTC-11 meant I needed a tint-qualified line. Line 3's not signed off for TC-11 yet — quality's still working through the product list — so it was Line 1 or Line 2. Line 2 was mid-run on a big white order for Meridian, and pulling it off early to wash down for tint would've cost me three hours plus scrap, then another washdown back to white. That's dead time I couldn't afford.\n\nSo I looked at Line 1. The specialty was wrapping Saturday around noon. I could squeeze TC-11 in next — it's same tint family as what was queued behind the specialty, so only a quick rinse between them, maybe half an hour. Pushed one of Monday's smaller runs back a few hours, called the distributor, told them Monday afternoon instead of Tuesday morning. They grumbled but took it.\n\nTold the floor at Monday's huddle. Line 1 ran it, came out fine. That's usually how it goes — juggle, squeeze, call somebody." + }, + { + "role": "user", + "content": "That's helpful — you made a real trade: avoided an expensive mid-run washdown on Line 2 by using Line 1's opening and pushing something else. \n\nYou mentioned **tint families** matter for how long a changeover takes. Help me understand that: what makes two products part of the same tint family, and what actually changes about the changeover when you're staying in the same family versus switching between families?" + }, + { + "role": "assistant", + "content": "*nods*\n\nRight, so there's three families: **base whites**, **tinted colours**, and **specialty clears**. It's about what goes into them and what residue they leave.\n\nBase whites are just that — titanium white pigment, no colour tints. Tinted colours have chromatic pigments added at the letdown stage — blues, reds, greens, earth tones, whatever. Specialty clears are thick formulations, different resin system entirely, no pigment at all — they're for industrial gloss coats.\n\nWhen you stay **inside a family**, it's a quick rinse — maybe 20, 30 minutes. You're flushing out one white for another white, or one tint shade for another tint shade. The lines can handle trace carryover, QA doesn't flinch.\n\n**Switching families** is where it gets expensive. If you go white to tint, that's about 45 minutes — you need to get the line properly clean so the white residue doesn't dilute the colour. But tint back to white? That's the killer — full washdown, around three hours. Any pigment carryover into a white batch shows up, and we can't ship it. QA will reject it, or worse, a customer sees streaks. So you scrub everything: mix tank, mill, letdown lines, the whole train.\n\nSpecialty in or out, either direction, is about two hours. Different chemistry, you've got to purge the resin system thoroughly or you get adhesion problems downstream.\n\nThe changeover crew does all of it — same two techs, day shift. If two lines need washing at once, somebody waits." + }, + { + "role": "user", + "content": "Good — so the changeover time isn't symmetric, and tint back to white is the expensive direction you want to avoid. That three-hour washdown is what you dodged by using Line 1 instead of pulling Line 2 off the Meridian white order.\n\nYou mentioned Line 3 wasn't signed off for TC-11 yet. Let me understand **what each line can actually run**: How many production lines do you have total, and what determines which products each line is allowed or qualified to make?" + }, + { + "role": "assistant", + "content": "*counts on fingers*\n\nThree lines total. They're all the same stages — mix, mill, tint-letdown, fill-and-pack — but they're not identical and they're not all qualified for everything.\n\n**Line 1** is the old workhorse. Slower, but it's qualified for the full product list — all the whites, all the tints, all the specialty clears. If there's a fussy product or something new, it goes on Line 1 first.\n\n**Line 2** is the fast line, built for volume. It runs whites beautifully — that's where all the big Meridian orders go. It can run tints too, but we've never piped it for the clear resins, so no specialty. Line 2 is whites and tints only.\n\n**Line 3** is the newest — quick, modern, still being qualified product by product. Quality and engineering are working through the SKU list one at a time, running validation batches. Right now it's cleared for most of the whites, some of the tints, and a couple of the specialty clears. But not all of them yet — TC-11's one of the tint SKUs still on the \"not yet\" list. It's a rolling thing; every few weeks another product gets signed off.\n\nSo when I'm slotting an order, I've got to check what's qualified where. Sometimes that boxes me in — like Friday, TC-11 meant Line 1 or nothing, because Line 2 doesn't do specialty and Line 3 isn't approved yet." + }, + { + "role": "user", + "content": "Understood — so Line 1 is universal but slower, Line 2 is fast but whites and tints only, and Line 3 is still being opened up product by product. That qualification constraint was binding when TC-11 arrived.\n\nLet me understand what actually happens once you've assigned an order to a qualified line. **Take that TC-11 run on Line 1**: after the specialty batch finished Saturday and you did the quick rinse, what happened during the TC-11 run itself — what stages did it go through, roughly how long did it take, and what made the line unavailable for anything else?" + } + ], + "violations": [ + { + "code": "missing-workpiece", + "detail": "No recoverable runbook-ir workpiece was emitted." + } + ], + "wroteCaptureStore": false, + "instrument": { + "sourceCommit": "605e681cebfaeaa3fcdd0502f50ab28adc7ac63d", + "instrumentStatus": "", + "fileSha256": { + "yarn.lock": "3f445d255ef7893c1941f5610e2a813a903d5b9053c9831012c3d7c3875571dc", + "apps/brunch-agent/package.json": "128b3fd6c9624c35b226b91d39e27cf1a9cdfbdafc0314b380b7d51e1de92b46", + "apps/brunch-agent/vite.config.ts": "f1a50233829599dff2d5ef7ee0cd10bdb55d7fe65a52a6e85913e00a33a5300d", + "apps/brunch-agent/src/app.ts": "979efb9172c82df7499b218125735929a9586a0d9355e042e9ff2402955f385e", + "apps/brunch-agent/src/db.ts": "9f3a2598df3ce6737de1348afa8baf7deeeeac51b8202bf87adb76dc96374481", + "apps/brunch-agent/src/agents/chat-agent/agent.ts": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e", + "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744", + "apps/brunch-agent/src/conversation/identity.ts": "b52f764ba6e6bb50602d477eb93234aa6a900c843412551de40ea6ad3050e5e6", + "apps/brunch-agent/src/conversation/transcript.ts": "b96cf8d2b8352d414f674985b7071c9570323ccaf862f7e77ec76311e4b11d44", + "apps/brunch-agent/src/http/routes.ts": "7d8389931c57671ad8376d0a0ab497413013d47b012ffbf531339d0d1ce3a0cd", + "apps/brunch-agent/src/evaluations/runbook/artifacts.ts": "15951ab3705b126d64a2593731b50775a16915298dae87b8cf7c99e265e2c2f4", + "apps/brunch-agent/src/evaluations/runbook/campaign-integrity.ts": "60215179f5d48bacbf4da7b0876d049ed8b15b4b774d99eab092a1297502a3b4", + "apps/brunch-agent/src/evaluations/runbook/load-built-application.ts": "c4b28985ad98dd1dde5afb8838ba6f9445692d208adde05c22f30f6074365056", + "apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts": "c5bfd41fb727da4fb07a6e2c15c1c66abacad7c9867983cf19377475117d88b1", + "libs/@hashintel/brunch-agent/packages/core/package.json": "e12d9cc79d38c43e5543a5667753e5049dfbf21bec425c25942f1c4cbb922a1f", + "libs/@hashintel/brunch-agent/packages/core/src/SYSTEM.md": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10", + "libs/@hashintel/brunch-agent/packages/core/src/agent/index.ts": "0bd463eaab4eb68b86431bf26e0e01f90c89437b2888c86f4da53d7a0f39ec13", + "libs/@hashintel/brunch-agent/packages/core/src/universal-elicitation.md": "a4aedd68317bcde4b98490ea73efa4ee7881e2fe3cbcf3363e3db26e29180716", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json": "dca0db144c530bcf66c5400c029efa5e1136c9d7f81ed174343e891985200c33", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/APPEND_SYSTEM.md": "9915751f11baf06de31b237ee8df57b5217f2e7f0531090370a7e08d0282b6c2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts": "d5bac87a192b92fb6b7d749ec3d58116f9e39527b59e425c60aaeedf5cc13bd0", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts": "a283342146c8d2c2cb8056c588e76c1c3094f1e21e731be251c8fcdb335ed1b2", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/instructions.md": "ea3a8755a576e98d35fce6a1e1f928a61148eb713f0bc2c2f818d104eb5f3832", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/profile.md": "11e5547ea055bb390d7ff96f3469a39a36c84a4ed8a182153965e6d841b5e4bf", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/workpiece-template.md": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/pn-construction.md": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/checks.md": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40", + "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml": "6be9753bcbeb31a4bcb839be1acd4de4a15b1754af25d2cdd0b75fe63dd3d860", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md": "817e43d15848bcec3c720a3e8b1104d31faf3035fd7ae8269d0a97ee43ef4a9a", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md": "a0ab12ceb3fc26d314b3f674ed6728a6bf5a6a24d2325ed32336020a605d96e5", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md": "ddd2620f17c5311a4a186b5bc229ae3f916d811e4aeb35f215ed7502c31fc8f5", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/protocol.md": "5f11b043fb0ec18c9c7afba3b729ea1212fe0f88e06220efead48dfd250af562" + }, + "builtArtifactManifest": [ + { + "path": "apps/brunch-agent/dist/app.mjs", + "sha256": "5ce74b2646c00b959b3ee847b47e006717ccc21e1fba769ce818566cba6da62e" + }, + { + "path": "apps/brunch-agent/dist/node-server-BWNSXBme.mjs", + "sha256": "90343929bc5274e0644003e8d7ea69b02003ac4d3b364a0afdb0be0e1032c066" + }, + { + "path": "apps/brunch-agent/dist/server.mjs", + "sha256": "c2d4ccb5aa999e33d33530ba87280e4bb6d6aacd41df630876297bfabea8f04e" + } + ], + "builtArtifactManifestSha256": "5bb67f5cbb1315e789426d83780ed9af23c58b1a04cf14a4b62a831cae435cd0" + }, + "failure": { + "name": "Error", + "message": "The simulated expert returned no text", + "stack": "Error: The simulated expert returned no text\n at askExpert (file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts:492:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async file:///Users/lunelson/.herdr/worktrees/hash/bravo/apps/brunch-agent/src/evaluations/runbook/prospective-runbook-v2-run.ts:564:25" + }, + "transcript": "## User\nI'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.\n\n## Assistant\nGood — I understand you need a simulation model that lets you test scheduling decisions before implementing them. I'll interview you to understand how your scheduling operation works, then build a model from that.\n\nLet me start with a concrete recent case: **Walk me through the last time you had to schedule a new batch or order — what arrived or triggered it, what did you look at to decide how to fit it in, and what happened next?**\n- tool activate_skill (toolu_01Hu53rTbB2WtXXgzuhhCCV2): \"Run the skill named \\\"sdcpn-modelling\\\".\\n\\n<skill_instructions>\\n# Capability-aware lifecycle\\n\\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\\n\\n## Select the runtime branch\\n\\n### Interactive elicitation, review, or revision\\n\\nInterview in the person's operational vocabulary. Before substantive\\nelicitation or workpiece revision, read\\n`references/universal-elicitation.md` and `references/profile.md`.\\n\\nFor review, first inspect the supplied workpiece or model. If it already answers\\nthe question, answer without reading the elicitation references or interviewing.\\nIf the review exposes a gap that requires human knowledge, read both elicitation\\nreferences and then ask exactly one focused question that resolves the smallest\\nconsequential gap.\\n\\nRead `templates/workpiece.md` only when first creating or materially revising\\nthe workpiece. Do not load the template merely to inspect an existing artifact,\\nanswer a resolvable review question, or frame the first elicitation question.\\nConstruct only when the mounted capabilities actually permit construction in\\nthis conversation.\\n\\n### Construct-only execution\\n\\nUse the supplied workpiece as the complete modelling input. Do not interview.\\nRead `references/pn-construction.md` and `references/checks.md`, then use the\\nmounted construction tools. If a consequential workpiece gap prevents faithful\\nconstruction, report the gap and the smallest question a later interactive\\nelicitation must answer; do not ask it or invent an answer in this conversation.\\n\\n## Procedure\\n\\n### Orient\\n\\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\\n\\n### Elicit or revise\\n\\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\\n\\n### Maintain the workpiece\\n\\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\\n\\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\\n\\n### Construct\\n\\nConstruct only from the current workpiece. Read\\n`references/pn-construction.md` and `references/checks.md` before beginning.\\nUse mounted Petrinaut tools for every net change and inspect the resulting\\ndefinition rather than emitting free-form net JSON. If the required tools are\\nabsent, limit the result to the workpiece and construction-ready notes.\\n\\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\\n\\n### Check and deliver\\n\\nApply `references/checks.md` whenever construction is prepared or attempted.\\nDeliver the current workpiece in every branch. Deliver a net only when the\\nmounted tool path has produced and checked one. State what the result can\\nsupport, what remains open, what was assumed or simplified, and what the target\\nor current tools could not represent.\\n\\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\\n\\n## Resource discipline\\n\\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\\n</skill_instructions>\\n\\nSupporting skill resources are available but are not loaded into context unless needed:\\n<skill_resources>\\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\\n</skill_resources>\"\n- tool read_skill_resource (toolu_01MjR5Fm6uufmdpFYgYYhumN): \"# Universal Elicitation Reference\\n\\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\\n\\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\\n\\n## Directives\\n\\n### Work from purpose\\n\\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\\n\\n### Accumulate posture conversationally\\n\\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\\n\\n### Follow the person's account\\n\\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\\n\\n### Protect interaction bandwidth\\n\\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\\n\\n### Preserve authorship and uncertainty\\n\\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\\n\\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\\n\\n### Treat divergence as information\\n\\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\\n\\n### Maintain a recoverable workpiece\\n\\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\\n\\n### Stop honestly\\n\\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\\n\\n## Recognition\\n\\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\\n\\n### Vague or compressed language\\n\\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\\n\\n### Normative language\\n\\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\\n\\n### Tension within or between accounts\\n\\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\\n\\n### Unexplained terms and artifacts\\n\\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\\n\\n### Burden, impatience, or limited availability\\n\\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\\n\\n### Diminishing yield\\n\\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\\n\\n### Assent without independent wording\\n\\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\\n\\n### Silence and absence\\n\\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\\n\\n## Operations\\n\\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\\n\\n### Select the smallest consequential absence\\n\\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\\n\\n### Slice a concrete case\\n\\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\\n\\n### Sweep one property\\n\\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\\n\\n### Ask for the last occurrence\\n\\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\\n\\n### Ask for the basis\\n\\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\\n\\n### Ground a term or artifact\\n\\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\\n\\n### Clarify until observable\\n\\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\\n\\n### Use contrastive cases\\n\\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\\n\\n### Investigate quantities relative to purpose\\n\\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\\n\\n### Turn an unknown into a decision threshold\\n\\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\\n\\n### State a contradiction without resolving it\\n\\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\\n\\n### Restate for correction\\n\\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\\n\\n### Propose structure for correction\\n\\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\\n\\n### Deposit and defer\\n\\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\\n\\n### Press without trapping\\n\\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\\n\\n### Explore a rare or severe outcome\\n\\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\\n\\n### Seek a witness or counterexample\\n\\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\\n\\n### Trade concrete outcomes\\n\\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\\n\\n### Close with one correction opportunity\\n\\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\\n\\n## Coverage\\n\\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\\n\\nA workpiece may need to preserve:\\n\\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\\n- a cold-readable account in the person's vocabulary, including consequential local terms;\\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\\n- decisions, alternatives, and reasons distinctions among them matter;\\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\\n- exact evidence where later interpretation depends on the person's wording;\\n- normalized accounts and agent inferences without laundering their authorship;\\n- assumptions with why they were introduced and how they could be checked;\\n- unknown, not-yet-asked, declined, and deferred material without conflation;\\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\\n- open questions with the consequence of leaving them open and the condition for returning.\\n\\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\\n\\n## Verification\\n\\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\\n\\n### Before asking\\n\\n- The question serves the stated purpose or resolves an active uncertainty.\\n- It follows the person's thread rather than the order of a schema or template.\\n- It asks one focused thing, or a small set that genuinely shares one frame.\\n- A proposed answer, category, number, or distinction is identified as yours.\\n\\n### Before recording\\n\\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\\n- Assent to your wording has not been presented as independently originated evidence.\\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\\n\\n### Before closing\\n\\n- The result's purpose and consequential account have been offered for correction.\\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\\n- The person has not been kept in an irrelevant thread merely to fill a category.\\n- A stop produces a useful partial result rather than a false claim of completion.\\n\\n### Failure signals and repairs\\n\\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\\n\"\n- tool read_skill_resource (toolu_011E14VjLKmNdTu2F4eVeyTo): \"# Operational-Process and SDCPN Elicitation\\n\\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\\n\\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\\n\\n## Directives\\n\\n### Build the operational account the purpose needs\\n\\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\\n\\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\\n\\n### Keep target structure backstage\\n\\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\\n\\n### Preserve operational context\\n\\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\\n\\n### Treat operational patterns as hypotheses\\n\\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\\n\\n## Recognition\\n\\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\\n\\n### Language and account signals\\n\\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\\n\\n### Operational situation patterns\\n\\n#### Timed work\\n\\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\\n\\n#### Conditional or probabilistic outcome\\n\\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\\n\\n#### Contended resource\\n\\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\\n\\n#### Consumed, reserved, or read input\\n\\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\\n\\n#### Gate, release, trigger, or prerequisite\\n\\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\\n\\n#### Continuous quantity and threshold\\n\\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\\n\\n#### Mode change\\n\\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\\n\\n#### Batch, lot, load, or grouped movement\\n\\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\\n\\n#### Spatial transfer\\n\\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\\n\\n#### Event, failure, retry, and recovery\\n\\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\\n\\n#### Policy under pressure\\n\\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\\n\\n#### Hidden waiting\\n\\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\\n\\n## Operations\\n\\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\\n\\n### Choose the case unit before slicing\\n\\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\\n\\n### Link the slice to the objective\\n\\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\\n\\n### Expose the process spine\\n\\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\\n\\n### Sweep operational concerns, not headings\\n\\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\\n\\n### Distinguish consumed, reserved, and read inputs\\n\\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\\n\\n### Sweep what can befall an activity\\n\\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\\n\\n### Test practiced policy with a borderline case\\n\\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\\n\\n### Close a resource account\\n\\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\\n\\n### Close a mode change in both directions\\n\\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\\n\\n### Turn waiting into a causal question\\n\\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\\n\\n### Ask what is conserved\\n\\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\\n\\n### Establish retry scope\\n\\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\\n\\n### Establish validation from observable behavior\\n\\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\\n\\n## Coverage\\n\\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\\n\\n### Purpose, goals, measures, constraints, and thresholds\\n\\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\\n\\n### Process boundary, triggers, prerequisites, and initial conditions\\n\\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\\n\\n### Participants, locations, flowing things, and resources\\n\\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\\n\\n### Activities, inputs, outputs, and resource use\\n\\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\\n\\n### Flow, branching, joining, failure, retry, and recovery\\n\\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\\n\\n### Time, quantities, arrivals, and stochastic behavior\\n\\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\\n\\n### Policies, exceptions, practiced rules, and contextual regimes\\n\\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\\n\\n### Validation, evidence sources, and data bindings\\n\\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\\n\\n### Things not independently elicited as target nodes\\n\\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\\n\\n## Verification\\n\\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\\n\\n### Purpose and process\\n\\n- At least one simulation question, comparison, or decision is stated in the person's terms.\\n- Every objective depends on recorded process material or remains visibly unsupported.\\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\\n\\n### Operational semantics\\n\\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\\n- Hidden waiting has not silently become an activity or unexplained queue.\\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\\n\\n### Quantities and context\\n\\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\\n\\n### Failure signals and repairs\\n\\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\\n\"\n\n## User\n*leans back, thinking*\n\nLast real fire was… Friday afternoon, actually. Commercial forwarded a rush order from one of our distributors — needed 800 units of TC-11, that's a mid-blue tint, by Tuesday. Normally that'd be fine, but we were already squeezed.\n\nFirst thing I did was pull up the sheet — I've got the week's runs mapped out by line, with changeovers pencilled in. Line 2 was running whites all weekend, Line 1 had a specialty batch finishing Saturday morning, and Line 3 was on a different tint family. \n\nTC-11 meant I needed a tint-qualified line. Line 3's not signed off for TC-11 yet — quality's still working through the product list — so it was Line 1 or Line 2. Line 2 was mid-run on a big white order for Meridian, and pulling it off early to wash down for tint would've cost me three hours plus scrap, then another washdown back to white. That's dead time I couldn't afford.\n\nSo I looked at Line 1. The specialty was wrapping Saturday around noon. I could squeeze TC-11 in next — it's same tint family as what was queued behind the specialty, so only a quick rinse between them, maybe half an hour. Pushed one of Monday's smaller runs back a few hours, called the distributor, told them Monday afternoon instead of Tuesday morning. They grumbled but took it.\n\nTold the floor at Monday's huddle. Line 1 ran it, came out fine. That's usually how it goes — juggle, squeeze, call somebody.\n\n## Assistant\nThat's helpful — you made a real trade: avoided an expensive mid-run washdown on Line 2 by using Line 1's opening and pushing something else. \n\nYou mentioned **tint families** matter for how long a changeover takes. Help me understand that: what makes two products part of the same tint family, and what actually changes about the changeover when you're staying in the same family versus switching between families?\n\n## User\n*nods*\n\nRight, so there's three families: **base whites**, **tinted colours**, and **specialty clears**. It's about what goes into them and what residue they leave.\n\nBase whites are just that — titanium white pigment, no colour tints. Tinted colours have chromatic pigments added at the letdown stage — blues, reds, greens, earth tones, whatever. Specialty clears are thick formulations, different resin system entirely, no pigment at all — they're for industrial gloss coats.\n\nWhen you stay **inside a family**, it's a quick rinse — maybe 20, 30 minutes. You're flushing out one white for another white, or one tint shade for another tint shade. The lines can handle trace carryover, QA doesn't flinch.\n\n**Switching families** is where it gets expensive. If you go white to tint, that's about 45 minutes — you need to get the line properly clean so the white residue doesn't dilute the colour. But tint back to white? That's the killer — full washdown, around three hours. Any pigment carryover into a white batch shows up, and we can't ship it. QA will reject it, or worse, a customer sees streaks. So you scrub everything: mix tank, mill, letdown lines, the whole train.\n\nSpecialty in or out, either direction, is about two hours. Different chemistry, you've got to purge the resin system thoroughly or you get adhesion problems downstream.\n\nThe changeover crew does all of it — same two techs, day shift. If two lines need washing at once, somebody waits.\n\n## Assistant\nGood — so the changeover time isn't symmetric, and tint back to white is the expensive direction you want to avoid. That three-hour washdown is what you dodged by using Line 1 instead of pulling Line 2 off the Meridian white order.\n\nYou mentioned Line 3 wasn't signed off for TC-11 yet. Let me understand **what each line can actually run**: How many production lines do you have total, and what determines which products each line is allowed or qualified to make?\n\n## User\n*counts on fingers*\n\nThree lines total. They're all the same stages — mix, mill, tint-letdown, fill-and-pack — but they're not identical and they're not all qualified for everything.\n\n**Line 1** is the old workhorse. Slower, but it's qualified for the full product list — all the whites, all the tints, all the specialty clears. If there's a fussy product or something new, it goes on Line 1 first.\n\n**Line 2** is the fast line, built for volume. It runs whites beautifully — that's where all the big Meridian orders go. It can run tints too, but we've never piped it for the clear resins, so no specialty. Line 2 is whites and tints only.\n\n**Line 3** is the newest — quick, modern, still being qualified product by product. Quality and engineering are working through the SKU list one at a time, running validation batches. Right now it's cleared for most of the whites, some of the tints, and a couple of the specialty clears. But not all of them yet — TC-11's one of the tint SKUs still on the \"not yet\" list. It's a rolling thing; every few weeks another product gets signed off.\n\nSo when I'm slotting an order, I've got to check what's qualified where. Sometimes that boxes me in — like Friday, TC-11 meant Line 1 or nothing, because Line 2 doesn't do specialty and Line 3 isn't approved yet.\n\n## Assistant\nUnderstood — so Line 1 is universal but slower, Line 2 is fast but whites and tints only, and Line 3 is still being opened up product by product. That qualification constraint was binding when TC-11 arrived.\n\nLet me understand what actually happens once you've assigned an order to a qualified line. **Take that TC-11 run on Line 1**: after the specialty batch finished Saturday and you did the quick rinse, what happened during the TC-11 run itself — what stages did it go through, roughly how long did it take, and what made the line unavailable for anything else?", + "rawConversationSnapshot": { + "v": 1, + "conversationId": "conv_01M1GY81MZBEJM8R1G89M214CC", + "offset": "0000000000000000_0000000000000097", + "messages": [ + { + "id": "entry_direct_c3ViXzAxTTFHWTgxTVhRSEVXU1FGSzVROEhGR0JW", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GY81MXQHEWSQFK5Q8HFGBV", + "parts": [ + { + "type": "text", + "text": "I'm the master scheduler at a coatings plant. My boss wants a simulation model of how we\nschedule production, so we can test scheduling decisions before making them. We have tooling\nthat runs Petri-net-style process models, so the end product should be a model I can hand to\nthat — but I can't tell you much about the format, I'm not the modelling person.\n\nPlease interview me about how our operation works, and then produce the model.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GY82YBK21BMQ2JTT3HXHT0", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GY81MXQHEWSQFK5Q8HFGBV", + "turnId": "turn_01M1GY81NP21YW0WJZG3BJ7GCX", + "parts": [ + { + "type": "reasoning", + "text": "The person is a master scheduler at a coatings plant who needs a simulation model of their production scheduling process. They mention:\n1. Purpose: Test scheduling decisions before implementing them\n2. Target format: Petri-net-style process models (their tooling runs these)\n3. They're not the modeling person, so won't know technical details about the format\n4. They want me to interview them and produce the model\n\nThis clearly matches the \"sdcpn-modelling\" skill description: \"Elicit, review, or revise an operational process model, maintain its recoverable workpiece, and construct an inspected SDCPN when Petrinaut capabilities are available.\"\n\nBefore I begin substantive elicitation, I should activate the skill as instructed: \"Activate the `sdcpn-modelling` skill before substantive elicitation, review, workpiece revision, or construction.\"\n\nLet me activate that skill first.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "activate_skill", + "toolCallId": "toolu_01Hu53rTbB2WtXXgzuhhCCV2", + "state": "output-available", + "input": { + "name": "sdcpn-modelling" + }, + "output": "Run the skill named \"sdcpn-modelling\".\n\n<skill_instructions>\n# Capability-aware lifecycle\n\nUse one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred.\n\n## Select the runtime branch\n\n### Interactive elicitation, review, or revision\n\nInterview in the person's operational vocabulary. Before substantive\nelicitation or workpiece revision, read\n`references/universal-elicitation.md` and `references/profile.md`.\n\nFor review, first inspect the supplied workpiece or model. If it already answers\nthe question, answer without reading the elicitation references or interviewing.\nIf the review exposes a gap that requires human knowledge, read both elicitation\nreferences and then ask exactly one focused question that resolves the smallest\nconsequential gap.\n\nRead `templates/workpiece.md` only when first creating or materially revising\nthe workpiece. Do not load the template merely to inspect an existing artifact,\nanswer a resolvable review question, or frame the first elicitation question.\nConstruct only when the mounted capabilities actually permit construction in\nthis conversation.\n\n### Construct-only execution\n\nUse the supplied workpiece as the complete modelling input. Do not interview.\nRead `references/pn-construction.md` and `references/checks.md`, then use the\nmounted construction tools. If a consequential workpiece gap prevents faithful\nconstruction, report the gap and the smallest question a later interactive\nelicitation must answer; do not ask it or invent an answer in this conversation.\n\n## Procedure\n\n### Orient\n\nEstablish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins.\n\n### Elicit or revise\n\nFor a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the two elicitation references for detailed operations and coverage; do not turn their register order into question order.\n\n### Maintain the workpiece\n\nTreat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it.\n\nWhenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece.\n\n### Construct\n\nConstruct only from the current workpiece. Read\n`references/pn-construction.md` and `references/checks.md` before beginning.\nUse mounted Petrinaut tools for every net change and inspect the resulting\ndefinition rather than emitting free-form net JSON. If the required tools are\nabsent, limit the result to the workpiece and construction-ready notes.\n\nConstruction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece.\n\n### Check and deliver\n\nApply `references/checks.md` whenever construction is prepared or attempted.\nDeliver the current workpiece in every branch. Deliver a net only when the\nmounted tool path has produced and checked one. State what the result can\nsupport, what remains open, what was assumed or simplified, and what the target\nor current tools could not represent.\n\nAn explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview.\n\n## Resource discipline\n\nRead resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions.\n</skill_instructions>\n\nSupporting skill resources are available but are not loaded into context unless needed:\n<skill_resources>\n- references/checks.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/checks.md\n- references/pn-construction.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/pn-construction.md\n- references/profile.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md\n- references/universal-elicitation.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md\n- templates/workpiece.md → read_skill_resource /.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/templates/workpiece.md\n</skill_resources>", + "durationMs": 4 + }, + { + "type": "reasoning", + "text": "Good, I've activated the skill. Now I need to proceed with the skill instructions:\n\n1. **Select the runtime branch**: This is \"Interactive elicitation, review, or revision\" - they want me to interview them and produce the model.\n\n2. **Before substantive elicitation**, I need to read:\n - `references/universal-elicitation.md`\n - `references/profile.md`\n\n3. I should read `templates/workpiece.md` only when first creating or materially revising the workpiece.\n\n4. **Orient**: I should establish purpose and context first.\n\nLet me read the two elicitation references before I begin interviewing.", + "state": "done" + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_01MjR5Fm6uufmdpFYgYYhumN", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/universal-elicitation.md" + }, + "output": "# Universal Elicitation Reference\n\nThis reference contains detailed elicitation and revision guidance that applies across domains and target formalisms. A plugin may add context or narrower guidance under the same registers. Where it is silent, this reference still applies.\n\nThe registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece.\n\n## Directives\n\n### Work from purpose\n\nEstablish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it.\n\n### Accumulate posture conversationally\n\nLearn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form.\n\n### Follow the person's account\n\nUse the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account.\n\n### Protect interaction bandwidth\n\nDo not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time.\n\n### Preserve authorship and uncertainty\n\nKeep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not.\n\nPreserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result.\n\n### Treat divergence as information\n\nDo not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes.\n\n### Maintain a recoverable workpiece\n\nRecord useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached.\n\n### Stop honestly\n\nCompletion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible.\n\n## Recognition\n\nRecognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question.\n\n### Vague or compressed language\n\nWords such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it.\n\n### Normative language\n\n“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists.\n\n### Tension within or between accounts\n\nAn answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood.\n\n### Unexplained terms and artifacts\n\nLocal terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading.\n\n### Burden, impatience, or limited availability\n\nThese are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly.\n\n### Diminishing yield\n\nSeveral turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question.\n\n### Assent without independent wording\n\nQuick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content.\n\n### Silence and absence\n\nMaterial may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds.\n\n## Operations\n\nChoose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule.\n\n### Select the smallest consequential absence\n\nCompare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it.\n\n### Slice a concrete case\n\nWalk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced.\n\n### Sweep one property\n\nAfter a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category.\n\n### Ask for the last occurrence\n\nWhen a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons.\n\n### Ask for the basis\n\nAsk how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement.\n\n### Ground a term or artifact\n\nAsk for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts.\n\n### Clarify until observable\n\nClarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe.\n\n### Use contrastive cases\n\nWhen ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies.\n\n### Investigate quantities relative to purpose\n\nBefore deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency.\n\n### Turn an unknown into a decision threshold\n\nWhen an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose.\n\n### State a contradiction without resolving it\n\nPut the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them.\n\n### Restate for correction\n\nOffer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept.\n\n### Propose structure for correction\n\nWhen low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it.\n\n### Deposit and defer\n\nWhen an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit.\n\n### Press without trapping\n\nWhen time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed.\n\n### Explore a rare or severe outcome\n\nUse a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment.\n\n### Seek a witness or counterexample\n\nAsk for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule.\n\n### Trade concrete outcomes\n\nWhen a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate.\n\n### Close with one correction opportunity\n\nBefore a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage.\n\n## Coverage\n\nCoverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model.\n\nA workpiece may need to preserve:\n\n- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation;\n- a cold-readable account in the person's vocabulary, including consequential local terms;\n- requirements, constraints, invariants, or safety conditions the purpose depends on;\n- decisions, alternatives, and reasons distinctions among them matter;\n- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes;\n- exact evidence where later interpretation depends on the person's wording;\n- normalized accounts and agent inferences without laundering their authorship;\n- assumptions with why they were introduced and how they could be checked;\n- unknown, not-yet-asked, declined, and deferred material without conflation;\n- ambiguity, unresolved conflict, correction history, and contextual coexistence;\n- deliberate omissions, simplifications, defaults, and target-representation losses; and\n- open questions with the consequence of leaving them open and the condition for returning.\n\nCoverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible.\n\n## Verification\n\nVerification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence.\n\n### Before asking\n\n- The question serves the stated purpose or resolves an active uncertainty.\n- It follows the person's thread rather than the order of a schema or template.\n- It asks one focused thing, or a small set that genuinely shares one frame.\n- A proposed answer, category, number, or distinction is identified as yours.\n\n### Before recording\n\n- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default.\n- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule.\n- Assent to your wording has not been presented as independently originated evidence.\n- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account.\n\n### Before closing\n\n- The result's purpose and consequential account have been offered for correction.\n- Remaining gaps are stated in terms of what they prevent the result from supporting.\n- The person has not been kept in an irrelevant thread merely to fill a category.\n- A stop produces a useful partial result rather than a false claim of completion.\n\n### Failure signals and repairs\n\n- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker.\n- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty.\n- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption.\n- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it.\n- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire.\n- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps.\n- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition.\n- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close.\n", + "durationMs": 2 + }, + { + "type": "dynamic-tool", + "toolName": "read_skill_resource", + "toolCallId": "toolu_011E14VjLKmNdTu2F4eVeyTo", + "state": "output-available", + "input": { + "path": "/.flue/packaged-skills/skill%3Asdcpn-modelling%3A4236dc3c0e6a2446/references/profile.md" + }, + "output": "# Operational-Process and SDCPN Elicitation\n\nThis reference adds operational-process and SDCPN-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance.\n\nThe registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary.\n\n## Directives\n\n### Build the operational account the purpose needs\n\nFor the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use.\n\nEvery objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure.\n\n### Keep target structure backstage\n\nAsk about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions.\n\n### Preserve operational context\n\nA value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter.\n\n### Treat operational patterns as hypotheses\n\nRecurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary.\n\n## Recognition\n\nRecognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread.\n\n### Language and account signals\n\n- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource.\n- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input.\n- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception.\n- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change.\n- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone.\n- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context.\n- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity.\n\n### Operational situation patterns\n\n#### Timed work\n\nAn activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability.\n\n#### Conditional or probabilistic outcome\n\nAn activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows.\n\n#### Contended resource\n\nSeveral activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return.\n\n#### Consumed, reserved, or read input\n\nAn activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities.\n\n#### Gate, release, trigger, or prerequisite\n\nWork becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it.\n\n#### Continuous quantity and threshold\n\nA level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior.\n\n#### Mode change\n\nSetup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity.\n\n#### Batch, lot, load, or grouped movement\n\nWork moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group.\n\n#### Spatial transfer\n\nA change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary.\n\n#### Event, failure, retry, and recovery\n\nA disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome.\n\n#### Policy under pressure\n\nMore than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule.\n\n#### Hidden waiting\n\nA gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node.\n\n## Operations\n\nUse the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns.\n\n### Choose the case unit before slicing\n\nWhen several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff.\n\n### Link the slice to the objective\n\nAs the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail.\n\n### Expose the process spine\n\nAsk what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine.\n\n### Sweep operational concerns, not headings\n\nAfter a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality.\n\n### Distinguish consumed, reserved, and read inputs\n\nFor each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns.\n\n### Sweep what can befall an activity\n\nAcross the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery.\n\n### Test practiced policy with a borderline case\n\nWhen a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule.\n\n### Close a resource account\n\nFor a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability.\n\n### Close a mode change in both directions\n\nAsk whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does.\n\n### Turn waiting into a causal question\n\nAsk what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity.\n\n### Ask what is conserved\n\nWhen quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it.\n\n### Establish retry scope\n\nAsk whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset.\n\n### Establish validation from observable behavior\n\nAsk what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests.\n\n## Coverage\n\nCoverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories.\n\n### Purpose, goals, measures, constraints, and thresholds\n\nPreserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights.\n\n### Process boundary, triggers, prerequisites, and initial conditions\n\nPreserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential.\n\n### Participants, locations, flowing things, and resources\n\nPreserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior.\n\n### Activities, inputs, outputs, and resource use\n\nFor each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation.\n\n### Flow, branching, joining, failure, retry, and recovery\n\nPreserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes.\n\n### Time, quantities, arrivals, and stochastic behavior\n\nPreserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports.\n\n### Policies, exceptions, practiced rules, and contextual regimes\n\nPreserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds.\n\n### Validation, evidence sources, and data bindings\n\nPreserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable.\n\n### Things not independently elicited as target nodes\n\n- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure.\n- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time.\n- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape.\n- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place.\n\n## Verification\n\nApply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`.\n\n### Purpose and process\n\n- At least one simulation question, comparison, or decision is stated in the person's terms.\n- Every objective depends on recorded process material or remains visibly unsupported.\n- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff.\n\n### Operational semantics\n\n- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available.\n- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required.\n- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential.\n- Hidden waiting has not silently become an activity or unexplained queue.\n- Mode-change and spatial-transfer effects preserve direction and context where they differ.\n\n### Quantities and context\n\n- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it.\n- A point value stands only where constancy or a purpose-relative simplification is supported and named.\n- Prescribed and practiced regimes or contextual variants have not been averaged into one false value.\n\n### Failure signals and repairs\n\n- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it.\n- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question.\n- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage.\n- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation.\n- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context.\n- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies.\n- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences.\n", + "durationMs": 2 + }, + { + "type": "reasoning", + "text": "Good, now I have the elicitation guidance. Let me now start the interview. According to the skill instructions:\n\n1. **Orient**: I should establish purpose and context first - the intended question/decision, audience, boundary, horizon, accuracy need, and available time.\n\n2. **Use the person's vocabulary** and follow concrete cases rather than abstract tours.\n\n3. **Do not open with a battery of independent questions** - ask one coherent, answerable opening question.\n\n4. **Accumulate posture conversationally** - don't present an intake form.\n\nFrom their opening statement, I know:\n- **Purpose**: Test scheduling decisions before making them\n- **Audience**: Presumably the boss and master scheduler (them)\n- **They are the master scheduler** at a coatings plant\n- **End product**: Petri-net-style process model\n\nI should start with one focused question that helps me understand their operation better. A good opening would be to follow one concrete case - perhaps asking them about a recent or typical scheduling situation that would help expose the structure of what they schedule, how they schedule it, and what decisions matter.\n\nLet me craft a single opening question that's concrete and answerable.", + "state": "done" + }, + { + "type": "text", + "text": "Good — I understand you need a simulation model that lets you test scheduling decisions before implementing them. I'll interview you to understand how your scheduling operation works, then build a model from that.\n\nLet me start with a concrete recent case: **Walk me through the last time you had to schedule a new batch or order — what arrived or triggered it, what did you look at to decide how to fit it in, and what happened next?**", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWTlDQUVWUDNDVlRDNVo2WDM0RzhY", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GY9CAEVP3CVTC5Z6X34G8X", + "parts": [ + { + "type": "text", + "text": "*leans back, thinking*\n\nLast real fire was… Friday afternoon, actually. Commercial forwarded a rush order from one of our distributors — needed 800 units of TC-11, that's a mid-blue tint, by Tuesday. Normally that'd be fine, but we were already squeezed.\n\nFirst thing I did was pull up the sheet — I've got the week's runs mapped out by line, with changeovers pencilled in. Line 2 was running whites all weekend, Line 1 had a specialty batch finishing Saturday morning, and Line 3 was on a different tint family. \n\nTC-11 meant I needed a tint-qualified line. Line 3's not signed off for TC-11 yet — quality's still working through the product list — so it was Line 1 or Line 2. Line 2 was mid-run on a big white order for Meridian, and pulling it off early to wash down for tint would've cost me three hours plus scrap, then another washdown back to white. That's dead time I couldn't afford.\n\nSo I looked at Line 1. The specialty was wrapping Saturday around noon. I could squeeze TC-11 in next — it's same tint family as what was queued behind the specialty, so only a quick rinse between them, maybe half an hour. Pushed one of Monday's smaller runs back a few hours, called the distributor, told them Monday afternoon instead of Tuesday morning. They grumbled but took it.\n\nTold the floor at Monday's huddle. Line 1 ran it, came out fine. That's usually how it goes — juggle, squeeze, call somebody.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GY9EKN8B2XW83Q0F6Q333A", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GY9CAEVP3CVTC5Z6X34G8X", + "turnId": "turn_01M1GY9CAM2FNGX0HT4DH38NTR", + "parts": [ + { + "type": "reasoning", + "text": "This is a rich concrete case. Let me capture the key operational elements:\n\n1. **Trigger**: Rush order from distributor (external, unplanned)\n2. **Decision process**: Master scheduler looks at current schedule, evaluates options based on:\n - Line availability\n - Product qualifications (Line 3 not qualified for TC-11)\n - Changeover/washdown costs (time + scrap)\n - Tint family compatibility (affects changeover duration)\n - Impact on existing commitments\n\n3. **Resources**: Multiple lines (1, 2, 3) with:\n - Different states (running, finishing)\n - Different qualifications\n - Different current products/families\n\n4. **Activities**: Production runs, changeovers/washdowns with varying durations\n5. **Constraints**: Product-line qualifications, tint family compatibility\n6. **Time-based decisions**: Weighing changeover costs vs. schedule displacement\n\nKey operational vocabulary:\n- Lines (numbered)\n- Runs/batches\n- Tint families\n- Changeover, washdown, rinse (different types/durations)\n- Scrap (associated with early pull-off)\n- Specialty vs. standard products\n- Product qualification\n\nI should follow up on aspects that connect to the simulation objective. The boss wants to test scheduling decisions - so I need to understand:\n- What makes a schedule \"better\" or \"worse\"?\n- What are the typical decision points?\n- What varies (arrival patterns, durations, line states)?\n- What are the practiced rules vs. documented rules?\n\nGood next questions could explore:\n- The tint family concept and its impact on changeovers\n- Line qualifications and how they constrain choices\n- Changeover types and their costs (time, scrap)\n- What normally triggers scheduling decisions\n- How outcomes are judged (what makes a good schedule?)\n\nI should stay in operational vocabulary and follow this thread rather than jumping to a different topic. \n\nThe tint family and changeover mechanics seem central to how the scheduler actually decides between lines, so that's worth pushing on next.", + "state": "done" + }, + { + "type": "text", + "text": "That's helpful — you made a real trade: avoided an expensive mid-run washdown on Line 2 by using Line 1's opening and pushing something else. \n\nYou mentioned **tint families** matter for how long a changeover takes. Help me understand that: what makes two products part of the same tint family, and what actually changes about the changeover when you're staying in the same family versus switching between families?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWUFIU0pEUlcyODg3R0EwRzlGRDcz", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYAHSJDRW2887GA0G9FD73", + "parts": [ + { + "type": "text", + "text": "*nods*\n\nRight, so there's three families: **base whites**, **tinted colours**, and **specialty clears**. It's about what goes into them and what residue they leave.\n\nBase whites are just that — titanium white pigment, no colour tints. Tinted colours have chromatic pigments added at the letdown stage — blues, reds, greens, earth tones, whatever. Specialty clears are thick formulations, different resin system entirely, no pigment at all — they're for industrial gloss coats.\n\nWhen you stay **inside a family**, it's a quick rinse — maybe 20, 30 minutes. You're flushing out one white for another white, or one tint shade for another tint shade. The lines can handle trace carryover, QA doesn't flinch.\n\n**Switching families** is where it gets expensive. If you go white to tint, that's about 45 minutes — you need to get the line properly clean so the white residue doesn't dilute the colour. But tint back to white? That's the killer — full washdown, around three hours. Any pigment carryover into a white batch shows up, and we can't ship it. QA will reject it, or worse, a customer sees streaks. So you scrub everything: mix tank, mill, letdown lines, the whole train.\n\nSpecialty in or out, either direction, is about two hours. Different chemistry, you've got to purge the resin system thoroughly or you get adhesion problems downstream.\n\nThe changeover crew does all of it — same two techs, day shift. If two lines need washing at once, somebody waits.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYAK9TF5QEYQE8SQVVXXQA", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYAHSJDRW2887GA0G9FD73", + "turnId": "turn_01M1GYAHSNQSJ0QSXT0KR0NGTA", + "parts": [ + { + "type": "reasoning", + "text": "Okay, that's a very useful distinction. Let me capture what I learned:\n\n1. Three product families:\n - Base whites (titanium white, no color)\n - Tinted colours (chromatic pigments)\n - Specialty clears (different resin, no pigment, industrial gloss)\n\n2. Changeover duration depends on direction and families:\n - Within same family: 20-30 min (quick rinse)\n - White → Tint: 45 min\n - Tint → White: 3 hours (full washdown, quality concern about pigment in white)\n - Specialty ↔ anything: 2 hours either direction (different chemistry)\n\n3. Changeover is performed by a dedicated crew: 2 techs, day shift only\n - This is a contended resource! If two lines need changeover simultaneously, one waits.\n\nImportant operational details:\n- Quality concerns drive the asymmetry (tint→white is expensive because contamination shows)\n- The changeover resource is limited and shared\n\nNow I should understand more about:\n- The lines themselves (how many, what can each do?)\n- What happens during production runs (duration, batch sizes, what determines them?)\n- What are the typical queued orders like?\n- What constraints exist around scheduling?\n\nLet me ask about the lines and their capabilities next, since Line 3 was mentioned as \"not signed off for TC-11 yet\" — there seems to be product qualification per line.", + "state": "done" + }, + { + "type": "text", + "text": "Good — so the changeover time isn't symmetric, and tint back to white is the expensive direction you want to avoid. That three-hour washdown is what you dodged by using Line 1 instead of pulling Line 2 off the Meridian white order.\n\nYou mentioned Line 3 wasn't signed off for TC-11 yet. Let me understand **what each line can actually run**: How many production lines do you have total, and what determines which products each line is allowed or qualified to make?", + "state": "done" + } + ] + }, + { + "id": "entry_direct_c3ViXzAxTTFHWUI4SDNEREdKRE44NEYzVDM5UUM1", + "role": "user", + "purpose": "user", + "display": "visible", + "submissionId": "sub_01M1GYB8H3DDGJDN84F3T39QC5", + "parts": [ + { + "type": "text", + "text": "*counts on fingers*\n\nThree lines total. They're all the same stages — mix, mill, tint-letdown, fill-and-pack — but they're not identical and they're not all qualified for everything.\n\n**Line 1** is the old workhorse. Slower, but it's qualified for the full product list — all the whites, all the tints, all the specialty clears. If there's a fussy product or something new, it goes on Line 1 first.\n\n**Line 2** is the fast line, built for volume. It runs whites beautifully — that's where all the big Meridian orders go. It can run tints too, but we've never piped it for the clear resins, so no specialty. Line 2 is whites and tints only.\n\n**Line 3** is the newest — quick, modern, still being qualified product by product. Quality and engineering are working through the SKU list one at a time, running validation batches. Right now it's cleared for most of the whites, some of the tints, and a couple of the specialty clears. But not all of them yet — TC-11's one of the tint SKUs still on the \"not yet\" list. It's a rolling thing; every few weeks another product gets signed off.\n\nSo when I'm slotting an order, I've got to check what's qualified where. Sometimes that boxes me in — like Friday, TC-11 meant Line 1 or nothing, because Line 2 doesn't do specialty and Line 3 isn't approved yet.", + "state": "done" + } + ] + }, + { + "id": "entry_01M1GYBBQ6CZR2NG610QQR1C71", + "role": "assistant", + "purpose": "assistant", + "display": "visible", + "submissionId": "sub_01M1GYB8H3DDGJDN84F3T39QC5", + "turnId": "turn_01M1GYB8H88SEDK5NC51NPVVXD", + "parts": [ + { + "type": "reasoning", + "text": "Good, so we have:\n- 3 lines with different capabilities\n- Line 1: full qualification (whites, tints, specialty)\n- Line 2: whites and tints only (no specialty)\n- Line 3: partial and evolving qualification - some whites, some tints, some specialty; TC-11 specifically not yet qualified\n\nLine qualification is a hard constraint on assignment.\n\nNow I want to understand the flow of work through a line. I should understand:\n- What actually happens when a batch runs on a line\n- How long it takes (and does that vary?)\n- What resources are involved\n- When the line is unavailable/occupied\n\nLet me ask about the production run itself, not just the changeover.", + "state": "done" + }, + { + "type": "text", + "text": "Understood — so Line 1 is universal but slower, Line 2 is fast but whites and tints only, and Line 3 is still being opened up product by product. That qualification constraint was binding when TC-11 arrived.\n\nLet me understand what actually happens once you've assigned an order to a qualified line. **Take that TC-11 run on Line 1**: after the specialty batch finished Saturday and you did the quick rinse, what happened during the TC-11 run itself — what stages did it go through, roughly how long did it take, and what made the line unavailable for anything else?", + "state": "done" + } + ] + } + ], + "settlements": [ + { + "submissionId": "sub_01M1GY81MXQHEWSQFK5Q8HFGBV", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GY81MXQHEWSQFK5Q8HFGBV" + }, + { + "submissionId": "sub_01M1GY9CAEVP3CVTC5Z6X34G8X", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GY9CAEVP3CVTC5Z6X34G8X" + }, + { + "submissionId": "sub_01M1GYAHSJDRW2887GA0G9FD73", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYAHSJDRW2887GA0G9FD73" + }, + { + "submissionId": "sub_01M1GYB8H3DDGJDN84F3T39QC5", + "outcome": "completed", + "answeredBySubmissionId": "sub_01M1GYB8H3DDGJDN84F3T39QC5" + } + ], + "incarnation": "inc_01M1GY81MYS7RNBTTB9RP5EWVW" + }, + "rawConversationSnapshotSha256": "75366f983a3afa137496b64706447292469d0bae8c8961c895a17cfa107af6be" +} diff --git a/libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-4-voice-integration-handoff.md b/libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-4-voice-integration-handoff.md new file mode 100644 index 00000000000..0e86cf84559 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-4-voice-integration-handoff.md @@ -0,0 +1,68 @@ +# Mission 4 to Voice integration handoff + +Date: 2026-09-03 + +Status: **branch-close reconciliation; no Voice branch was merged or modified.** + +## Compared heads + +After `gt sync && gt restack`, the content-verified Mission 4 branch was compared with the current remote Voice stack. Current restacked commit IDs are deliberately not pinned here; accepted campaign identity comes from the frozen content manifests, while execution-time SHAs remain historical provenance in those records. + +| Surface | Head / PR | Observed purpose | +| --- | --- | --- | +| Mission 4 | `ln/fe-1563-redesign-runbook-workpiece` | Package-composed Brunch core capability and SDCPN job skill, production Flue evidence, persona harness | +| Spoken-response optimization | `c7fe8a2e68e8fdc37018b21ec2e9daf4e9ef7c82`, PR [#9496](https://github.com/hashintel/hash/pull/9496) | Voice-optimized Brunch response lifecycle | +| Temporary Brunch ask shim | `252b9dbb0c77fae8cee45a506f09cac3e20c381c`, PR [#9507](https://github.com/hashintel/hash/pull/9507) | Dynamic client ask and correlated answer history | +| Voice turn-taking and provenance | `a7b1115228df64f3592037cf2cd316a551d348fe`, PR [#9512](https://github.com/hashintel/hash/pull/9512) | Voice dock, interruption/cancellation, finalized-answer provenance, canonical transcript behavior | + +The latest Voice head descends through the temporary ask stack but not through this Mission 4 branch. After Graphite synchronization their observed common base remains `807fc0481ae3eed147f911d5d4a49ef9031a8afe`, before Mission 4's current app/package restructuring. Mission 4 remains stacked on `ln/fe-1525-headless-runbook-pn`; the Voice stack begins from a separate PR based on `main`. Neither stack currently includes the other. + +## Integration rule + +Port Voice behavior onto Mission 4's current ownership and file topology; do not resolve conflicts by restoring the Voice branch's older app-local Brunch stub. + +A read-only `git merge-tree --write-tree HEAD origin/kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance` after the final restack confirmed five direct conflict surfaces: + +- modify/delete conflict at the obsolete `apps/brunch-agent/src/agents/chat-agent.ts` path; +- content conflicts in `apps/brunch-agent/src/conversation/client-tools.ts` and `conversation/ui-stream.ts`; +- a directory-rename split for the old `apps/brunch-agent/src/tools/` directory, which Mission 4 split by ownership while Voice adds `brunch-ask.ts` there; +- content conflicts in `apps/brunch-agent/test/petrinaut-chat.integration.ts` and `petrinaut-chat.test.ts`. + +`apps/brunch-agent/package.json`, `conversation/transcript.ts`, `test/flue-transcript.test.ts`, `test/petrinaut-chat-result.ts`, and `yarn.lock` merged mechanically in that probe, but still require semantic review. The Petrinaut panel/Voice subtree did not directly conflict because Mission 4 does not modify it. This probe changed no branch or worktree. + +| Voice-stack edit location | Current Mission 4 authority | Reconciliation | +| --- | --- | --- | +| `apps/brunch-agent/src/agents/chat-agent.ts` | `apps/brunch-agent/src/agents/chat-agent/agent.ts`, `@hashintel/brunch-agent/flue`, and `@hashintel/brunch-agent-plugin-sdcpn/flue` | Preserve `useBrunchAgent()` and `useSdcpnPlugin()`. Mount only the Voice-required client tool and narrowly scoped host instruction in the current composer. Do not restore `confirm-path` or the concise stub prompt. | +| `apps/brunch-agent/src/tools/brunch-ask.ts` | Core ask name/input/output contracts in `packages/core/src/client-tools.ts`; executable host tool remains an app/production-composition decision | Re-evaluate the temporary shim against the current suspended structured-question policy. If retained for Voice, keep it visibly temporary and mount it without changing universal elicitation policy. | +| `apps/brunch-agent/src/client-tool.ts` | `apps/brunch-agent/src/conversation/client-tools.ts` | Add any accepted ask tool to the current client-tool registry and preserve exact suspension/result correlation. Keep `readPetrinautDoc` behavior unchanged. | +| `apps/brunch-agent/src/flue-transcript.ts` | `apps/brunch-agent/src/conversation/transcript.ts` | Port finalized Voice-answer provenance and dynamic ask history to the relocated transcript projection; canonical Flue history remains authoritative. | +| `apps/brunch-agent/src/flue-ui-stream.ts` | `apps/brunch-agent/src/conversation/ui-stream.ts` | Port only current streaming/provenance behavior through the relocated module. | +| Voice changes in Petrinaut `ai-assistant-panel.tsx` and its private subtree | Same Petrinaut panel/public contracts, largely unchanged by Mission 4 | Preserve Voice's `submitText`, interruption, playback, and provenance contracts; adapt host tool names/types to the current Brunch package exports rather than duplicating them. | + +## Compatible decisions + +The branches agree on several useful invariants: + +- Brunch chooses the interview question and canonical response text. +- Voice may prepare or speak that text but does not become the elicitation decision-maker. +- Finalized answers enter canonical conversation history; provisional audio/transcription remains ephemeral. +- Client-tool answers are correlated to the exact pending tool call and resume the existing Flue turn. +- Canonical history, not a secondary Voice transcript, is the durable evidence source. +- Host/browser code executes interactive UI behavior; core owns reusable question/answer semantics when that capability is promoted beyond a temporary preview shim. + +## Decisions that remain open at integration + +1. Whether the temporary `brunch_ask` shim is still needed after current structured-question policy is re-evaluated, or Voice should initially remain text-turn-only. +2. Whether Voice integrates by making its stack a new parent for this closed branch, by porting Mission 4 commits onto the Voice stack, or by a fresh reconciliation branch. This document selects no Git operation. +3. How the Voice stack's conversation identity maps onto current principal + conversation id derivation and per-net continuity. +4. Whether the current S4 report-versus-immediate-ask policy matters to Voice. It remains deferred; Voice must not silently make it acceptance-critical. +5. Which current documentation screenshots or user-guide passages need refresh after the final merged UI is observable. + +## Verification floor for a reconciliation branch + +- The built app still mounts the independent `elicitation` and `sdcpn-modelling` skills through the current core/plugin composition. +- One typed Brunch interview and one Voice interview share canonical Flue history without duplicated or inferred answers. +- One dynamic ask, if retained, renders in the panel, accepts exactly one finalized typed or spoken answer, records its provenance, and resumes the originating tool call. +- Voice cancellation and **Your turn** cannot submit playback or pre-handoff microphone audio as an answer. +- Stock assistant mode remains functional and independent. +- Existing Mission 4 package, topology, transcript, transport, and persona-harness tests plus the Voice stack's panel/turn-taking/provenance tests pass after conflict resolution. diff --git a/libs/@hashintel/brunch-agent/docs/inbox/amp-session-reviewing-architecture.md b/libs/@hashintel/brunch-agent/docs/inbox/amp-session-reviewing-architecture.md deleted file mode 100644 index 0de291bc352..00000000000 --- a/libs/@hashintel/brunch-agent/docs/inbox/amp-session-reviewing-architecture.md +++ /dev/null @@ -1,6996 +0,0 @@ ---- -title: Package architecture review -author: Lu Nelson -threadId: T-01a032f5-37ff-7328-a0d5-f8ee48678dd2 -created: 2026-08-24T08:48:46.591Z -agentMode: high ---- - -# Package architecture review - -## User - -please get oriented in this package, and then I'd like to conduct a high-level design- and planning-review session - -## Assistant - -I’ll first map the package’s declared architecture, posture, active planning artifacts, and recent work. Then I’ll frame the review around the decisions that are actually open rather than giving a generic codebase tour. - -## User - -it's actually multiple packages; I should have referred to it as a "context root" - -## Assistant - -Right — I’ll treat `libs/@hashintel/brunch-agent` as the **Brunch context root**: one authority surface for the glossary, ADRs, protocols, and planning records; four child package workspaces; plus the separately located `apps/brunch-agent` application. That distinction is architecturally important here. - -The initial read suggests the package boundaries are comparatively settled; the more consequential review target is likely the transition from the completed import/integration spine to the still-provisional elicitation semantics and milestone plan. I’m narrowing that now against the coordination ledger and current Linear state. - -## Assistant - -The coordination document’s literal `yarn turbo …` form is stale for this Yarn setup; it fails before invoking Turbo. I’ll use the repository’s installed Turbo binary directly, preserving the same read-only graph task. - -## Assistant - -The live DAG materially diverges from the 21 August coordination snapshot: seven new root-level issues, FE-1476–FE-1482, now describe the September/demo, routing, provenance, re-elicitation, requirements-graph projection, and CPS-plugin direction. Before proposing a review frame, I’m reading those issue contracts and the three established root maps to determine whether this is intentional reframing or planning drift. - -## Assistant - -I’m oriented to the **Brunch context root**: - -```diagram -┌──────────────────────────────────────────────┐ -│ Brunch context root │ -│ glossary · ADRs · protocols · planning │ -└───────────────┬──────────────────────────────┘ - │ - ┌────────────┼─────────────┬──────────────┐ - ▼ ▼ ▼ ▼ - core binding-flue transport-aisdk plugin-gherkin - harness substrate UI wire target policy - └────────────┴─────────────┴──────────────┘ - │ - ▼ - apps/brunch-agent - remote server - │ - ▼ - apps/petrinaut-website - user-facing composition -``` - -### Assessment - -- Package boundaries are coherent and mechanically guarded by the [ratified topology](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/topology.md#L10-L75). -- The durable mechanics exist: ask/reply, settlement and sweep, provenance anchoring, capture storage, Flue binding, and AI SDK transport. -- The product-defining path remains mostly design: the plugin API is intentionally skeletal, and Gherkin is only a one-proposal tracer ([plugin API](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts#L5-L14), [Gherkin tracer](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts#L43-L54)). -- The live Linear plan has drifted beyond [COORDINATION.md](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md#L14-L65): FE-1476–FE-1482 are unregistered roots, FE-1477 substantially duplicates FE-1440, and several new tickets overlap earlier plugin/demo work. -- Most importantly, FE-1480 assumes an inferential requirements-graph → SDCPN projection. That conflicts with ADR-0003’s rule that projections are deterministic over register 2 and semantic inference occurs only through contestable captures ([decision](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/adr/0003-three-register-ir.md#L24-L49)). - -### Recommendation - -Next: an `arc-map` review of the September milestone—but first settle that projection boundary. Afterwards we can reconcile the demo story, dependencies, and duplicate tickets. - -Opening question: - -**Where should the non-deterministic requirements → SDCPN judgment live?** - -1. **Preserve ADR-0003:** the LLM emits explicit, provenance-bearing formalization captures; folding and SDCPN projection remain pure. -2. **Amend ADR-0003:** projection itself may infer, requiring an auditable and reproducible projection-run model. - -My initial recommendation is **1**. Is that still your intended architecture? - -## User - -To your points: - -- yes, there are new tickets, drafted by my PM Dora, which we must adopt and fold-in to our graph -- package boundaries are in place yes; but implementation boundaries and paths are only traced so far, and this needs serious attention -- and yes FE-1480 will require discussion (as will other issues) - -Before we address those things, let me give you the high-level concerns that are on my mind, as other entrypoints: - -- the integration with the petrinaut demo website is only the thinnest of tracer bullets so far; completing the full contract layers here will bloom in to much larger scopes -- we haven't even begun `plugin-cps`, which is the actual thing we need to build a critical demo around, very soon (2 weeks in human time) -- the current design for the elicitor architecture seems sound; but its rigor may be at cross-purposes with establishing an e2e working model as fast as we can -- IMO the current issue topology/graph likely does not inherently express the goals we need to be driving at - -## Assistant - -Those concerns point to one underlying problem: the current plan is organized around **contract completion**, while the urgent objective is a **demo-capable vertical system**. That makes locally sensible work compete with global time-to-proof. - -I think the review should separate what must stay rigorous from what may remain deliberately narrow for two weeks, then rebuild the dependency graph around the demonstrated outcome. - -## Assistant - -Your diagnosis matches mine. The tension is less **rigor versus speed** than **generality versus speed**. - -We should retain rigor at irreversible boundaries—provenance, capture correction, real application wiring, valid SDCPN output—and defer broad genericity: complete plugin SDK, full CPS ontology, every affordance, multi-target validation, and contract freeze. - -I’d reshape the plan around vertical proofs: - -```diagram -Fixture captures ─▶ CPS model ─▶ valid SDCPN - │ -Live correction ─▶ supersession ─▶ changed SDCPN - │ -Net element ─▶ model slot ─▶ capture ─▶ user evidence - │ - real Petrinaut demo website -``` - -Existing capability tickets become constraints and inputs, not the primary execution sequence. FE-1476 should likely own the demo outcome, with FE-1477–FE-1482 folded beneath it and reconciled against FE-1393/1402/1403/1438/1440/1442. - -The first decisive question: - -**Is FE-1476 now the authoritative demo contract, replacing cold-start comprehensive elicitation with the prebuilt-model → provenance → targeted revision story?** - -My recommendation: **yes**. Otherwise we are planning two demos in two weeks. This would remove full cold-start elicitation and comprehensive completion accounting from the critical path unless they are still explicitly required. - -## User - -This essential line of reasoning is correct; but may still be working on too-thin signals. The core concern that exhibits the tension between the priorities of correct/complete vs "works effective e2e" is the actual elicitor architecture, and I think we should work through a fuller evaluation of this, to understand what's really on the critical path and what's not - -## Assistant - -Agreed. We should not infer the critical path from ticket titles or the demo script yet. We need to evaluate the elicitor as a system: trace each runtime responsibility from user turn to durable assertion, model, projection, and UI effect; then distinguish **missing load-bearing behavior** from merely incomplete contract breadth. - -I’ll assess the traced layers for role completeness, contract coverage, and fidelity to the proposed architecture before we resume prioritization. - -## Assistant - -One concrete issue is already emerging: the implemented loop can interview and append provenance-safe captures, but it cannot yet *reason over the target-document it has built*. The binding gives the model generic ask/sweep instructions; the private sweep sees conversation text and a proposal schema, but there is no register-2 model, demand state, projection, or plugin guidance feeding the next turn. - -I’m checking the correction and projection seams now, because the new demo requires the loop to revise existing knowledge—not merely capture new statements. - -### Readiness Map: Elicitor architecture - -**Verdict:** the substrate and capture foundation is strong, but the system is only tracer-complete. The implemented path currently terminates at captures: - -```diagram -Implemented -─────────── -User ─▶ agent ─▶ ask/reply ─▶ settlement ─▶ sweep ─▶ durable captures - │ - └── dead end - -Designed but absent -─────────────────── -captures ─▶ fold ─▶ elicited model ─┬─▶ completion/gaps ─▶ next-question policy - ├─▶ SDCPN projection ─▶ Petrinaut - └─▶ provenance map ─▶ targeted revision -``` - -| Layer | Readiness | Critical-path judgment | -|---|---|---| -| Ask/suspend/transport | Mechanically substantial | Foundation ready; broader affordances are deferrable | -| Sweep/capture/provenance | Mechanically substantial | Foundation ready, but not yet usable for revision | -| Model-aware elicitation loop | Absent | **Critical** | -| Register-2 fold/model | Design only | **Critical**, in a deliberately narrow CPS form | -| CPS plugin | Absent | **Critical** | -| SDCPN projection | Design only and contested | **Critical design decision** | -| Petrinaut document application | Ask-only local tracer | **Critical**, but its correct mechanism is unresolved | -| Existing-document/reviewer session | Domain primitives exist; host collapses document to conversation | **Critical** for FE-1476 | -| Artifact provenance and scoped re-elicitation | Store primitives only | **Critical** for FE-1478/1479 | -| Generic plugin authoring, completion, loss reporting, full catalog | Design only | Mostly deferrable unless selected by the demo story | -| Public durability/security/deployment | Partial | Critical only after the functional loop exists | - -#### Important architectural findings - -1. **The elicitor does not yet consult what it has elicited.** - Sweep extraction receives conversation text and proposal names, not active captures, issues, or a derived model ([binding](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts#L138-L227)). Consequently, no model state can steer the next question. - -2. **Targeted correction is not reachable.** - The store rigorously supports `supersedes`, but extraction cannot see the active capture IDs it must reference. The current Gherkin schema cannot emit `supersedes` either. - -3. **The planned projection and client-tool paths potentially duplicate semantic authority.** - The plugin design says register 2 projects to a canonical SDCPN. FE-1438 instead has the agent manipulate Petrinaut through browser tools. If both perform modelling judgments, provenance and reproducibility break. We must separate: - - semantic projection: model → SDCPN; - - application: SDCPN/diff → live document; - - optional repair: diagnostics → revised projection. - -4. **The reviewer workflow needs a real target-document boundary.** - The domain model supports multiple sessions against one document, but the current host maps `targetDocumentId` directly from `conversationId` ([current mapping](file:///Users/lunelson/Code/hashintel/hash/apps/brunch-agent/src/petrinaut-chat.ts#L31-L45)). FE-1476 requires a reviewer session reopening an existing elicitation. - -5. **The generic plugin architecture may be premature, but its fundamental registers are not.** - We probably need captures → model → projection for provenance and revision. We probably do **not** need the complete declarative meta-schema, generic fold algebra, Gherkin-first SDK, all CPS proposal kinds, and contract freeze before proving one CPS path. - -My resulting hypothesis is: - -> Preserve the three-register architecture, but let the first CPS implementation discover the executable plugin boundary instead of completing the generic plugin framework first. - -The next unresolved design area is therefore the **semantic materialization boundary**: concrete CPS fold, projection ownership, provenance mapping, and how the resulting artifact enters Petrinaut. - -**Routing**: where should we go next? - -1. **Explore module shape (Recommended)** — design two competing executable shapes for that semantic boundary. -2. **Design verification** — first define the minimum end-to-end proof and its oracles. -3. **Back to triage** — continue auditing other architectural layers before choosing a boundary. - -Reply **1–3**, or redirect the review. - -## User - -Those routing options are good; but let's keep discussing for now. - -FWIW, your observations match my intuition and my suspicions. - -I must say, that tracer discpline has been a real double-edged sword: it's been a very common for the agent to take the tracer as the "definition of done" and for the actual implementation to be essentially left not only incomplete but barely started. - -I think your essential prioritization and plan-shape is correct; but we're going to need a new protocol for representing and driving that kind of plan or it's going to evaporate after this session: the `COORDINATION.md` doc is nice, but right now only tracks the issue-graph topology with a bias toward _what is mechanically next_ i.e. unblocked, while not having any way to track an actual strategic execution plan, which should rather be objective-driven, epistemically-tracked and with the issue-graph as a sequencing and trade-off surface - -I want to offer the mental model I have of the elicitor architecture so far, which I had skethed out before starting this thread, and which you can also therefore analyze as both a proposal and and an inventory, and help me determine where I have gaps or weak layers. So far, I think of it in roughly four parts, which belong to two essential layers (there are more layers than these two but these are the two with real architectural design pressure and which are currently hollow) - -1. __harness layer: elicitation agentics__. the prompting and skilling parts, general to all elicitations: - - orientations, motivations, licenses - - interviewing/question-formulation guidance -2. __harness layer: capture mechanics and agentics__. the capture mechanics so far; but lacking further build-out, as you've observed - - evidence sweeping and logging mechanism; with annotations re: supersession, conflicts etc. - - next-question ledger and loop? - - mapping plugin-defined types from utterances: entities (nodes), relations (edges), quantities, conditionalities -3. __plugin layer: typologies, critical points and motifs, ...?__. the things for the capture parts to be aware of - - what entities are we after - - what shapes and relations do they have -4. __plugin: runbook (objectives, trajectories, boundaries)__. this is the least sketched part so far IMO, and so "runbook" is a proposal at this point, about how the rest of the plugin manifest could actually be shaped - - the job - - what 'done' looks like - - how to check - - ... (what else?) - -## Assistant - -Your model is sound. I would make two refinements: - -1. Separate the **elicitation controller** from capture mechanics. -2. Separate the plugin’s static **runbook** from the dynamic **engagement brief** it produces for one target-document. - -```diagram - HARNESS -┌─────────────────────────────────────────────┐ -│ Strategy repertoire │ -│ orientations · techniques · licenses │ -└───────────────────┬─────────────────────────┘ - ▼ -┌─────────────────────────────────────────────┐ -│ Elicitation controller │ -│ assess situation · choose move · stop/replan│ -└───────▲───────────────────────────┬─────────┘ - │ ▼ -┌───────┴───────────────┐ ask · propose · validate -│ Evidence engine │ -│ sweep · capture · │ -│ supersede · resolve │ -└──────────┬────────────┘ - │ assertions - ▼ - PLUGIN -┌─────────────────────────────────────────────┐ -│ Domain contract │ -│ proposals · model · fold · diagnostics · │ -│ projection · provenance │ -└──────────▲──────────────────────────────────┘ - │ model state and gaps -┌──────────┴──────────────────────────────────┐ -│ Job runbook │ -│ objectives · trajectory · checks · stopping │ -└─────────────────────────────────────────────┘ -``` - -### 1. Harness: strategy repertoire - -Your contents fit, with one qualification: - -- **Orientations**: generic role and epistemic posture. -- **Licenses**: re-ask, challenge, propose for correction, expose assumptions. -- **Techniques**: contrastive questions, incident reconstruction, quantile elicitation. -- **Question formulation guidance**: generic forms only. - -The harness should define these capabilities, but not decide when domain-specific questions matter. Prompting and Flue skills are their delivery mechanism—not the architectural concepts themselves. - -**Current weakness:** the generic quiver is named but not designed. More importantly, there is no module composing its strategies into a coherent engagement. - -### 2. Harness: evidence engine - -This should own: - -- conversation archive and evidence classification; -- settlement and sweep execution; -- capture envelope and provenance; -- atomic application; -- issues, conflicts, supersession and retraction; -- invocation of plugin-defined proposal extraction. - -But two items in your list sit elsewhere: - -- **“Next-question ledger and loop” belongs to the controller.** -- **Entities, relations and conditionalities belong to plugin vocabulary.** The harness executes schema-constrained extraction; the plugin defines what can be extracted. Quantities may come from a shared stated-form library, but should not become universal harness ontology. - -A useful decomposition is: - -```diagram -Model demand ─▶ knowledge gap ─▶ candidate move ─▶ chosen move ─▶ concrete ask - derived derived derived session state transcript -``` - -The “ledger” should mostly be derived, not persisted. Persist the selected trajectory or active commitment only when continuity requires it; otherwise stale agendas will compete with the current model. - -**Current weakness:** the evidence engine writes captures but provides no read path back into an elicitation controller. It is an append-capable substrate, not yet a closed loop. - -### 3. Plugin: domain contract - -This is broader than “what entities are we after.” It owns: - -- model node kinds, slots and relations; -- utterance-shaped proposal catalog; -- fold and identity semantics; -- grade and conflict semantics; -- domain validators and diagnostics; -- projection into artifacts; -- artifact-element → model-slot → capture provenance mapping. - -I would place your concepts as follows: - -- **Typologies** → model/proposal schemas. -- **Critical points** → derived diagnostics and question triggers. -- **Motifs** → runbook hypotheses or questioning scaffolds, not model facts unless the user confirms them. - -The existing “two schemas, two tables” design covers much of this, but is probably overcommitted to a generic authoring representation before one real CPS model works. - -### 4. Plugin: runbook - -“Runbook” is a good provisional name because it adds time, direction and judgment to the current `ElicitationPack`, which is otherwise mostly a bag of cards and checks ([current contract](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/spec.md#L531-L550)). - -A runbook likely needs: - -- **Supported job**: build, review, explain, or revise. -- **Kickoff contract**: objective, intended use, scope, audience, available effort. -- **Trajectory**: orient → skeleton → deepen → cross-examine → validate → hand off. -- **Priority policy**: objective relevance, information gain, dependency, user effort and risk. -- **Demand/completion rules**: what knowledge is required and to what grade. -- **Challenge checks**: contradictions, ungrounded assumptions, missing boundaries, invalid projections. -- **Stopping outcomes**: sufficient, useful-partial, stalled, budget-exhausted, user-stopped. -- **Default/inference policy**: what may be proposed, defaulted, inferred or must be asked. -- **Revision policy**: how to scope targeted re-elicitation and determine affected projections. -- **Output/handoff contract**: artifacts, provenance, visible gaps and validation results. -- **Recovery policy**: re-entry after another session or changed target-document. - -However, “the job” is not wholly static plugin content. The plugin offers a runbook template; kickoff creates an **engagement brief** containing the actual objective, boundaries, appetite and selected trajectory. - -### The largest missing architectural role - -The current design contains pieces of the controller—`demandRunner`, `affordanceCuer`, `firesWhen`, completion—but no interface unifying them. The controller must read: - -```text -engagement brief -+ current model and open issues -+ runbook -+ available generic strategies -+ recent conversation state -``` - -and produce a bounded next move: - -```text -ask | propose | contrast | validate | project | explain | stop -``` - -That is the hollow center between “we captured facts” and “we conducted an effective elicitation.” - -There is a parallel planning insight here: **the issue graph is analogous to the capture store**—durable inventory and dependencies, but not an execution strategy. `COORDINATION.md` is a projection over that inventory. A separate strategic plan must act like the controller/runbook: objective, current model of reality, confidence, current bet, proof spine, issue mapping and replanning triggers. - -The next architectural question I would examine is: - -> Does a plugin have one runbook, or a family of job-specific runbooks sharing one domain contract? - -My recommendation is the latter. For CPS, at least **cold-start construction** and **review-and-revise** have different kickoff, trajectory and stopping conditions. The September demo can then implement one honest `review-and-revise` runbook without pretending the complete CPS elicitor already exists. - -## User - -I agree a plugin could define multiple runbooks, that makes sense. And yes the planning insight echoes the gaps we're seeing in teh implementation. The planning itself needs a kind of "runbook" as well, in effect. The routines we've instituted so far here around the COORDINATION.md doc are just one part of that surface, and that surface should be the central sense-making tool for running the project - -## Assistant - -Exactly. **Unblocked is a property of the work graph, not a reason to perform work.** The missing surface must decide what matters next from objectives, evidence, risk and time. - -```diagram -Code/tests/docs/Linear - │ evidence - ▼ -┌──────────────────────────┐ -│ Strategic execution model│ -│ objective · current truth│ -│ bets · proof spine · risk│ -└────────────┬─────────────┘ - │ selects and scopes - ▼ - Issue graph / work - │ - └──── new evidence ────┘ -``` - -The existing artifacts each retain a narrower authority: - -- **Linear:** issue state and hard dependency truth. -- **COORDINATION.md:** topology, soft edges, seams and registry integrity. -- **SPEC-LEDGER.md:** implementation against settled obligations. -- **ADRs/specs:** durable design truth. -- **New strategic surface:** current objective, execution strategy, priority and confidence. - -### What the central surface should contain - -#### 1. Milestone contract - -- Outcome being pursued. -- Observable proof. -- Deadline and audience. -- Explicitly excluded outcomes. -- Which product claims must be honest versus merely demonstrated narrowly. - -#### 2. Current system model - -Not “tickets completed,” but: - -- what genuinely works end-to-end; -- which layers are tracer-only; -- which responsibilities are absent; -- external facts and constraints; -- assumptions with confidence and supporting evidence. - -#### 3. Current strategic bet - -A concise theory such as: - -> Preserve the three-register architecture, prove one CPS review-and-revise runbook concretely, and allow that implementation to determine the generic plugin interface. - -It should state why this bet beats alternatives and what evidence would reverse it. - -#### 4. Proof spine - -Proof obligations, not issues: - -```text -P1 CPS captures derive a model and valid SDCPN with provenance -P2 A reviewer session opens the existing target-document -P3 One scoped correction supersedes evidence and changes the model -P4 Reprojection changes the live Petrinaut net -P5 A provenance question reaches net → model → capture → utterance -P6 The complete path runs through the deployed demo surface -``` - -Each proof carries: - -- certainty; -- missing knowledge; -- acceptance oracle; -- mapped issues; -- dependencies—functional and epistemic; -- replan trigger. - -#### 5. Strategic frontiers - -“Now / next” chosen from the proof spine, with a reason. An issue being unblocked only makes it *eligible*. - -A frontier entry should answer: - -- Why now? -- What uncertainty or capability does it retire? -- What becomes possible after it? -- What is the smallest honest proof? -- Which adjacent work remains deliberately deferred? - -#### 6. Issue projection - -Map issues onto proofs many-to-many. This is where Dora’s tickets are adopted without allowing their initial decomposition to dictate implementation architecture. - -It should expose: - -- duplicate or overlapping tickets; -- tickets that contribute to no current proof; -- proofs with no owned work; -- scope that should move between tickets; -- trade-offs created by sequencing. - -#### 7. Learning and replanning - -Record only consequential shifts: - -- evidence observed; -- assumption promoted, weakened or falsified; -- confidence change; -- resulting strategy or sequence change. - -### Operating protocol - -1. **Orient:** read the strategic surface, then query live issue state. -2. **Select:** choose the frontier with the highest closure or information gain—not the lowest issue number. -3. **Scope:** define the smallest proof-producing slice. -4. **Execute and verify.** -5. **Reconcile:** update evidence, confidence, proof status and issue topology. -6. **Replan when a named trigger fires.** - -I would not design this protocol abstractly first. The September demo should be its first worked instance. We can construct the real strategic surface around the elicitor architecture, use it through at least one replan, and only then extract a reusable schema or skill. - -## User - -Agreed. I don't know yet what this is called; but it's not a roadmap, and not a blueprint, and not the spec either (we can have multiple specs anyway, at different levels) - -It's the thing that tracks objectives, evidence, risk and time -- but strategically, which is to say it continually evaluates the former against the latter: achieving objectives/obligations _UNDER_ constraints, limitations, pressures, unknowns. It consumes specs as one input and uses issues as a planning surface. - -We can model this protocol as we use it. A combination of document structure (perhaps templated?) and perhaps local agent-skills might be necessary, as we go - -## Assistant - -I think the core concept is **steering**. - -- **Steering model** — the current strategic understanding of objectives, position, constraints, evidence, risks and bets. -- **Steering surface** — the document plus tools through which people and agents inspect and update that model. -- **Steering protocol** — the recurring orient → choose → execute → reconcile → replan loop. - -`STEERING.md` would be a plausible provisional name. Unlike a roadmap, steering is explicitly feedback-driven: it continually adjusts the route against observed conditions. - -### Proposed structure - -```md -## Project steering - -### Mission -Outcome, obligations, proof, audience, deadline. - -### Current position -What is proved, tracer-only, absent, contradicted or unknown. - -### Pressures and constraints -Deadline, capacity, external dependencies, non-negotiable invariants. - -### Current strategy -The active bet, why it is preferred, and alternatives rejected for now. - -### Proof spine -The ordered capabilities or claims that must be demonstrated. - -### Active frontiers -The next proof-producing moves, why now, and what they unlock. - -### Risks, assumptions and unknowns -Confidence, evidence, consequence, retirement test and fallback. - -### Commitments and cut lines -Must / should / stretch / defer, with dates or evidence that trigger cuts. - -### Issue projection -Issues mapped onto objectives and proofs; overlaps, gaps and trade-offs. -Links to the live coordination graph rather than mirroring its state. - -### Replan triggers -Events that require reconsidering the strategy. - -### Recent confidence shifts -Only consequential learning; stable truth graduates elsewhere. -``` - -The key vocabulary could be: - -- **Objective:** desired outcome. -- **Obligation:** condition that must remain true. -- **Constraint:** unavailable or prohibited option. -- **Pressure:** fact that changes trade-offs, such as time. -- **Unknown:** material unanswered question. -- **Risk:** uncertain adverse outcome. -- **Bet:** chosen strategy under uncertainty. -- **Proof:** evidence that supports an objective or retires risk. -- **Frontier:** strategically selected next proof—not merely unblocked work. -- **Cut line:** condition under which scope is reduced or fallback selected. - -### Relationship to existing artifacts - -```diagram -Specs / ADRs ───────▶ obligations and design truth -Code / tests ───────▶ implementation evidence -Linear ─────────────▶ work inventory and hard dependencies -COORDINATION.md ────▶ issue topology, seams and registry - │ - ▼ - STEERING.md - strategy and next action -``` - -I would keep `COORDINATION.md` narrow rather than expanding it indefinitely. The steering surface consumes its graph projection while owning priority and rationale. - -### Adoption approach - -Start with the document and run it manually for the September effort. Avoid building a family of skills before the operating loop has been exercised. - -After a few real updates, stable routines could become local skills: - -1. **Orient** — assemble current evidence and identify drift. -2. **Steer** — select or revise the strategic frontier. -3. **Reconcile** — incorporate completed work and confidence shifts. -4. **Pressure-test** — challenge the proof spine, assumptions and cut lines. - -“Steering” is my strongest candidate, but I would treat the name as provisional until we use the model on the current elicitor plan. - -## User - -I like it. Let's run it on the current plan, both the explict (existing) parts and the implicit/projected ones per your observations in this thread. Let the September Demo effort's own content be the driver of the document structure, at least on this first pass (don't let our imagined headings and ontologies become forms to be filled for their own sake). Run it all past the elicitor when you're done for a sanity and comprehensiveness check WRT our findings and decisions above - -## Assistant - -*(cancelled)* - -I’ll create the first steering artifact as an evidence-backed September execution model, not a generic template. It will reconcile the existing coordination/spec/issue material with the architectural gaps established here, while leaving Linear state unchanged. - -I’ll then audit it against the actual elicitor flow and its missing feedback loops. The current Brunch agent cannot itself perform that strategic review, so I’m interpreting “past the elicitor” as a systematic check against the elicitor - -## User - -Sorry I meant "run it all past the oracle when you're done..." above, not the "elicitor" - -## Assistant - -Understood—I’ll draft the steering artifact, self-review it against the elicitor architecture and live plan, then ask Oracle for an independent sanity and comprehensiveness review before finalizing it. - -## Assistant - -I’m turning the discussion into a live steering model, not a generic planning template. Its center will be the September reviewer loop, with the current architecture and issue graph treated as evidence and constraints rather than as the plan itself. - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"captureStore\\.read|captureStore|targetDocumentIdFor|conversationId|supersedes|active.*head|proposal\" packages/binding-flue/src/index.ts packages/core/src apps/brunch-agent/src/petrinaut-chat.ts 2>/dev/null | head -160", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"client tool|tool-result|tool result|brunch_ask|readPetrinaut|write|apply|Petrinaut\" libs/@hashintel/brunch-agent/packages/transport-aisdk/src apps/brunch-agent/src apps/petrinaut-website/src 2>/dev/null | head -200", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"ElicitationPack|completion|cards|schema|fold|demand|projection\" libs/@hashintel/brunch-agent/packages/core/src libs/@hashintel/brunch-agent/packages/plugin-gherkin/src | head -220", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git status --short -- docs/INDEX.md docs/planning/_shared/COORDINATION.md docs/planning/_shared/STEERING.md; printf '%s\\n' '--- all worktree status ---'; git status --short", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CeZJSBoqePV9R2KWIqc` - -``` -{ - "output": "apps/brunch-agent/src/petrinaut-chat.ts:33:const targetDocumentIdFor = (conversationId: string): string =>\napps/brunch-agent/src/petrinaut-chat.ts:34: `petrinaut-local:${conversationId}`;\napps/brunch-agent/src/petrinaut-chat.ts:37: conversationId: string,\napps/brunch-agent/src/petrinaut-chat.ts:41: const agent = init(GherkinElicitor, { id: conversationId });\napps/brunch-agent/src/petrinaut-chat.ts:44: initialData: { targetDocumentId: targetDocumentIdFor(conversationId) },\napps/brunch-agent/src/petrinaut-chat.ts:64: input.conversationId,\napps/brunch-agent/src/petrinaut-chat.ts:74: input.conversationId,\napps/brunch-agent/src/petrinaut-chat.ts:75: targetDocumentIdFor(input.conversationId),\napps/brunch-agent/src/petrinaut-chat.ts:78: await session.historyReader.peek(input.conversationId),\napps/brunch-agent/src/petrinaut-chat.ts:89: input.conversationId,\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZJSg2bY9mqNZsXPSL` - -``` -{ - "output": "libs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:136: * Ask-return support. Absent, every tool-result follow-up stays refused\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:179: readonly reason: \"tool-result-follow-up-not-supported\";\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:196: reason: \"tool-result-follow-up-not-supported\",\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:261: * Classify one tool-result follow-up POST. A human answer submitted through\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:263: * machine tool result: exactly one submitted `brunch_ask` output on the\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:265: * Petrinaut mutation outputs, the synthetic diagnostics message — remains\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:587: execute: async ({ writer }) => {\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:592: // panel as an awaiting client tool, and the harness's own output\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:600: writer.write({\nlibs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts:626: writer.write(toUiChunk(wireEvent));\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:8:import { Petrinaut, type ViewportAction } from \"@hashintel/petrinaut/ui\";\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:14: PetrinautDocHandle,\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:21:const BrunchPetrinautWithHandle = ({\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:32: const [handle] = useState<PetrinautDocHandle>(() =>\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:46: <Petrinaut\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:58:export const BrunchPetrinaut = ({\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx:102: <BrunchPetrinautWithHandle\napps/petrinaut-website/src/main/app/brunch-demo/brunch-actual-mode-route.tsx:3:import { BrunchPetrinaut } from \"./brunch-petrinaut\";\napps/petrinaut-website/src/main/app/brunch-demo/brunch-actual-mode-route.tsx:33: <BrunchPetrinaut viewportActions={viewportActions} />\napps/brunch-agent/src/petrinaut-chat.ts:1:/** Application composition for Petrinaut's stock AI SDK chat transport. */\napps/petrinaut-website/src/main/app/brunch-demo/brunch-frame-parsers.ts:50: * Brunch execution-plan shape into a read-only Petrinaut SDCPN for rendering.\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:2: attachPetrinautOptimizationRunStream,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:3: createPetrinautOptimizerClient,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:4: PetrinautOptimizerHttpError,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:9: PetrinautOptimization,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:10: PetrinautOptimizationEvent,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:12:import type { PetrinautOptimizerFetch } from \"@local/petrinaut-optimizer-client\";\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:15: * Dev-proxy base for the local Petrinaut Optimizer: `vite.config.ts` rewrites\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:27: * Stamp the duck-typed classification fields Petrinaut's optimization\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:33: error instanceof PetrinautOptimizerHttpError\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:66: error instanceof PetrinautOptimizerHttpError\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:72:/** Create the local-only Petrinaut capability backed directly by Python. */\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:73:export const createPetrinautOptOptimization = (\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:74: fetchImpl: PetrinautOptimizerFetch = fetch,\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:75:): PetrinautOptimization => {\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:76: const client = createPetrinautOptimizerClient(\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:100: let events: AsyncIterable<PetrinautOptimizationEvent>;\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.ts:102: ({ events } = await attachPetrinautOptimizationRunStream({\napps/petrinaut-website/src/main/app/brunch-demo/brunch-status-page.tsx:58: Back to Petrinaut\napps/petrinaut-website/src/main/app/brunch-demo/brunch-protocol.ts:45: * This is intentionally not Petrinaut's full SDCPN document format. It only\napps/petrinaut-website/src/main/app/brunch-demo/brunch-protocol.ts:53: * creating a read-only handle with Petrinaut extensions disabled.\napps/petrinaut-website/src/main/app/brunch-demo/brunch-protocol.ts:56: * Brunch/Petrinaut protocol once that protocol is owned in Petrinaut Core.\napps/brunch-agent/src/routes.ts:4:/** Stock `DefaultChatTransport` endpoint used by Petrinaut's local panel. */\napps/petrinaut-website/src/main/app/brunch-demo/brunch-route.ts:2: * This is temporary, until Petrinaut Demo app gets a real Router.\napps/brunch-agent/src/agents/gherkin-elicitor.ts:54: * demo shell is chartered to mount this library alongside the Petrinaut\napps/petrinaut-website/src/main/app/optimization-demo/optimization-demo-app.tsx:2:import { PetrinautOptOptimizationProvider } from \"./petrinaut-opt-optimization-provider\";\napps/petrinaut-website/src/main/app/optimization-demo/optimization-demo-app.tsx:5: <PetrinautOptOptimizationProvider>\napps/petrinaut-website/src/main/app/optimization-demo/optimization-demo-app.tsx:7: </PetrinautOptOptimizationProvider>\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:3:import { createPetrinautOptOptimization } from \"./petrinaut-opt-optimization\";\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:5:import type { PetrinautOptimizationInput } from \"@hashintel/petrinaut-core\";\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:10:} as PetrinautOptimizationInput;\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:12:describe(\"createPetrinautOptOptimization\", () => {\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:33: const optimization = createPetrinautOptOptimization(fetchImpl);\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization.test.ts:72: const optimization = createPetrinautOptOptimization(fetchImpl);\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:1:import { PetrinautOptimizationContext } from \"@hashintel/petrinaut/react\";\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:3:import { createPetrinautOptOptimization } from \"./petrinaut-opt-optimization\";\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:7:const petrinautOptOptimization = createPetrinautOptOptimization();\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:9:/** Direct Petrinaut Opt integration for the local demo website only. */\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:10:export const PetrinautOptOptimizationProvider: FC<PropsWithChildren> = ({\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:13: <PetrinautOptimizationContext value={petrinautOptOptimization}>\napps/petrinaut-website/src/main/app/optimization-demo/petrinaut-opt-optimization-provider.tsx:15: </PetrinautOptimizationContext>\napps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx:12: definePetrinautAiInteractiveTool,\napps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx:143:export const brunchAskInteractiveTool = definePetrinautAiInteractiveTool({\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:7: type PetrinautDocHandle,\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:8: type PetrinautHandleCapabilities,\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:13: Petrinaut,\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:14: type PetrinautAiChatTransport,\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:15: type PetrinautAiMessage,\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:74:} satisfies PetrinautHandleCapabilities;\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:76:const createHandle = (net: SDCPNInLocalStorage): PetrinautDocHandle =>\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:83:const petrinautAiChatTransport: PetrinautAiChatTransport =\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:100: handle: PetrinautDocHandle;\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:112: * Local-storage demo shell for Petrinaut.\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:115: * Petrinaut document handle owns the currently open net's live editable state.\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:263: onMessages: (messages: PetrinautAiMessage[]) => {\napps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx:299: <Petrinaut\napps/petrinaut-website/src/main/app/local-storage-demo/use-local-storage-ai-messages.ts:3:import type { PetrinautAiMessage } from \"@hashintel/petrinaut/ui\";\napps/petrinaut-website/src/main/app/local-storage-demo/use-local-storage-ai-messages.ts:7:type AiMessagesByNetId = Record<string, PetrinautAiMessage[]>;\napps/petrinaut-website/src/main/app/local-storage-demo/walkthrough/walkthrough-steps.tsx:28: Welcome to Petrinaut\napps/petrinaut-website/src/main/app/local-storage-demo/walkthrough/walkthrough-steps.tsx:34: <strong>Petrinaut</strong> is a workshop for building, simulating, and\napps/petrinaut-website/src/main/app/local-storage-demo/walkthrough/walkthrough-steps.tsx:46: videoAlt: \"The Petrinaut editor with an example net on the canvas\",\napps/petrinaut-website/src/main/app/local-storage-demo/walkthrough/walkthrough-steps.tsx:59: <strong>Petrinaut</strong> lets you run experiments on complex\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZJT4MsLJLaJGxncm4` - -``` -{ - "output": "libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts:51: schema: StatementNotedProposal,\nlibs/@hashintel/brunch-agent/packages/core/src/testing/index.ts:41: schema: fixtureProposalSchema,\nlibs/@hashintel/brunch-agent/packages/core/src/session-log.ts:12: /** Stable identity supplied by the substrate's public projection. */\nlibs/@hashintel/brunch-agent/packages/core/src/session-log.ts:24: /** Substrate projection identity, distinct from the harness session id. */\nlibs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts:22: proposals: v.array(plugin.proposalCatalog[0].schema),\nlibs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts:227: `Use only the declared proposal schema: ${plugin.proposalNames.join(\", \")}. Do not add parsed structure or undeclared proposal types.`,\nlibs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts:297: \"Projection and validation are read-time operations; do not treat sweep completion as a stored derived result.\",\nlibs/@hashintel/brunch-agent/packages/core/src/capture-store.ts:294:// Range ordering belongs to this schema rather than to any one caller: every\nlibs/@hashintel/brunch-agent/packages/core/src/capture-store.ts:746: // States what the schema checked, and no more: the spans are structurally\nlibs/@hashintel/brunch-agent/packages/core/src/capture-store.ts:748: // having been resolved against an entry projection.\nlibs/@hashintel/brunch-agent/packages/core/src/capture-store.ts:1111: // Through the same schema a persisted issue is read with, so a command\nlibs/@hashintel/brunch-agent/packages/core/src/capture-store.ts:1131: // than in the schema: a closed conflict's captures are legitimately\nlibs/@hashintel/brunch-agent/packages/core/src/plugin.ts:11: * target has stressed it, so nothing in this scaffold ratifies the SDK export\nlibs/@hashintel/brunch-agent/packages/core/src/plugin.ts:29: readonly schema: v.GenericSchema<unknown, CaptureInputProposal>;\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZJTSUcr5qWmnMmPHy` - -``` -{ - "output": "--- all worktree status ---\n M libs/@hashintel/brunch-agent/docs/agents/git-workflow.md\n M libs/@hashintel/brunch-agent/docs/agents/issue-writing.md\n M libs/@hashintel/brunch-agent/docs/agents/legibility.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/hash-monorepo-import-plan.md\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,240p' packages/core/src/plugin.ts; sed -n '180,340p' packages/core/src/sweep-protocol.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"captureStore|sweep|archive|read\\(\" packages/binding-flue/src/index.ts; sed -n '300,560p' packages/binding-flue/src/index.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,120p' ../../../apps/brunch-agent/src/petrinaut-chat.ts; sed -n '240,290p' packages/transport-aisdk/src/index.ts; sed -n '120,210p' packages/transport-aisdk/src/index.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,120p' packages/plugin-gherkin/src/index.ts; sed -n '1,170p' ../../../apps/brunch-agent/src/agents/gherkin-elicitor.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CeZZ7NzVgHV9ojR89cd` - -``` -{ - "output": "import * as v from \"valibot\";\n\nimport type { CaptureInputProposal } from \"./capture-store\";\n\n/**\n * The plugin descriptor — identity only, at this stage.\n *\n * The plugin's real surface is its packs and the four operations (spec §6.1,\n * §11.1). Those are **deliberately absent here**: spec §13's two-targets rule\n * says the trivial target must not freeze the plugin contract before the hard\n * target has stressed it, so nothing in this scaffold ratifies the SDK export\n * surface. What the descriptor fixes now is only what the topology needs —\n * that a plugin declares which target-domain it defines, and does so through\n * Valibot like every other boundary in the system (spec §12.4).\n */\nexport const PluginDescriptor = v.object({\n /** Package-level identity, matching the `plugin-*` role prefix (spec §12.2). */\n name: v.pipe(\n v.string(),\n v.regex(/^plugin-[a-z][a-z0-9-]*$/, \"expected a `plugin-<name>` name\"),\n ),\n /** The artifact family this plugin elicits — gherkin scenarios, assurance arguments. */\n targetDomain: v.pipe(v.string(), v.nonEmpty()),\n});\n\nexport interface PluginProposalType {\n readonly name: string;\n readonly description: string;\n readonly schema: v.GenericSchema<unknown, CaptureInputProposal>;\n}\n\nexport type Plugin = v.InferOutput<typeof PluginDescriptor> & {\n /** FE-1392's declared floor; FE-1393 grows the catalog and SDK around it. */\n readonly proposalCatalog: readonly [PluginProposalType];\n};\n\n/**\n * Declare a plugin. Inversion of control (spec §4): the plugin declares and\n * registers; the harness discovers, orders, and invokes. Nothing a plugin\n * declares can reach persistence — the storage port is harness-defined and\n * binding-implemented, and plugins are storage-blind (spec §9.6).\n */\nexport function definePlugin(descriptor: Plugin): Plugin {\n const identity = v.parse(PluginDescriptor, descriptor);\n const [proposal, ...extraProposals] = descriptor.proposalCatalog;\n if (!proposal || extraProposals.length > 0) {\n throw new TypeError(\n \"This slice requires exactly one declared proposal type.\",\n );\n }\n const name = v.parse(v.pipe(v.string(), v.nonEmpty()), proposal.name);\n const description = v.parse(\n v.pipe(v.string(), v.nonEmpty()),\n proposal.description,\n );\n return {\n ...identity,\n proposalCatalog: [{ ...proposal, name, description }],\n };\n}\n ...parsedState,\n lastCheckedUserEntryId: parsedState.sweptThroughUserEntryId,\n });\n};\n\nconst renderEntry = (entry: SweepSessionEntry): readonly string[] => {\n const rendered: string[] = [];\n for (const affordance of entry.affordances ?? []) {\n rendered.push(`[assistant ask] ${affordance.markdown}`);\n }\n if (entry.text.length > 0) {\n const label = isTrueUserEntry(entry) ? \"user\" : entry.kind;\n rendered.push(`[${label}] ${entry.text}`);\n }\n return rendered;\n};\n\nconst renderTail = (tail: readonly SweepSessionEntry[]): string =>\n tail.flatMap(renderEntry).join(\"\\n\");\n\nexport interface SettlementCheckSignal {\n readonly type: \"settlement-check\";\n readonly tagName: \"settlement-check\";\n readonly body: string;\n}\n\nexport const buildSettlementCheckSignal = (\n tail: readonly SweepSessionEntry[],\n): SettlementCheckSignal => ({\n type: \"settlement-check\",\n tagName: \"settlement-check\",\n body: [\n \"The harness computed this unswept conversation tail:\",\n renderTail(tail),\n `Judge whether this range has settled. If it has, call ${toolName(\"sweep\")}. Declining is legal; continue the interview when the topic is still open.`,\n ].join(\"\\n\\n\"),\n});\n\nexport const buildSweepExtractionPrompt = (\n plugin: {\n readonly targetDomain: string;\n readonly proposalNames: readonly string[];\n },\n tail: readonly SweepSessionEntry[],\n): string =>\n [\n `Extract capture proposals for the ${plugin.targetDomain} target from this settled conversation range.`,\n `Use only the declared proposal schema: ${plugin.proposalNames.join(\", \")}. Do not add parsed structure or undeclared proposal types.`,\n \"Every user-grounded proposal must cite one or more exact verbatim quotes from the user lines below. Never supply entry ids, ranges, pointers, or evidence sources; the harness resolves those.\",\n \"The declared verbatim interior must preserve what was said without paraphrase or normalization. Return an empty proposal list when no honest capture is available.\",\n renderTail(tail),\n ].join(\"\\n\\n\");\n\nexport interface SweepRepairSignal {\n readonly type: \"sweep-repair\";\n readonly tagName: \"sweep-repair\";\n readonly body: string;\n}\n\nexport const buildSweepRepairSignal = (\n refusal: Pick<CaptureStoreRefusal, \"code\" | \"message\"> | SweepRefusalFact,\n): SweepRepairSignal => ({\n type: \"sweep-repair\",\n tagName: \"sweep-repair\",\n body: `The sweep was refused: ${refusal.message} Repair the proposal and call ${toolName(\"sweep\")} again. Declining is legal.`,\n});\n\nexport const pendingSweepRepair = (\n entries: readonly SweepSessionEntry[],\n): SweepRefusalFact | null => {\n let pending: SweepRefusalFact | null = null;\n for (const entry of entries) {\n if (entry.sweepRepairSignal) pending = null;\n if (entry.sweepResult?.status === \"refused\" && entry.sweepResult.refusal) {\n pending = entry.sweepResult.refusal;\n } else if (entry.sweepResult && entry.sweepResult.status !== \"refused\") {\n pending = null;\n }\n }\n return pending;\n};\n\nexport interface UnaccountedAskAdvisory {\n readonly type: \"unaccounted-ask\";\n readonly affordanceId: string;\n readonly question: string;\n readonly message: string;\n}\n\nexport const computeUnaccountedAskAdvisories = (\n tail: readonly SweepSessionEntry[],\n capturedUserEntryIds: ReadonlySet<string>,\n): readonly UnaccountedAskAdvisory[] => {\n const accountedAffordanceIds = new Set(\n tail.flatMap((entry) =>\n entry.replyToAffordanceId === undefined ||\n !capturedUserEntryIds.has(entry.id)\n ? []\n : [entry.replyToAffordanceId],\n ),\n );\n return tail.flatMap((entry) =>\n (entry.affordances ?? [])\n .filter((affordance) => !accountedAffordanceIds.has(affordance.id))\n .map((affordance) => ({\n type: \"unaccounted-ask\" as const,\n affordanceId: affordance.id,\n question: affordance.markdown,\n message:\n \"The swept range contains an ask with no affordance-bound capture.\",\n })),\n );\n};\n\nexport const settlementProtocolInstructionFragments = (): readonly string[] => [\n \"When the harness reports an unswept tail, judge whether that range has settled. Declining is legal.\",\n `When it has settled, call ${toolName(\"sweep\")}. The harness privately extracts quote-anchored proposals, refreshes durable history, applies them atomically, and advances the swept high-water mark only on success.`,\n \"Projection and validation are read-time operations; do not treat sweep completion as a stored derived result.\",\n];\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZZ7m3yMGGMRQHGE53` - -``` -{ - "output": "45: sweepableRange,\n78: readonly captureStore: CaptureStore;\n100: \"sweepHighWater\",\n104: let sweepState = parseSweepState(storedSweepState);\n139: name: toolName(\"sweep\"),\n141: \"Apply or replay the settled conversation prefix. The harness privately extracts quote-anchored captures, refreshes durable history immediately before atomic application, and advances sweep state only on success.\",\n152: const range = sweepableRange(historyAtJudgment);\n159: \"extract-sweep-proposals\",\n178: // archive write makes every quote resolvable before the store sees it.\n181: await session.historyReader.read(session.sessionId),\n184: const applied = await step.do(\"apply-sweep\", () =>\n185: session.captureStore.execute(\n187: type: \"apply-sweep\",\n196: sweepState = reopenSweepAfterRefusal(sweepState);\n197: setSweepState(sweepState);\n203: sweepState = advanceSweepHighWater(sweepState, throughUserEntryId);\n204: setSweepState(sweepState);\n206: session.captureStore,\n246: state: sweepState,\n251: sweepState = decision.nextState;\n252: setSweepState(sweepState);\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZZ8SSapF1CCmTDc8k` - -``` -{ - "output": "/** Application composition for Petrinaut's stock AI SDK chat transport. */\n\nimport { init } from \"@flue/runtime\";\n\nimport {\n decideAskReplyAdmission,\n pendingAskAffordanceId,\n} from \"@hashintel/brunch-agent\";\nimport {\n createFlueReplyProjector,\n projectFlueHistoryForSweep,\n} from \"@hashintel/brunch-agent-binding-flue\";\nimport {\n createAiSdkChatHandler,\n type HarnessReplyEvent,\n type TransportInspectionEvent,\n} from \"@hashintel/brunch-agent-transport-aisdk\";\n\nimport { GherkinElicitor } from \"./agents/gherkin-elicitor.ts\";\nimport { createGherkinElicitationSession } from \"./elicitation-session.ts\";\n\nconst inspect =\n process.env.BRUNCH_TRANSPORT_AISDK_INSPECT === \"1\"\n ? (event: TransportInspectionEvent): void => {\n // This is an opt-in shell diagnostic stream. It is never dispatched\n // into Flue and therefore cannot become elicitation evidence.\n console.log(`TRANSPORT_AISDK ${JSON.stringify(event)}`);\n }\n : undefined;\n\n// FE-1439 replaces this local one-conversation/one-document identity\n// with principal-owned private session lookup. Keep it opaque here.\nconst targetDocumentIdFor = (conversationId: string): string =>\n `petrinaut-local:${conversationId}`;\n\nconst streamElicitorTurn = async (\n conversationId: string,\n dispatch: { readonly message: string; readonly idempotencyKey: string },\n emit: (event: HarnessReplyEvent) => void,\n): Promise<void> => {\n const agent = init(GherkinElicitor, { id: conversationId });\n const receipt = await agent.dispatch({\n ...dispatch,\n initialData: { targetDocumentId: targetDocumentIdFor(conversationId) },\n });\n const projector = createFlueReplyProjector({\n submissionId: receipt.submissionId,\n emit,\n });\n await agent.read(receipt, { onEvent: (chunk) => projector.accept(chunk) });\n};\n\nexport const petrinautChatHandler = createAiSdkChatHandler({\n allowedOrigins: (\n process.env.BRUNCH_PETRINAUT_ORIGINS ??\n \"http://127.0.0.1:4915,http://localhost:4915\"\n )\n .split(\",\")\n .map((origin) => origin.trim())\n .filter((origin) => origin.length > 0),\n inspect,\n runTurn: (input, emit) =>\n streamElicitorTurn(\n input.conversationId,\n { message: input.userMessage.text, idempotencyKey: input.idempotencyKey },\n emit,\n ),\n askReply: {\n // Admission consults durable Flue history, not request-shaped claims: the\n // submission resumes the conversation only when its tool-call id\n // correlates with the one ask still awaiting a reply.\n async admit(input) {\n const session = createGherkinElicitationSession(\n input.conversationId,\n targetDocumentIdFor(input.conversationId),\n );\n const entries = projectFlueHistoryForSweep(\n await session.historyReader.peek(input.conversationId),\n );\n return decideAskReplyAdmission(\n pendingAskAffordanceId(entries),\n input.ask.toolCallId,\n );\n },\n // The admitted answer is a fresh user dispatch (spec §7.4); the binding\n // binds it to the pending affordance, making it the user-affordance reply.\n run: (input, emit) =>\n streamElicitorTurn(\n input.conversationId,\n { message: input.ask.answer, idempotencyKey: input.idempotencyKey },\n emit,\n ),\n },\n});\n \"text\" in part &&\n typeof part.text === \"string\",\n )\n .map((part) => part.text)\n .join(\"\");\n return text.length > 0 ? text : undefined;\n};\n\ntype ParsedTransportRequest =\n | { readonly kind: \"initial\"; readonly value: HarnessTurnInput }\n | { readonly kind: \"ask-reply\"; readonly value: HarnessAskReplyInput }\n | { readonly kind: \"refused\"; readonly refusal: TransportRequestRefusal };\n\nconst isAnsweredAskPart = (\n part: NonNullable<PanelMessage[\"parts\"]>[number],\n): boolean =>\n ((part.type === \"dynamic-tool\" && part.toolName === ASK_TOOL_NAME) ||\n part.type === `tool-${ASK_TOOL_NAME}`) &&\n part.state === \"output-available\";\n\n/**\n * Classify one tool-result follow-up POST. A human answer submitted through\n * the registered ask component travels tool-output-shaped but is not a\n * machine tool result: exactly one submitted `brunch_ask` output on the\n * referenced assistant message is a candidate reply. Everything else —\n * Petrinaut mutation outputs, the synthetic diagnostics message — remains\n * the machine-input protocol this transport still refuses (FE-1438 owns it).\n */\nconst parseAskReplyTurn = (body: PanelPostBody): ParsedTransportRequest => {\n if (\n typeof body.id !== \"string\" ||\n body.id.length === 0 ||\n typeof body.messageId !== \"string\" ||\n body.messageId.length === 0 ||\n body.trigger !== \"submit-message\" ||\n !Array.isArray(body.messages)\n ) {\n return {\n kind: \"refused\",\n refusal: transportRequestRefusals.invalidChatRequest,\n };\n }\n\n const message = body.messages.find(\n (candidate) =>\n candidate.id === body.messageId && candidate.role === \"assistant\",\n );\n const askParts = (message?.parts ?? []).filter(isAnsweredAskPart);\n if (askParts.length === 0) {\n return {\n kind: \"refused\",\n readonly type: \"ask-reply-admitted\";\n readonly requestId: string;\n readonly conversationId: string;\n readonly toolCallId: string;\n }\n | {\n readonly type: \"ask-reply-refused\";\n readonly requestId: string;\n readonly conversationId: string;\n readonly toolCallId: string;\n readonly reason: \"no-pending-ask\" | \"different-ask-pending\";\n };\n\nexport interface AiSdkChatHandlerOptions {\n readonly runTurn: HarnessTurnRunner;\n /**\n * Ask-return support. Absent, every tool-result follow-up stays refused\n * (the FE-1436 negative contract); present, exactly the pending ask's\n * correlated submission resumes the conversation.\n */\n readonly askReply?: AskReplyHandler;\n /** Exact browser origins allowed to call this endpoint across origins. */\n readonly allowedOrigins?: readonly string[];\n /** Opt-in diagnostic sink. Events are metadata only and never re-enter the conversation. */\n readonly inspect?: (event: TransportInspectionEvent) => void;\n}\n\nconst panelPartSchema = v.looseObject({\n type: v.optional(v.unknown()),\n text: v.optional(v.unknown()),\n toolName: v.optional(v.unknown()),\n toolCallId: v.optional(v.unknown()),\n state: v.optional(v.unknown()),\n output: v.optional(v.unknown()),\n});\n\nconst panelMessageSchema = v.looseObject({\n id: v.optional(v.unknown()),\n role: v.optional(v.unknown()),\n parts: v.optional(v.array(panelPartSchema)),\n});\n\nconst panelPostBodySchema = v.looseObject({\n id: v.optional(v.unknown()),\n messageId: v.optional(v.unknown()),\n messages: v.optional(v.array(panelMessageSchema)),\n trigger: v.optional(v.unknown()),\n});\n\ntype PanelMessage = v.InferOutput<typeof panelMessageSchema>;\ntype PanelPostBody = v.InferOutput<typeof panelPostBodySchema>;\n\ntype TransportRequestRefusal =\n | {\n readonly reason: \"invalid-chat-request\";\n readonly status: 400;\n readonly error: \"invalid_chat_request\";\n }\n | {\n readonly reason: \"tool-result-follow-up-not-supported\";\n readonly status: 422;\n readonly error: \"tool_result_follow_up_not_supported\";\n }\n | {\n readonly reason: \"invalid-ask-submission\";\n readonly status: 400;\n readonly error: \"invalid_ask_submission\";\n };\n\nconst transportRequestRefusals = {\n invalidChatRequest: {\n reason: \"invalid-chat-request\",\n status: 400,\n error: \"invalid_chat_request\",\n },\n toolResultFollowUpNotSupported: {\n reason: \"tool-result-follow-up-not-supported\",\n status: 422,\n error: \"tool_result_follow_up_not_supported\",\n },\n invalidAskSubmission: {\n reason: \"invalid-ask-submission\",\n status: 400,\n error: \"invalid_ask_submission\",\n },\n} as const satisfies Record<string, TransportRequestRefusal>;\n\nconst askReplyRefusalErrors = {\n \"no-pending-ask\": \"ask_not_pending\",\n \"different-ask-pending\": \"ask_mismatch\",\n} as const;\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CeZZ8wgHAAtTub6xUHz` - -``` -{ - "output": "/**\n * `@hashintel/brunch-agent-plugin-gherkin` — the gherkin target-domain (spec §13.1).\n *\n * The tracer target: cheap enough to wire end-to-end first, and deliberately\n * trivial, so it must not be the plugin that freezes the contract (spec §13's\n * two-targets-on-each-axis rule). Its packs, `project`, and `validate` land\n * with their own slice.\n *\n * **This package resolves `@hashintel/brunch-agent` and nothing else** — never the binding,\n * never Flue. Target policy has no business knowing which substrate it is\n * running on, and it is storage-blind besides (spec §9.6).\n */\n\nimport * as v from \"valibot\";\n\nimport { definePlugin } from \"@hashintel/brunch-agent\";\n\nconst nonEmptyString = v.pipe(v.string(), v.nonEmpty());\nconst evidenceQuote = v.strictObject({ excerpt: nonEmptyString });\n\nconst StatementNotedProposal = v.pipe(\n v.strictObject({\n evidence: v.pipe(v.array(evidenceQuote), v.minLength(1)),\n epistemicStatus: v.literal(\"explicit\"),\n confidence: v.picklist([\"firm\", \"hedged\", \"speculative\"]),\n content: v.strictObject({\n value: v.strictObject({\n type: v.literal(\"statement-noted\"),\n interior: v.strictObject({ verbatim: nonEmptyString }),\n }),\n }),\n }),\n v.check(\n (proposal) =>\n proposal.evidence.some(\n (evidence) =>\n evidence.excerpt === proposal.content.value.interior.verbatim,\n ),\n \"The verbatim interior must equal one cited user quote.\",\n ),\n);\n\nexport const gherkin = definePlugin({\n name: \"plugin-gherkin\",\n targetDomain: \"gherkin\",\n proposalCatalog: [\n {\n name: \"statement-noted\",\n description:\n \"Record one condition-shaped statement at the verbatim grade floor, with no parsed structure.\",\n schema: StatementNotedProposal,\n },\n ],\n});\n\"use agent\";\n/**\n * The gherkin elicitor (spec §12.5: one agent per target).\n *\n * Named as a noun — the thing, not the act — and read target-first, so the\n * family sorts together as targets multiply: `gherkin-elicitor`,\n * `assurance-elicitor`.\n *\n * The product is the harness library in a thin host-authored agent — Flue's\n * build-time scan makes the alternative structurally unavailable, since a\n * library cannot ship a pre-registered agent (spec §12.1). So this module is\n * deliberately thin: it mounts harness capability and holds no elicitation\n * semantics of its own.\n *\n * Three recorded Flue constraints are honoured here by construction (spec §10):\n * the `'use agent'` directive is the file's first statement; `agentName` is a\n * pinned string literal, because conversation storage keys on it; and the tool\n * set is static, because prompt-cache economics forbid per-question tool\n * swapping.\n */\n\nimport { useInitialData, useModel, type AgentProps } from \"@flue/runtime\";\nimport * as v from \"valibot\";\n\nimport { useElicitation } from \"@hashintel/brunch-agent-binding-flue\";\nimport { gherkin } from \"@hashintel/brunch-agent-plugin-gherkin\";\n\nimport { createGherkinElicitationSession } from \"../elicitation-session.ts\";\n\n/**\n * One definition for the agent and the faux provider alike: the two must name\n * the same model id, and drift fails at resolution only if both sides resolve\n * the same string (Flue patterns audit, 2026-08-17).\n */\nexport const GHERKIN_MODEL_ID = \"claude-haiku-4-5\";\n\nexport function GherkinElicitor(props: AgentProps) {\n useModel(`anthropic/${GHERKIN_MODEL_ID}`);\n const initialData = useInitialData<{ targetDocumentId: string }>();\n return useElicitation(\n gherkin,\n createGherkinElicitationSession(props.id, initialData.targetDocumentId),\n );\n}\n\n/**\n * Pinned, and never to be edited: conversation storage keys on this literal,\n * so changing it orphans every existing conversation. Flue requires a string\n * literal here because build targets derive durable identifiers from it before\n * any user code runs.\n *\n * Product-prefixed on purpose, and this is the one place the prefix is not\n * cosmetic. Agent identities are global per application, and the September\n * demo shell is chartered to mount this library alongside the Petrinaut\n * libraries — a bare `gherkin-elicitor` could collide with another library's\n * agent, and the collision would land on durable conversation storage.\n *\n * The exported symbol stays the shorter `GherkinElicitor` because it reads\n * better at the mount site; `agentName` exists precisely to let durable\n * identity and source-level name differ.\n */\nGherkinElicitor.agentName = \"brunch-gherkin-elicitor\";\n\n/**\n * Session→document binding (spec §9.1, adjudication L4): a new session's\n * `initialData` carries the target-document id, validated once at creation and\n * immutable thereafter — Flue's own lane for a target descriptor. Dispatching\n * to an existing conversation id resumes that session against the current state\n * of its target-document.\n */\nGherkinElicitor.initialData = v.object({\n targetDocumentId: v.pipe(v.string(), v.nonEmpty()),\n});\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,275p' packages/binding-flue/src/index.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CeZeseLZ2GAIwyJ0ck0` - -``` -{ - "output": "/**\n * `@hashintel/brunch-agent-binding-flue` — the Flue binding.\n *\n * One binding per substrate. It implements the substrate-capability list\n * (spec §10), owns the local capture-store/session-log storage-port\n * implementation (spec §9.6), and is the\n * only shell allowed to know Flue's dialect: **the harness imports no\n * substrate; a binding imports both** (spec §4).\n *\n * Every time mechanism wants to land in here, the second-binding test applies\n * (spec §14.2): genuinely substrate-specific, or mechanism leaking into Flue's\n * dialect?\n */\n\nimport {\n useAgentFinish,\n useAgentStart,\n useDataWriter,\n useDelivery,\n usePersistentState,\n useTool,\n} from \"@flue/runtime\";\nimport * as v from \"valibot\";\n\nimport {\n ASK_TOOL_DESCRIPTION,\n AskInput,\n FreeTextAffordance,\n advanceSweepHighWater,\n askProtocolInstructionFragments,\n buildSettlementCheckSignal,\n buildReplyBindingSignalPayload,\n buildSweepExtractionPrompt,\n buildSweepRepairSignal,\n computeUnaccountedAskAdvisories,\n createSweepExtractionResultSchema,\n createInitialSweepState,\n decidePendingAffordance,\n decideSettlementTrigger,\n mintAskAffordance,\n parseSweepState,\n pendingSweepRepair,\n reopenSweepAfterRefusal,\n settlementProtocolInstructionFragments,\n sweepableRange,\n toolName,\n type CaptureStore,\n type FreeTextAffordanceValue,\n type Plugin,\n type SweepState,\n} from \"@hashintel/brunch-agent\";\n\nimport { capturedUserEntryIdsForSession } from \"./capture-accounting\";\nimport {\n projectFlueHistoryForSweep,\n type FlueHistoryReader,\n} from \"./history-reader\";\n\nconst SweepToolOutput = v.looseObject({\n status: v.picklist([\"no-settled-range\", \"refused\", \"applied\"]),\n});\n\nexport { CAPABILITIES, type Capability, type Provision } from \"./capabilities\";\nexport {\n createFlueHistoryReader,\n projectFlueHistoryForSweep,\n type FlueHistoryReaderOptions,\n} from \"./history-reader\";\nexport {\n createFlueReplyProjector,\n type FlueReplyProjector,\n type FlueReplyProjectorOptions,\n} from \"./reply-projector\";\nexport { createLocalCaptureStore } from \"./local-capture-store\";\n\nexport interface ElicitationSession {\n readonly sessionId: string;\n readonly captureStore: CaptureStore;\n readonly historyReader: FlueHistoryReader;\n}\n\n/**\n * Mount the elicitation harness in a Flue agent.\n *\n * Flue has no ask-the-user primitive, so the harness owns the turn-suspension\n * protocol: a `terminate: true` ask tool, the pending affordance in\n * per-session state, and the answer arriving as a fresh dispatch (spec §7.4).\n */\nexport function useElicitation(\n plugin: Plugin,\n session: ElicitationSession,\n): string {\n const delivery = useDelivery();\n const [pending, setPending] =\n usePersistentState<FreeTextAffordanceValue | null>(\n \"pendingAffordance\",\n null,\n );\n const [storedSweepState, setSweepState] = usePersistentState<SweepState>(\n \"sweepHighWater\",\n createInitialSweepState(),\n );\n let pendingAtFinish = pending;\n let sweepState = parseSweepState(storedSweepState);\n const extractionResult = createSweepExtractionResultSchema(plugin);\n const writeAffordance = useDataWriter(\"affordance\", {\n schema: FreeTextAffordance,\n });\n\n useAgentStart((ctx) => {\n if (delivery.kind !== \"user\" || pending === null) return;\n\n pendingAtFinish = null;\n setPending(null);\n ctx.append({ kind: \"signal\", ...buildReplyBindingSignalPayload(pending) });\n });\n\n useTool({\n name: toolName(\"ask\"),\n description: ASK_TOOL_DESCRIPTION,\n input: AskInput,\n output: FreeTextAffordance,\n run({ data, toolCallId }) {\n const affordance = mintAskAffordance(data.question, toolCallId);\n\n setPending((current) => {\n const decision = decidePendingAffordance(current, affordance);\n if (!decision.ok) throw new Error(decision.reason);\n pendingAtFinish = decision.pending;\n return decision.pending;\n });\n writeAffordance(affordance);\n\n return { output: affordance, terminate: true };\n },\n });\n\n useTool({\n name: toolName(\"sweep\"),\n description:\n \"Apply or replay the settled conversation prefix. The harness privately extracts quote-anchored captures, refreshes durable history immediately before atomic application, and advances sweep state only on success.\",\n input: v.strictObject({}),\n output: SweepToolOutput,\n harness: true,\n durable: true,\n async run({ harness, signal, step }) {\n const historyAtJudgment = await step.do(\"read-settled-range\", async () =>\n projectFlueHistoryForSweep(\n await session.historyReader.peek(session.sessionId),\n ),\n );\n const range = sweepableRange(historyAtJudgment);\n const throughUserEntryId = range.at(-1)?.id;\n if (!throughUserEntryId) {\n return { output: { status: \"no-settled-range\" as const } };\n }\n\n const extraction = await step.do(\n \"extract-sweep-proposals\",\n async () =>\n (\n await harness.prompt(\n buildSweepExtractionPrompt(\n {\n targetDomain: plugin.targetDomain,\n proposalNames: plugin.proposalCatalog.map(\n (proposal) => proposal.name,\n ),\n },\n range,\n ),\n { result: extractionResult, signal },\n )\n ).data,\n );\n\n // This read is intentionally adjacent to application: its binding-owned\n // archive write makes every quote resolvable before the store sees it.\n await step.do(\"refresh-history-before-apply\", async () =>\n projectFlueHistoryForSweep(\n await session.historyReader.read(session.sessionId),\n ),\n );\n const applied = await step.do(\"apply-sweep\", () =>\n session.captureStore.execute(\n {\n type: \"apply-sweep\",\n // The plugin schema narrows the existing envelope here; the store\n // repeats envelope validation and owns anchoring at apply.\n proposals: extraction.proposals,\n },\n { sessionId: session.sessionId },\n ),\n );\n if (!applied.ok) {\n sweepState = reopenSweepAfterRefusal(sweepState);\n setSweepState(sweepState);\n return {\n output: { status: \"refused\" as const, refusal: applied.refusal },\n };\n }\n\n sweepState = advanceSweepHighWater(sweepState, throughUserEntryId);\n setSweepState(sweepState);\n const accountedEntryIds = await capturedUserEntryIdsForSession(\n session.captureStore,\n applied.snapshot,\n session.sessionId,\n );\n return {\n output: {\n status: \"applied\" as const,\n appliedCaptureIds:\n \"appliedCaptureIds\" in applied.value\n ? applied.value.appliedCaptureIds\n : [],\n skippedDedupKeys:\n \"skippedDedupKeys\" in applied.value\n ? applied.value.skippedDedupKeys\n : [],\n advisories: [\n ...(\"advisories\" in applied.value ? applied.value.advisories : []),\n ...computeUnaccountedAskAdvisories(range, accountedEntryIds),\n ],\n },\n };\n },\n });\n\n useAgentFinish(async (ctx) => {\n // useAgentFinish also fires on terminate:true asks. The callback's local\n // view is updated by ask/reply callbacks in this render, so it observes the\n // live slot rather than the render-time persistent-state snapshot.\n if (pendingAtFinish !== null) return;\n\n const entries = projectFlueHistoryForSweep(\n await session.historyReader.peek(session.sessionId),\n );\n const repair = pendingSweepRepair(entries);\n if (repair) {\n ctx.append({ kind: \"signal\", ...buildSweepRepairSignal(repair) });\n return;\n }\n const decision = decideSettlementTrigger({\n entries,\n state: sweepState,\n pendingAffordance: false,\n });\n if (decision.action !== \"nudge\") return;\n\n sweepState = decision.nextState;\n setSweepState(sweepState);\n ctx.append({\n kind: \"signal\",\n ...buildSettlementCheckSignal(decision.tail),\n });\n });\n\n return [\n ...askProtocolInstructionFragments(plugin.targetDomain),\n ...settlementProtocolInstructionFragments(),\n ].join(\"\\n\\n\");\n}\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `apply_patch` - -```json -{ - "patchText": "*** Begin Patch\n*** Add File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n+# Brunch steering model\n+\n+This is the current strategic understanding of Brunch: what outcome matters now, what the system\n+can actually do, which beliefs remain weak, and where to spend the next unit of time. Its immediate\n+decision horizon is the September demo, but it steers the whole context rather than one package or\n+one Linear map.\n+\n+This is not a roadmap, a specification, or an issue mirror. Specifications and accepted ADRs define\n+obligations and settled architecture. Linear is canonical for issue state, hierarchy, and hard\n+blockers. [COORDINATION](./COORDINATION.md) projects this strategy onto the mechanically available\n+work. This document owns the judgment between them: which objective to pursue under the current\n+time, evidence, risk, and product pressure; what to defer; and what new evidence would change that\n+choice. It may expose a conflict with a specification or ADR, but cannot silently override one.\n+\n+The document remains live. A steering pass orients from current evidence, chooses a frontier,\n+executes to a named proof, reconciles the result with specifications and issues, then replans only\n+where the evidence changed. Git carries the history; this file carries only the current model. Its\n+shape is intentionally specific to the present effort. Do not extract a generic template or skill\n+until a second real planning cycle shows which parts recur.\n+\n+## The September outcome\n+\n+As of **2026-08-24**, the working constraint is roughly two human weeks. The event date and final\n+business use case are not yet recorded here; Dora's confirmation is a decision gate, not an excuse\n+to leave the technical spine vague.\n+\n+FE-1476 (the September demo delivery) supplies the working scenario:\n+\n+1. A reviewer opens a prebuilt cyber-physical-process requirements model and its generated SDCPN in\n+ Petrinaut.\n+2. The reviewer selects or describes a net element and asks why it was modelled that way.\n+3. Brunch traces the answer through the requirements model and captures to an exact source\n+ utterance.\n+4. The reviewer scopes one correction and conducts three to five focused chat turns.\n+5. New or superseding captures change the elicited model; reprojection changes the corresponding\n+ part of the live net without rebuilding unrelated parts.\n+6. The resulting artifact is handed to the existing optimisation experiment flow.\n+\n+This is a **review-and-revise** demonstration. It does not need to prove that Brunch can elicit an\n+entire CPS model from a blank conversation. It does need to prove a closed semantic and interaction\n+loop. A chat transcript beside a static fixture, an unexplained net mutation, or a test that injects\n+wiring absent from the deployed entrypoint does not satisfy the outcome.\n+\n+The proof spine is therefore:\n+\n+```text\n+source utterance\n+ -> active typed capture\n+ -> folded CPS requirements model\n+ -> SDCPN element + provenance\n+ -> reviewer question and scoped correction\n+ -> superseding capture\n+ -> changed folded model\n+ -> changed live SDCPN\n+ -> optimisation handoff\n+```\n+\n+## Where the system actually stands\n+\n+The package topology is in place and the implemented tracer is real, but the September loop is not\n+an incremental extension of an almost-finished product. Most of the contract-bearing middle is\n+absent.\n+\n+| Surface | Evidence now | September consequence |\n+| --- | --- | --- |\n+| Ask, suspend, return | A user answer to `brunch_ask` survives the AI SDK/Flue boundary and resumes durable history. | Reuse; do not redesign the ask protocol. |\n+| Settlement and capture | A settled range is privately swept into quote-anchored captures and applied atomically. Supersession and active-head validation exist in the store. | Preserve as the evidence foundation, but expose active state to the controller. |\n+| Plugin SDK | The exported `Plugin` is deliberately only identity plus exactly one proposal type. Gherkin captures one verbatim statement. | There is no implemented fold, demand runner, model, projection, or useful hard-target plugin to extend. |\n+| Elicitation control | The agent receives general ask/sweep instructions. Sweep extraction sees a conversation range and proposal names only. No production path reads the active capture set or a derived model back into the interview. | Brunch cannot yet choose a next question from what it has learned or conduct a targeted correction. |\n+| CPS semantics | The three-register design and provisional two-schema/two-table plugin contract are desk-designed. No `plugin-cps` exists. | The critical semantic path must be built against a concrete CPS case, not inferred from Gherkin completeness. |\n+| Correction | The store can represent supersession, but extraction cannot see active capture IDs, model issues, or the target region; Gherkin cannot propose a supersession. | Targeted re-elicitation is structurally unreachable despite the storage mechanics being present. |\n+| Petrinaut transport | Local panel streaming and human ask-return work. Machine client-tool-result follow-ups are explicitly refused pending FE-1438 (the client-tool round-trip). | The agent cannot yet apply a projection to the live document and receive the result. |\n+| Session target | The current application derives `targetDocumentId` from `conversationId`. | A new reviewer session cannot address a pre-existing elicitation target without changing this identity boundary. |\n+| Demo website | The production website still uses its stock assistant route. The `/brunch` Actual Mode is a separate read-only fixture/SSE surface. | Local tracer proof must not be mistaken for deployed integration. |\n+\n+The decisive reading is that the current design is not too rigorous in its preservation of\n+evidence, correction, or register boundaries. It is too broad and too generic for the remaining\n+time. Completing generic plugin machinery, a second target, a full CPS ontology, and a cold-start\n+interviewer before crossing the real reviewer loop would optimize the library while leaving the\n+demo hollow.\n+\n+## The strategic bet\n+\n+Build the smallest honest **CPS review-and-revise loop** through all three registers and the real\n+Petrinaut entrypoint. Let that concrete implementation discover the minimum plugin interface, then\n+generalize only what the CPS case and existing Gherkin case both need.\n+\n+This is not permission to take another thin tracer as the definition of done. The vertical proof is\n+contract-bearing: it includes model assembly, provenance, targeted correction, reprojection,\n+application, and the deployed route. Breadth inside each layer may be narrow; no layer in that loop\n+may be a fixture masquerading as production wiring.\n+\n+The bet preserves these load-bearing decisions:\n+\n+- Captures remain the durable, source-grounded assertion register.\n+- Every semantic inference happens at write time and is recorded as a contestable capture.\n+- The elicited model is a pure fold over active captures and every model part names its supporting\n+ capture IDs.\n+- SDCPN projection consumes the elicited model without rereading the transcript or making hidden\n+ semantic judgments.\n+- Petrinaut application and diagnostics are separate from semantic projection: the application may\n+ use client tools to apply a projected artifact, but it does not become the authority that invents\n+ the model.\n+- A correction supersedes or adds assertions and re-runs the fold and projection; it does not patch\n+ an unexplained net element directly.\n+\n+FE-1480 (requirements-model-to-SDCPN inference) challenges the third and fourth decisions by\n+assuming the projection itself requires LLM inference. That assumption is unresolved. If a worked\n+CPS case proves that the register-2 model is insufficient for pure projection, the honest choices\n+are to record the missing semantic judgment as a capture before folding or to amend ADR-0003 (the\n+three-register IR) explicitly. Hiding inference inside a read-time projection is not an available\n+shortcut.\n+\n+## The elicitor architecture under this load\n+\n+The discussion began with four parts; the current model has five responsibilities across the\n+harness and plugin layers, plus one per-engagement input. The missing responsibility is the\n+controller that closes the loop between captured evidence and the next move.\n+\n+| Responsibility | Owner | What it contains | State and September obligation |\n+| --- | --- | --- | --- |\n+| Strategy repertoire | Harness | Orientations, motivations, conversational licences, interviewing techniques, and question-formulation guidance. | Partly researched, not operationally selected. Implement only the techniques used by the review-and-revise runbook. |\n+| Evidence engine | Harness | Archive, settlement sweep, quote anchoring, durable captures, issues, conflict, supersession, and provenance primitives. | Strongest implemented layer. Add the active-model/issues read path needed by control and correction; do not broaden storage semantics without evidence. |\n+| Elicitation controller | Harness | Reads the engagement brief, active folded model and issues, current runbook, and strategy repertoire; chooses `ask`, `propose`, `contrast`, `validate`, `project`, `explain`, or `stop`. | Absent. Build the narrow controller loop needed to explain and revise one selected region. |\n+| Domain contract | Plugin | Proposal and model schemas; identity, fold, grade, demand, diagnostics, projection, and provenance rules for one target domain. | Designed but unimplemented. Build the CPS subset exercised by the fixture and correction; let it pressure the generic interface. |\n+| Job runbooks | Plugin | Named jobs over the same domain: objectives, entry conditions, trajectories, demand/completion rules, checks, stopping, revision, boundaries, and handoff. | Absent. Implement `review-and-revise`; defer a complete cold-start runbook. |\n+\n+The **engagement brief** is dynamic input, not plugin policy: target document, participant role,\n+objective, scope, known constraints, allowed actions, and time budget for this run. For September it\n+binds a reviewer to an existing target and one revisable region.\n+\n+A separate free-form “next-question ledger” should not become another authority. Most of it is a\n+derived control trace:\n+\n+```text\n+runbook demand -> model gap or issue -> candidate move -> chosen move -> concrete ask\n+```\n+\n+Persist only what replay, audit, or explicit user commitment requires. The controller must be able\n+to explain its chosen move from the runbook and active model; it must not accumulate an independent\n+shadow plan.\n+\n+The September `review-and-revise` runbook is provisionally:\n+\n+```text\n+entry:\n+ existing target + folded requirements model + projected net + reviewer scope\n+trajectory:\n+ orient -> select -> explain provenance -> frame correction\n+ -> ask/validate (3-5 turns) -> show semantic and net delta -> confirm -> hand off\n+done:\n+ scoped demands are met at the declared grade\n+ no open conflict blocks the selected projection\n+ reviewer confirms the intended delta\n+ every changed net element retains provenance\n+boundary:\n+ do not expand into cold-start elicitation or unrelated net repair\n+```\n+\n+## Proof frontiers and execution order\n+\n+The work has four frontiers. They are ordered by learning dependency, not by which ticket is\n+currently unblocked. The semantic and experience lanes start in parallel after Frontier 0, then\n+join as early as possible; they are not two long independent streams to integrate at the end.\n+\n+### Frontier 0 — make the demo claim decidable\n+\n+Confirm the business use case, freeze one representative prebuilt requirements-model/net fixture,\n+and name the optimisation handoff artifact. On that fixture, settle the FE-1480 authority question:\n+which steps are write-time semantic capture, pure model fold, pure SDCPN projection, and document\n+application?\n+\n+**Proof:** one reviewed worked transformation in which every SDCPN element needed by the scenario\n+traces to model fields and captures, with every non-mechanical judgment assigned to a write-time\n+producer. If this cannot be drawn honestly, implementation should not freeze an interface.\n+\n+### Frontier 1 — close the CPS semantic loop\n+\n+Implement only the CPS proposal kinds, model slots, identity/fold rules, demands, projection, and\n+provenance exercised by the fixture and one realistic correction. Carry capture IDs through every\n+derived layer. Make active model issues and selected-region context available to the controller.\n+\n+**Proof:** from the production fold/projection APIs, one source-grounded supersession changes the\n+expected model field and corresponding SDCPN elements, leaves an unrelated region stable, and\n+answers both forward and reverse provenance queries. A YAML or Markdown rendering of the model is\n+enough for inspection at this frontier.\n+\n+### Frontier 2 — close the reviewer control loop\n+\n+Allow a new conversation to bind to an existing target document. Admit the machine client-tool\n+results needed to apply and diagnose a net change. Mount the narrow `review-and-revise` runbook and\n+controller so that the active model and selected region, rather than the raw transcript alone,\n+drive three to five questions.\n+\n+**Proof:** through the real Brunch HTTP handler and Petrinaut panel, a reviewer selects the prepared\n+region, receives a grounded explanation, submits a scoped correction, and sees the returned apply\n+result resume the same durable session. No test-only injection supplies the target or tool wiring.\n+\n+### Frontier 3 — converge on the deployed demo\n+\n+Wire provider/mode routing, browser principal and private session lookup, remote transport,\n+deployment gates, and the optimisation handoff. Rehearse the exact scenario with a clean browser\n+against the deployed demo surface.\n+\n+**Proof:** a screen-recordable run completes the six September beats, survives one reload, exposes\n+the before/after requirements-model delta, and hands the resulting SDCPN to the optimisation flow.\n+Diagnostics show the source capture and projection identities needed to investigate a failure.\n+\n+## What is deliberately cut\n+\n+Until the proof spine is closed:\n+\n+- Do not freeze a broad declarative plugin SDK or require a second hard target. Extract the shared\n+ contract after CPS has stressed it.\n+- Do not make the Gherkin artifact path a prerequisite for the CPS demo.\n+- Do not build a full requirements-graph UI. FE-1481's YAML or Markdown export is the selected\n+ fallback; a UI earns time only if the core loop is already green.\n+- Do not build a complete cold-start CPS interview, general target gallery, every affordance type,\n+ voice input, surprising-scenario generation, or broad telemetry vocabulary.\n+- Do not implement a comprehensive CPS ontology. Support the fixture, the correction, and the\n+ optimisation handoff while keeping the data model honest about what it omits.\n+- Do not bypass provenance or write-time semantics to make a visually convincing net mutation.\n+\n+These are sequencing cuts, not claims that the deferred obligations are unimportant.\n+\n+## Issue projection\n+\n+The PM-authored issues are adopted here as the September delivery decomposition. Linear has not yet\n+been changed; its current unparented state is recorded in COORDINATION until an explicitly approved\n+registry update. The recommended hierarchy is FE-1357 (September planning and plugin design) →\n+FE-1476 (September delivery) → FE-1477 through FE-1482.\n+\n+| Issue | Strategic role | Reconciliation with existing work |\n+| --- | --- | --- |\n+| FE-1476 — prepare the September demo | Outcome owner and acceptance narrative. | Child of FE-1357 while that map remains active; owns rehearsal and handoff rather than implementation details. |\n+| FE-1477 — route Petrinaut AI and Brunch | Experience-lane entry and mode selection. | Product acceptance overlaps FE-1440 (ship the elicitor in the demo site). Keep one implementation owner; do not build two switches. |\n+| FE-1478 — trace a generated net to requirements | Provenance acceptance through registers 3 → 2 → 1 → utterance. | Must shape Frontier 1 from its first model/projection types, not arrive as post-hoc metadata. |\n+| FE-1479 — targeted re-elicitation | Convergence issue for the reviewer loop. | Consumes FE-1438's machine client-tool/application path, FE-1439's session ownership, and the CPS correction path; it does not own a second mutation mechanism. |\n+| FE-1480 — infer requirements model to SDCPN | Authority and projection decision, then the production projector. | Must be reconciled with ADR-0003 before implementation. FE-1438 owns browser application, not hidden semantic projection. |\n+| FE-1481 — expose the requirements model | Inspection fallback and demo delta surface. | Select YAML/Markdown first. Defer FE-1442's broader live capture/completion UI unless the proof spine closes early. |\n+| FE-1482 — add the CPS plugin | Semantic-lane owner and concrete pressure on the plugin boundary. | Pulls the demo-critical slices from FE-1402 (completion), FE-1403 (CPS guidance), FE-1406 (strategies), and FE-1431 (declarative contract). FE-1393 remains the generic/Gherkin path and no longer gates September. |\n+\n+Other consequences for the old graph:\n+\n+- FE-1387 (second target and plugin-contract freeze) follows the CPS proof instead of preceding the\n+ demo.\n+- FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n+ scenario.\n+- FE-1438, FE-1439, FE-1440, FE-1423 (pre-remote gates), and FE-1441 (deployment) remain real\n+ implementation obligations; the new issues state user outcomes rather than replacing these\n+ substrate and release seams.\n+- FE-1402, FE-1403, FE-1406, and FE-1431 should produce only what the CPS runbook and domain\n+ contract consume. Their old standalone completion must not become a hidden prerequisite.\n+\n+## Beliefs, risks, and replan conditions\n+\n+| Current belief | Confidence and evidence | Replan when |\n+| --- | --- | --- |\n+| A bounded review-and-revise scenario can carry the September product claim without cold-start elicitation. | Medium. It is the written FE-1476 scenario, but Dora has not confirmed the use case. | The confirmed use case requires model creation rather than review, or the optimisation handoff requires fields absent from the fixture. |\n+| A concrete CPS implementation will discover a better minimum plugin contract faster than completing the generic design first. | Medium-high. Gherkin deliberately under-stresses the interface; CPS is the first real consumer. | The first worked CPS transformation cannot be expressed without a reusable harness primitive that must precede it. Build that primitive, then return immediately to the vertical proof. |\n+| Register 2 can be rich enough for pure SDCPN projection. | Low-medium. ADR-0003 requires it, but no real fold or projector exists and FE-1480 asserts non-determinism. | The worked transformation identifies an unavoidable semantic choice not represented in captures/model. Record it earlier or explicitly revisit the ADR. |\n+| Three to five turns can produce a meaningful scoped correction. | Low. No CPS runbook has been rehearsed. | Two rehearsals exceed the budget or require unrelated context. Narrow the region, preload explicit context, or revise the demo claim rather than script fake success. |\n+| The proven ask suspension can extend to document-application client tools. | Medium-low. The suspension spike succeeded, but the production transport intentionally refuses machine tool-result follow-ups. | The first tool round-trip cannot preserve correlation, durability, or non-user evidence semantics. Treat this as a critical integration blocker, not a UI detail. |\n+| The production website and remote server can be joined inside the timebox. | Medium-low. Local host seams exist; provider routing, session ownership, deployment, and release gates do not yet converge. | A deployable path is not proved by the end of Frontier 2. Preserve the real semantic loop and seek an explicit demo-surface decision rather than quietly falling back to test-only wiring. |\n+| Requirements-model UI is unnecessary for comprehension. | Medium. The reviewer needs inspectability, but FE-1481 explicitly permits export. | Rehearsal shows that provenance and delta cannot be understood from chat plus a structured export. |\n+\n+## Current choice\n+\n+Stop treating “unblocked” as “next.” The next strategic move is to run Frontier 0 immediately and\n+open Frontiers 1 and 2 in parallel: one worked CPS semantic slice and one existing-target reviewer\n+session/tool-return slice. Join them at the earliest correction, then drive the same bones through\n+provider routing and deployment. The generic Gherkin/plugin-freeze path and broad UI work wait for\n+that join.\n+\n+Revisit this choice as soon as Dora confirms the use case, the FE-1480 worked transformation lands,\n+or either parallel frontier fails its first proof. A steering pass that only updates ticket status\n+does not change this document; a new fact that changes the objective, proof spine, authority\n+boundary, or cut line does.\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n@@\n | Document | Status | Linear | Digest |\n | -------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n | [COORDINATION](planning/_shared/COORDINATION.md) | active | cross-project; maintained by arc-close | Current sequencing recommendation, soft cross-map edges, unresolved seams, and exceptional roots; hard blockers, state, and hierarchy remain in Linear |\n | [hash-monorepo-import-plan](planning/_shared/hash-monorepo-import-plan.md) | active until FE-1437 lands | FE-1437 | Native HASH assimilation plan: preserved history and child package workspaces under one Brunch context root, explicit authority cutover, exhaustive repository-material disposition, toolchain port, boundary gates, and verification |\n | [SPEC-LEDGER](planning/_shared/SPEC-LEDGER.md) | active until milestone-one closure | FE-1383 | Obligation-level status and evidence ledger for the elicitation-kernel specification; settles when the milestone closes |\n+| [STEERING](planning/_shared/STEERING.md) | active | FE-1357 / FE-1476 | Current objective-driven strategy for the September CPS review-and-revise proof: evidence, architectural pressure, proof frontiers, cuts, issue projection, risks, and replan conditions |\n | [flue-architecture-cheatsheet](planning/_shared/flue-architecture-cheatsheet.md) | active | commented on FE-1383; feeds docs/agents/flue-routing.md | Architect's consolidation of all 21 Flue guide pages: direct structured generation uses `harness.prompt`; model-delegated work uses `useSubagent`; three-lane boundary summary and ranked divergence risks; reconciled against installed Flue 2.0.3 source |\n | [topology](planning/_shared/topology.md) | active | ratified → ADR-0002; N1 discharged by FE-1422 + FE-1392; local N5 implemented by FE-1391; N3 amended by FE-1437 | Pseudo-style verification of the package/app tree against the three-lane model and spec §12.2: portable ask/sweep protocols, Flue binding wiring, package boundaries, and application-only Brunch–Petrinaut composition |\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@\n-This is the cross-map coordination surface for the `brunch-agent` project. FE-1383 owns the\n-milestone-one harness build; FE-1357 owns the September demo and process-model plugin design.\n-Linear is canonical for issue state, parentage, and hard `blocks` relations. This file owns\n-only the judgment Linear cannot express: the current project-wide recommendation, soft edges,\n-unresolved seams, and exceptional roots.\n+This is the cross-map coordination surface for the `brunch-agent` project. FE-1383 owns the\n+milestone-one harness build; FE-1357 owns the September demo and process-model plugin design.\n+Linear is canonical for issue state, parentage, and hard `blocks` relations. The\n+[steering model](./STEERING.md) chooses the current objective, proof frontiers, and cuts under\n+pressure. This file projects that strategy onto mechanically available work: the current\n+project-wide recommendation, soft edges, unresolved seams, and exceptional roots.\n@@\n ## Current sequencing recommendation\n \n-As of **2026-08-21**, the FE-1437 authority cutover has been executed: the full brunch-lite\n-history is imported on `ln/fe-1437-hash-monorepo-import` in `hashintel/hash` (frozen standalone\n-SHA `43a0022918861846344b96a32cb94f92e2ee96ae`), every import gate re-verified. `hashintel/hash`\n-is authoritative; the standalone repository accepts no further implementation work. FE-1437\n-closes when the branch lands on `main` (squash merge, per convention). Part of FE-1440's website\n-wiring (the Brunch interactive-tool panel in `apps/petrinaut-website`) travelled with the import\n-branch; FE-1440 was trimmed on 2026-08-21 to the remaining mode wiring (mode switch, browser\n-identifier bootstrap, remote transport swap). After landing, advance FE-1438 (client-tool round-trip)\n-beside FE-1393 (plugin SDK and first projection); FE-1439 (private durable sessions) proceeds in\n-parallel. The integration stream joins at FE-1440 and deployment follows at FE-1441 (which also\n-waits on FE-1423's pre-exposure gates), while the harness stream reaches its contract-freeze\n-decision at FE-1387. FE-1402/FE-1403 form a parallel content/evaluation stream, without\n-displacing the two convergence edges.\n+As of **2026-08-24**, FE-1476 (the September demo delivery) changes the recommendation from generic\n+package completion to a concrete CPS review-and-revise proof. After FE-1437 (the monorepo import)\n+lands, open two fronts in parallel. The semantic front starts FE-1482 (the CPS plugin) against one\n+worked fixture and settles FE-1480's requirements-model-to-SDCPN authority boundary before it\n+implements a projector; FE-1478 (net-to-requirements provenance) is part of that spine from its\n+first types. The experience front advances FE-1438 (machine client-tool round-trip) and FE-1439\n+(private sessions) far enough for a new reviewer conversation to target an existing document,\n+while FE-1477/FE-1440 share one provider-routing implementation. Join the fronts at FE-1479\n+(targeted re-elicitation), then drive the same path through FE-1423's pre-exposure gates and\n+FE-1441 deployment.\n+\n+FE-1393's generic Gherkin artifact and FE-1387's second-target contract freeze no longer gate the\n+September proof. FE-1402, FE-1403, FE-1406, and FE-1431 supply only the completion, guidance,\n+strategy, and contract slices the CPS `review-and-revise` runbook consumes. FE-1481 selects\n+YAML/Markdown export as the requirements-model inspection floor; broad UI follows only if the\n+closed loop is already proved.\n@@\n nodes:\n FE-1437 [executed, landing] # history imported; HASH authoritative; PR pending\n- FE-1438 [next] # client-tool round-trip\n- FE-1439 [next, parallel] # private durable sessions\n- FE-1440 [join, partly landed] # website elicitor mode; panel wiring on import branch\n- FE-1441 [post-landing] # HASH deployment\n- FE-1393 [next] # plugin SDK + first projection\n- FE-1387 [after-FE-1393] # second pack + contract freeze\n- FE-1395 [coordination] # full affordance set\n- FE-1402 [parallel, content] # completion contract\n- FE-1403 [parallel, content] # interviewing guidance\n- FE-1404 [after-content] # armed baseline\n+ FE-1476 [objective] # September reviewer demo\n+ FE-1482 [next, semantic] # concrete CPS plugin + review/revise runbook\n+ FE-1480 [decision, semantic] # model/projection authority, then projector\n+ FE-1478 [semantic proof] # provenance through all three registers\n+ FE-1438 [next, experience] # machine client-tool round-trip + application\n+ FE-1439 [next, experience] # existing-target reviewer session ownership\n+ FE-1477/FE-1440 [experience] # one provider-routing implementation\n+ FE-1479 [join] # targeted correction changes the live net\n+ FE-1481 [fallback] # structured model export before UI\n+ FE-1441 [deployed proof] # HASH deployment\n \n edges:\n FE-1449 -[hard]-> FE-1438\n- FE-1392 -[hard]-> FE-1393\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n- FE-1393 -[hard]-> FE-1387\n- FE-1402, FE-1403 -[hard]-> FE-1404\n- FE-1395 -[coord]-> FE-1438\n FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n- FE-1387 -[input]-> FE-1440\n+ FE-1480 decision -[input]-> FE-1482, FE-1478\n+ FE-1402, FE-1403,\n+ FE-1406, FE-1431 -[input]-> FE-1482\n+ FE-1482, FE-1478,\n+ FE-1438, FE-1439 -[input]-> FE-1479\n+ FE-1479, FE-1440 -[input]-> FE-1441\n ```\n@@\n ## Open seams\n \n-- **Contract freeze — FE-1387.** The process-model target must stress the plugin contract before\n- it freezes. FE-1393 makes the contract programmable; FE-1387 remains the cross-map hinge.\n+- **Projection authority — FE-1480.** The ticket assumes non-deterministic LLM inference from the\n+ requirements model to SDCPN, while ADR-0003 requires write-time-only semantic inference and a\n+ pure projection. A worked CPS transformation must assign every judgment to capture, fold,\n+ projection, or document application before the interface freezes.\n+- **Controller and runbook.** The harness does not read the folded model or open issues back into\n+ the agent, and no plugin defines a job trajectory or stopping rule. FE-1482 must exercise the\n+ narrow `review-and-revise` loop; FE-1406 and FE-1402/FE-1403 are inputs, not parallel products.\n+- **Reviewer target identity — FE-1439 × FE-1479.** The current host derives target-document\n+ identity from conversation identity. September requires a new reviewer conversation against an\n+ existing target without weakening owner isolation.\n+- **Contract freeze — FE-1387.** The CPS target must stress the plugin contract before it freezes.\n+ The freeze follows the September semantic proof rather than gating it.\n@@\n ## Exceptional roots\n@@\n - **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n+- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n+ map has been chosen.\n+- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n+ under FE-1357.\n+- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n+ steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n+ ownership boundaries are recorded in STEERING's issue projection before external mutation.\n*** End Patch" -} -``` - -## User - -**Tool Result:** `TU-034CebqF6NebEylhcnTlux` - -``` -{ - "files": [ - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n@@ -0,0 +1,278 @@\n+# Brunch steering model\n+\n+This is the current strategic understanding of Brunch: what outcome matters now, what the system\n+can actually do, which beliefs remain weak, and where to spend the next unit of time. Its immediate\n+decision horizon is the September demo, but it steers the whole context rather than one package or\n+one Linear map.\n+\n+This is not a roadmap, a specification, or an issue mirror. Specifications and accepted ADRs define\n+obligations and settled architecture. Linear is canonical for issue state, hierarchy, and hard\n+blockers. [COORDINATION](./COORDINATION.md) projects this strategy onto the mechanically available\n+work. This document owns the judgment between them: which objective to pursue under the current\n+time, evidence, risk, and product pressure; what to defer; and what new evidence would change that\n+choice. It may expose a conflict with a specification or ADR, but cannot silently override one.\n+\n+The document remains live. A steering pass orients from current evidence, chooses a frontier,\n+executes to a named proof, reconciles the result with specifications and issues, then replans only\n+where the evidence changed. Git carries the history; this file carries only the current model. Its\n+shape is intentionally specific to the present effort. Do not extract a generic template or skill\n+until a second real planning cycle shows which parts recur.\n+\n+## The September outcome\n+\n+As of **2026-08-24**, the working constraint is roughly two human weeks. The event date and final\n+business use case are not yet recorded here; Dora's confirmation is a decision gate, not an excuse\n+to leave the technical spine vague.\n+\n+FE-1476 (the September demo delivery) supplies the working scenario:\n+\n+1. A reviewer opens a prebuilt cyber-physical-process requirements model and its generated SDCPN in\n+ Petrinaut.\n+2. The reviewer selects or describes a net element and asks why it was modelled that way.\n+3. Brunch traces the answer through the requirements model and captures to an exact source\n+ utterance.\n+4. The reviewer scopes one correction and conducts three to five focused chat turns.\n+5. New or superseding captures change the elicited model; reprojection changes the corresponding\n+ part of the live net without rebuilding unrelated parts.\n+6. The resulting artifact is handed to the existing optimisation experiment flow.\n+\n+This is a **review-and-revise** demonstration. It does not need to prove that Brunch can elicit an\n+entire CPS model from a blank conversation. It does need to prove a closed semantic and interaction\n+loop. A chat transcript beside a static fixture, an unexplained net mutation, or a test that injects\n+wiring absent from the deployed entrypoint does not satisfy the outcome.\n+\n+The proof spine is therefore:\n+\n+```text\n+source utterance\n+ -> active typed capture\n+ -> folded CPS requirements model\n+ -> SDCPN element + provenance\n+ -> reviewer question and scoped correction\n+ -> superseding capture\n+ -> changed folded model\n+ -> changed live SDCPN\n+ -> optimisation handoff\n+```\n+\n+## Where the system actually stands\n+\n+The package topology is in place and the implemented tracer is real, but the September loop is not\n+an incremental extension of an almost-finished product. Most of the contract-bearing middle is\n+absent.\n+\n+| Surface | Evidence now | September consequence |\n+| --- | --- | --- |\n+| Ask, suspend, return | A user answer to `brunch_ask` survives the AI SDK/Flue boundary and resumes durable history. | Reuse; do not redesign the ask protocol. |\n+| Settlement and capture | A settled range is privately swept into quote-anchored captures and applied atomically. Supersession and active-head validation exist in the store. | Preserve as the evidence foundation, but expose active state to the controller. |\n+| Plugin SDK | The exported `Plugin` is deliberately only identity plus exactly one proposal type. Gherkin captures one verbatim statement. | There is no implemented fold, demand runner, model, projection, or useful hard-target plugin to extend. |\n+| Elicitation control | The agent receives general ask/sweep instructions. Sweep extraction sees a conversation range and proposal names only. No production path reads the active capture set or a derived model back into the interview. | Brunch cannot yet choose a next question from what it has learned or conduct a targeted correction. |\n+| CPS semantics | The three-register design and provisional two-schema/two-table plugin contract are desk-designed. No `plugin-cps` exists. | The critical semantic path must be built against a concrete CPS case, not inferred from Gherkin completeness. |\n+| Correction | The store can represent supersession, but extraction cannot see active capture IDs, model issues, or the target region; Gherkin cannot propose a supersession. | Targeted re-elicitation is structurally unreachable despite the storage mechanics being present. |\n+| Petrinaut transport | Local panel streaming and human ask-return work. Machine client-tool-result follow-ups are explicitly refused pending FE-1438 (the client-tool round-trip). | The agent cannot yet apply a projection to the live document and receive the result. |\n+| Session target | The current application derives `targetDocumentId` from `conversationId`. | A new reviewer session cannot address a pre-existing elicitation target without changing this identity boundary. |\n+| Demo website | The production website still uses its stock assistant route. The `/brunch` Actual Mode is a separate read-only fixture/SSE surface. | Local tracer proof must not be mistaken for deployed integration. |\n+\n+The decisive reading is that the current design is not too rigorous in its preservation of\n+evidence, correction, or register boundaries. It is too broad and too generic for the remaining\n+time. Completing generic plugin machinery, a second target, a full CPS ontology, and a cold-start\n+interviewer before crossing the real reviewer loop would optimize the library while leaving the\n+demo hollow.\n+\n+## The strategic bet\n+\n+Build the smallest honest **CPS review-and-revise loop** through all three registers and the real\n+Petrinaut entrypoint. Let that concrete implementation discover the minimum plugin interface, then\n+generalize only what the CPS case and existing Gherkin case both need.\n+\n+This is not permission to take another thin tracer as the definition of done. The vertical proof is\n+contract-bearing: it includes model assembly, provenance, targeted correction, reprojection,\n+application, and the deployed route. Breadth inside each layer may be narrow; no layer in that loop\n+may be a fixture masquerading as production wiring.\n+\n+The bet preserves these load-bearing decisions:\n+\n+- Captures remain the durable, source-grounded assertion register.\n+- Every semantic inference happens at write time and is recorded as a contestable capture.\n+- The elicited model is a pure fold over active captures and every model part names its supporting\n+ capture IDs.\n+- SDCPN projection consumes the elicited model without rereading the transcript or making hidden\n+ semantic judgments.\n+- Petrinaut application and diagnostics are separate from semantic projection: the application may\n+ use client tools to apply a projected artifact, but it does not become the authority that invents\n+ the model.\n+- A correction supersedes or adds assertions and re-runs the fold and projection; it does not patch\n+ an unexplained net element directly.\n+\n+FE-1480 (requirements-model-to-SDCPN inference) challenges the third and fourth decisions by\n+assuming the projection itself requires LLM inference. That assumption is unresolved. If a worked\n+CPS case proves that the register-2 model is insufficient for pure projection, the honest choices\n+are to record the missing semantic judgment as a capture before folding or to amend ADR-0003 (the\n+three-register IR) explicitly. Hiding inference inside a read-time projection is not an available\n+shortcut.\n+\n+## The elicitor architecture under this load\n+\n+The discussion began with four parts; the current model has five responsibilities across the\n+harness and plugin layers, plus one per-engagement input. The missing responsibility is the\n+controller that closes the loop between captured evidence and the next move.\n+\n+| Responsibility | Owner | What it contains | State and September obligation |\n+| --- | --- | --- | --- |\n+| Strategy repertoire | Harness | Orientations, motivations, conversational licences, interviewing techniques, and question-formulation guidance. | Partly researched, not operationally selected. Implement only the techniques used by the review-and-revise runbook. |\n+| Evidence engine | Harness | Archive, settlement sweep, quote anchoring, durable captures, issues, conflict, supersession, and provenance primitives. | Strongest implemented layer. Add the active-model/issues read path needed by control and correction; do not broaden storage semantics without evidence. |\n+| Elicitation controller | Harness | Reads the engagement brief, active folded model and issues, current runbook, and strategy repertoire; chooses `ask`, `propose`, `contrast`, `validate`, `project`, `explain`, or `stop`. | Absent. Build the narrow controller loop needed to explain and revise one selected region. |\n+| Domain contract | Plugin | Proposal and model schemas; identity, fold, grade, demand, diagnostics, projection, and provenance rules for one target domain. | Designed but unimplemented. Build the CPS subset exercised by the fixture and correction; let it pressure the generic interface. |\n+| Job runbooks | Plugin | Named jobs over the same domain: objectives, entry conditions, trajectories, demand/completion rules, checks, stopping, revision, boundaries, and handoff. | Absent. Implement `review-and-revise`; defer a complete cold-start runbook. |\n+\n+The **engagement brief** is dynamic input, not plugin policy: target document, participant role,\n+objective, scope, known constraints, allowed actions, and time budget for this run. For September it\n+binds a reviewer to an existing target and one revisable region.\n+\n+A separate free-form “next-question ledger” should not become another authority. Most of it is a\n+derived control trace:\n+\n+```text\n+runbook demand -> model gap or issue -> candidate move -> chosen move -> concrete ask\n+```\n+\n+Persist only what replay, audit, or explicit user commitment requires. The controller must be able\n+to explain its chosen move from the runbook and active model; it must not accumulate an independent\n+shadow plan.\n+\n+The September `review-and-revise` runbook is provisionally:\n+\n+```text\n+entry:\n+ existing target + folded requirements model + projected net + reviewer scope\n+trajectory:\n+ orient -> select -> explain provenance -> frame correction\n+ -> ask/validate (3-5 turns) -> show semantic and net delta -> confirm -> hand off\n+done:\n+ scoped demands are met at the declared grade\n+ no open conflict blocks the selected projection\n+ reviewer confirms the intended delta\n+ every changed net element retains provenance\n+boundary:\n+ do not expand into cold-start elicitation or unrelated net repair\n+```\n+\n+## Proof frontiers and execution order\n+\n+The work has four frontiers. They are ordered by learning dependency, not by which ticket is\n+currently unblocked. The semantic and experience lanes start in parallel after Frontier 0, then\n+join as early as possible; they are not two long independent streams to integrate at the end.\n+\n+### Frontier 0 — make the demo claim decidable\n+\n+Confirm the business use case, freeze one representative prebuilt requirements-model/net fixture,\n+and name the optimisation handoff artifact. On that fixture, settle the FE-1480 authority question:\n+which steps are write-time semantic capture, pure model fold, pure SDCPN projection, and document\n+application?\n+\n+**Proof:** one reviewed worked transformation in which every SDCPN element needed by the scenario\n+traces to model fields and captures, with every non-mechanical judgment assigned to a write-time\n+producer. If this cannot be drawn honestly, implementation should not freeze an interface.\n+\n+### Frontier 1 — close the CPS semantic loop\n+\n+Implement only the CPS proposal kinds, model slots, identity/fold rules, demands, projection, and\n+provenance exercised by the fixture and one realistic correction. Carry capture IDs through every\n+derived layer. Make active model issues and selected-region context available to the controller.\n+\n+**Proof:** from the production fold/projection APIs, one source-grounded supersession changes the\n+expected model field and corresponding SDCPN elements, leaves an unrelated region stable, and\n+answers both forward and reverse provenance queries. A YAML or Markdown rendering of the model is\n+enough for inspection at this frontier.\n+\n+### Frontier 2 — close the reviewer control loop\n+\n+Allow a new conversation to bind to an existing target document. Admit the machine client-tool\n+results needed to apply and diagnose a net change. Mount the narrow `review-and-revise` runbook and\n+controller so that the active model and selected region, rather than the raw transcript alone,\n+drive three to five questions.\n+\n+**Proof:** through the real Brunch HTTP handler and Petrinaut panel, a reviewer selects the prepared\n+region, receives a grounded explanation, submits a scoped correction, and sees the returned apply\n+result resume the same durable session. No test-only injection supplies the target or tool wiring.\n+\n+### Frontier 3 — converge on the deployed demo\n+\n+Wire provider/mode routing, browser principal and private session lookup, remote transport,\n+deployment gates, and the optimisation handoff. Rehearse the exact scenario with a clean browser\n+against the deployed demo surface.\n+\n+**Proof:** a screen-recordable run completes the six September beats, survives one reload, exposes\n+the before/after requirements-model delta, and hands the resulting SDCPN to the optimisation flow.\n+Diagnostics show the source capture and projection identities needed to investigate a failure.\n+\n+## What is deliberately cut\n+\n+Until the proof spine is closed:\n+\n+- Do not freeze a broad declarative plugin SDK or require a second hard target. Extract the shared\n+ contract after CPS has stressed it.\n+- Do not make the Gherkin artifact path a prerequisite for the CPS demo.\n+- Do not build a full requirements-graph UI. FE-1481's YAML or Markdown export is the selected\n+ fallback; a UI earns time only if the core loop is already green.\n+- Do not build a complete cold-start CPS interview, general target gallery, every affordance type,\n+ voice input, surprising-scenario generation, or broad telemetry vocabulary.\n+- Do not implement a comprehensive CPS ontology. Support the fixture, the correction, and the\n+ optimisation handoff while keeping the data model honest about what it omits.\n+- Do not bypass provenance or write-time semantics to make a visually convincing net mutation.\n+\n+These are sequencing cuts, not claims that the deferred obligations are unimportant.\n+\n+## Issue projection\n+\n+The PM-authored issues are adopted here as the September delivery decomposition. Linear has not yet\n+been changed; its current unparented state is recorded in COORDINATION until an explicitly approved\n+registry update. The recommended hierarchy is FE-1357 (September planning and plugin design) →\n+FE-1476 (September delivery) → FE-1477 through FE-1482.\n+\n+| Issue | Strategic role | Reconciliation with existing work |\n+| --- | --- | --- |\n+| FE-1476 — prepare the September demo | Outcome owner and acceptance narrative. | Child of FE-1357 while that map remains active; owns rehearsal and handoff rather than implementation details. |\n+| FE-1477 — route Petrinaut AI and Brunch | Experience-lane entry and mode selection. | Product acceptance overlaps FE-1440 (ship the elicitor in the demo site). Keep one implementation owner; do not build two switches. |\n+| FE-1478 — trace a generated net to requirements | Provenance acceptance through registers 3 → 2 → 1 → utterance. | Must shape Frontier 1 from its first model/projection types, not arrive as post-hoc metadata. |\n+| FE-1479 — targeted re-elicitation | Convergence issue for the reviewer loop. | Consumes FE-1438's machine client-tool/application path, FE-1439's session ownership, and the CPS correction path; it does not own a second mutation mechanism. |\n+| FE-1480 — infer requirements model to SDCPN | Authority and projection decision, then the production projector. | Must be reconciled with ADR-0003 before implementation. FE-1438 owns browser application, not hidden semantic projection. |\n+| FE-1481 — expose the requirements model | Inspection fallback and demo delta surface. | Select YAML/Markdown first. Defer FE-1442's broader live capture/completion UI unless the proof spine closes early. |\n+| FE-1482 — add the CPS plugin | Semantic-lane owner and concrete pressure on the plugin boundary. | Pulls the demo-critical slices from FE-1402 (completion), FE-1403 (CPS guidance), FE-1406 (strategies), and FE-1431 (declarative contract). FE-1393 remains the generic/Gherkin path and no longer gates September. |\n+\n+Other consequences for the old graph:\n+\n+- FE-1387 (second target and plugin-contract freeze) follows the CPS proof instead of preceding the\n+ demo.\n+- FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n+ scenario.\n+- FE-1438, FE-1439, FE-1440, FE-1423 (pre-remote gates), and FE-1441 (deployment) remain real\n+ implementation obligations; the new issues state user outcomes rather than replacing these\n+ substrate and release seams.\n+- FE-1402, FE-1403, FE-1406, and FE-1431 should produce only what the CPS runbook and domain\n+ contract consume. Their old standalone completion must not become a hidden prerequisite.\n+\n+## Beliefs, risks, and replan conditions\n+\n+| Current belief | Confidence and evidence | Replan when |\n+| --- | --- | --- |\n+| A bounded review-and-revise scenario can carry the September product claim without cold-start elicitation. | Medium. It is the written FE-1476 scenario, but Dora has not confirmed the use case. | The confirmed use case requires model creation rather than review, or the optimisation handoff requires fields absent from the fixture. |\n+| A concrete CPS implementation will discover a better minimum plugin contract faster than completing the generic design first. | Medium-high. Gherkin deliberately under-stresses the interface; CPS is the first real consumer. | The first worked CPS transformation cannot be expressed without a reusable harness primitive that must precede it. Build that primitive, then return immediately to the vertical proof. |\n+| Register 2 can be rich enough for pure SDCPN projection. | Low-medium. ADR-0003 requires it, but no real fold or projector exists and FE-1480 asserts non-determinism. | The worked transformation identifies an unavoidable semantic choice not represented in captures/model. Record it earlier or explicitly revisit the ADR. |\n+| Three to five turns can produce a meaningful scoped correction. | Low. No CPS runbook has been rehearsed. | Two rehearsals exceed the budget or require unrelated context. Narrow the region, preload explicit context, or revise the demo claim rather than script fake success. |\n+| The proven ask suspension can extend to document-application client tools. | Medium-low. The suspension spike succeeded, but the production transport intentionally refuses machine tool-result follow-ups. | The first tool round-trip cannot preserve correlation, durability, or non-user evidence semantics. Treat this as a critical integration blocker, not a UI detail. |\n+| The production website and remote server can be joined inside the timebox. | Medium-low. Local host seams exist; provider routing, session ownership, deployment, and release gates do not yet converge. | A deployable path is not proved by the end of Frontier 2. Preserve the real semantic loop and seek an explicit demo-surface decision rather than quietly falling back to test-only wiring. |\n+| Requirements-model UI is unnecessary for comprehension. | Medium. The reviewer needs inspectability, but FE-1481 explicitly permits export. | Rehearsal shows that provenance and delta cannot be understood from chat plus a structured export. |\n+\n+## Current choice\n+\n+Stop treating “unblocked” as “next.” The next strategic move is to run Frontier 0 immediately and\n+open Frontiers 1 and 2 in parallel: one worked CPS semantic slice and one existing-target reviewer\n+session/tool-return slice. Join them at the earliest correction, then drive the same bones through\n+provider routing and deployment. The generic Gherkin/plugin-freeze path and broad UI work wait for\n+that join.\n+\n+Revisit this choice as soon as Dora confirms the use case, the FE-1480 worked transformation lands,\n+or either parallel frontier fails its first proof. A steering pass that only updates ticket status\n+does not change this document; a new fact that changes the objective, proof spine, authority\n+boundary, or cut line does.\n", - "type": "add", - "additions": 278, - "deletions": 0 - }, - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/INDEX.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n@@ -70,8 +70,9 @@\n | -------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n | [COORDINATION](planning/_shared/COORDINATION.md) | active | cross-project; maintained by arc-close | Current sequencing recommendation, soft cross-map edges, unresolved seams, and exceptional roots; hard blockers, state, and hierarchy remain in Linear |\n | [hash-monorepo-import-plan](planning/_shared/hash-monorepo-import-plan.md) | active until FE-1437 lands | FE-1437 | Native HASH assimilation plan: preserved history and child package workspaces under one Brunch context root, explicit authority cutover, exhaustive repository-material disposition, toolchain port, boundary gates, and verification |\n | [SPEC-LEDGER](planning/_shared/SPEC-LEDGER.md) | active until milestone-one closure | FE-1383 | Obligation-level status and evidence ledger for the elicitation-kernel specification; settles when the milestone closes |\n+| [STEERING](planning/_shared/STEERING.md) | active | FE-1357 / FE-1476 | Current objective-driven strategy for the September CPS review-and-revise proof: evidence, architectural pressure, proof frontiers, cuts, issue projection, risks, and replan conditions |\n | [flue-architecture-cheatsheet](planning/_shared/flue-architecture-cheatsheet.md) | active | commented on FE-1383; feeds docs/agents/flue-routing.md | Architect's consolidation of all 21 Flue guide pages: direct structured generation uses `harness.prompt`; model-delegated work uses `useSubagent`; three-lane boundary summary and ranked divergence risks; reconciled against installed Flue 2.0.3 source |\n | [topology](planning/_shared/topology.md) | active | ratified → ADR-0002; N1 discharged by FE-1422 + FE-1392; local N5 implemented by FE-1391; N3 amended by FE-1437 | Pseudo-style verification of the package/app tree against the three-lane model and spec §12.2: portable ask/sweep protocols, Flue binding wiring, package boundaries, and application-only Brunch–Petrinaut composition |\n \n ## planning/legibility-sweep (FE-1401 arc records)\n", - "type": "update", - "additions": 1, - "deletions": 0 - }, - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@ -1,33 +1,37 @@\n # Project coordination\n \n This is the cross-map coordination surface for the `brunch-agent` project. FE-1383 owns the\n milestone-one harness build; FE-1357 owns the September demo and process-model plugin design.\n-Linear is canonical for issue state, parentage, and hard `blocks` relations. This file owns\n-only the judgment Linear cannot express: the current project-wide recommendation, soft edges,\n-unresolved seams, and exceptional roots.\n+Linear is canonical for issue state, parentage, and hard `blocks` relations. The\n+[steering model](./STEERING.md) chooses the current objective, proof frontiers, and cuts under\n+pressure. This file projects that strategy onto mechanically available work: the current\n+project-wide recommendation, soft edges, unresolved seams, and exceptional roots.\n \n Before revising the recommendation, run\n `turbo run linear:graph --filter '@hashintel/brunch-agent'`. Its compact projection supplies the\n factual open-issue DAG; read the relevant issue bodies for semantic content, then infer the smallest\n honest recommendation. Do not paste the generated graph here or mirror issue status.\n \n ## Current sequencing recommendation\n \n-As of **2026-08-21**, the FE-1437 authority cutover has been executed: the full brunch-lite\n-history is imported on `ln/fe-1437-hash-monorepo-import` in `hashintel/hash` (frozen standalone\n-SHA `43a0022918861846344b96a32cb94f92e2ee96ae`), every import gate re-verified. `hashintel/hash`\n-is authoritative; the standalone repository accepts no further implementation work. FE-1437\n-closes when the branch lands on `main` (squash merge, per convention). Part of FE-1440's website\n-wiring (the Brunch interactive-tool panel in `apps/petrinaut-website`) travelled with the import\n-branch; FE-1440 was trimmed on 2026-08-21 to the remaining mode wiring (mode switch, browser\n-identifier bootstrap, remote transport swap). After landing, advance FE-1438 (client-tool round-trip)\n-beside FE-1393 (plugin SDK and first projection); FE-1439 (private durable sessions) proceeds in\n-parallel. The integration stream joins at FE-1440 and deployment follows at FE-1441 (which also\n-waits on FE-1423's pre-exposure gates), while the harness stream reaches its contract-freeze\n-decision at FE-1387. FE-1402/FE-1403 form a parallel content/evaluation stream, without\n-displacing the two convergence edges.\n+As of **2026-08-24**, FE-1476 (the September demo delivery) changes the recommendation from generic\n+package completion to a concrete CPS review-and-revise proof. After FE-1437 (the monorepo import)\n+lands, open two fronts in parallel. The semantic front starts FE-1482 (the CPS plugin) against one\n+worked fixture and settles FE-1480's requirements-model-to-SDCPN authority boundary before it\n+implements a projector; FE-1478 (net-to-requirements provenance) is part of that spine from its\n+first types. The experience front advances FE-1438 (machine client-tool round-trip) and FE-1439\n+(private sessions) far enough for a new reviewer conversation to target an existing document,\n+while FE-1477/FE-1440 share one provider-routing implementation. Join the fronts at FE-1479\n+(targeted re-elicitation), then drive the same path through FE-1423's pre-exposure gates and\n+FE-1441 deployment.\n \n+FE-1393's generic Gherkin artifact and FE-1387's second-target contract freeze no longer gate the\n+September proof. FE-1402, FE-1403, FE-1406, and FE-1431 supply only the completion, guidance,\n+strategy, and contract slices the CPS `review-and-revise` runbook consumes. FE-1481 selects\n+YAML/Markdown export as the requirements-model inspection floor; broad UI follows only if the\n+closed loop is already proved.\n+\n ```text\n legend:\n -[hard]-> native Linear blocker\n -[coord]-> either order; do not implement concurrently\n@@ -35,31 +39,32 @@\n -[state-gate]-> condition in the world, not an issue edge\n \n nodes:\n FE-1437 [executed, landing] # history imported; HASH authoritative; PR pending\n- FE-1438 [next] # client-tool round-trip\n- FE-1439 [next, parallel] # private durable sessions\n- FE-1440 [join, partly landed] # website elicitor mode; panel wiring on import branch\n- FE-1441 [post-landing] # HASH deployment\n- FE-1393 [next] # plugin SDK + first projection\n- FE-1387 [after-FE-1393] # second pack + contract freeze\n- FE-1395 [coordination] # full affordance set\n- FE-1402 [parallel, content] # completion contract\n- FE-1403 [parallel, content] # interviewing guidance\n- FE-1404 [after-content] # armed baseline\n+ FE-1476 [objective] # September reviewer demo\n+ FE-1482 [next, semantic] # concrete CPS plugin + review/revise runbook\n+ FE-1480 [decision, semantic] # model/projection authority, then projector\n+ FE-1478 [semantic proof] # provenance through all three registers\n+ FE-1438 [next, experience] # machine client-tool round-trip + application\n+ FE-1439 [next, experience] # existing-target reviewer session ownership\n+ FE-1477/FE-1440 [experience] # one provider-routing implementation\n+ FE-1479 [join] # targeted correction changes the live net\n+ FE-1481 [fallback] # structured model export before UI\n+ FE-1441 [deployed proof] # HASH deployment\n \n edges:\n FE-1449 -[hard]-> FE-1438\n- FE-1392 -[hard]-> FE-1393\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n- FE-1393 -[hard]-> FE-1387\n- FE-1402, FE-1403 -[hard]-> FE-1404\n- FE-1395 -[coord]-> FE-1438\n FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n- FE-1387 -[input]-> FE-1440\n+ FE-1480 decision -[input]-> FE-1482, FE-1478\n+ FE-1402, FE-1403,\n+ FE-1406, FE-1431 -[input]-> FE-1482\n+ FE-1482, FE-1478,\n+ FE-1438, FE-1439 -[input]-> FE-1479\n+ FE-1479, FE-1440 -[input]-> FE-1441\n ```\n \n Hard-edge truth remains in Linear. The graph above is a deliberately focused recommendation,\n not a second issue database.\n@@ -89,10 +94,20 @@\n explicit approval from Lu.\n \n ## Open seams\n \n-- **Contract freeze — FE-1387.** The process-model target must stress the plugin contract before\n- it freezes. FE-1393 makes the contract programmable; FE-1387 remains the cross-map hinge.\n+- **Projection authority — FE-1480.** The ticket assumes non-deterministic LLM inference from the\n+ requirements model to SDCPN, while ADR-0003 requires write-time-only semantic inference and a\n+ pure projection. A worked CPS transformation must assign every judgment to capture, fold,\n+ projection, or document application before the interface freezes.\n+- **Controller and runbook.** The harness does not read the folded model or open issues back into\n+ the agent, and no plugin defines a job trajectory or stopping rule. FE-1482 must exercise the\n+ narrow `review-and-revise` loop; FE-1406 and FE-1402/FE-1403 are inputs, not parallel products.\n+- **Reviewer target identity — FE-1439 × FE-1479.** The current host derives target-document\n+ identity from conversation identity. September requires a new reviewer conversation against an\n+ existing target without weakening owner isolation.\n+- **Contract freeze — FE-1387.** The CPS target must stress the plugin contract before it freezes.\n+ The freeze follows the September semantic proof rather than gating it.\n - **Absence locator.** An absence capture carries no payload, but the fold needs a field-specific\n coordinate (anchor × slot). The plugin-contract spec records three worked cases; any envelope\n amendment belongs to the harness side of this seam.\n - **Structured-tap evidence — FE-1395 × capture store.** `resolve-conflict` currently rejects\n@@ -119,4 +134,11 @@\n - **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n no owning map yet; closest to the motif/quiver strategy work.\n - **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n+- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n+ map has been chosen.\n+- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n+ under FE-1357.\n+- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n+ steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n+ ownership boundaries are recorded in STEERING's issue projection before external mutation.\n", - "type": "update", - "additions": 55, - "deletions": 33 - } - ], - "summary": "add: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md (+278/-0)\nupdate: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md (+1/-0)\nupdate: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md (+55/-33)" -} -``` - -## Assistant - -The first pass is now written and wired into the existing control surface. Before I treat it as settled, I’m checking whether it actually preserves the key distinction we reached: concrete end-to-end contract proof first, without weakening the evidence/register architecture or turning the new ticket list into the strategy. - -**Tool Use:** `oracle` - -```json -{ - "task": "Review the current uncommitted steering/control-surface diff for the September Brunch demo. Intended outcome: @docs/planning/_shared/STEERING.md must be an objective-driven strategic model (not a roadmap/template or issue mirror) that captures this session's findings: the current implementation ends at durable captures; the missing controller/model/projection/correction loops are the critical architecture; the September scenario is a prebuilt CPS model reviewed for provenance then corrected in 3–5 turns; build a concrete CPS review-and-revise path before generic plugin breadth; preserve write-time semantics, pure fold, pure projection, provenance, and real-entrypoint verification; treat FE-1480's LLM-projection premise as unresolved; and adopt/fold FE-1476–FE-1482 into the old graph without making Linear writes. @docs/planning/_shared/COORDINATION.md should remain the compact sequencing projection, and @docs/INDEX.md should register the new control document. Read the current diff plus @docs/adr/0003-three-register-ir.md, @docs/adr/0004-in-petrinaut-staging-and-the-monorepo-import.md, @docs/planning/process-model-elicitation/plugin-contract-spec.md, @docs/planning/process-model-elicitation/petrinaut-integration-spec.md, and relevant source if needed. Determine whether the steering model is strategically comprehensive and internally coherent under the roughly two-week constraint. Report only concrete high-impact omissions, contradictions, unjustified commitments, or sequencing errors, with the smallest correction for each. Explicitly assess whether the proof frontiers close the real semantic and deployed interaction loops rather than repeating the project's tracer-as-done failure. Ignore copyediting and unrelated repository findings." -} -``` - -## User - -**Tool Result:** `TU-034CefPqdFOcX5VtYmUwLi` - -``` -## Verdict - -The steering document does what the outcome asked: it is an objective-driven strategic model, not a roadmap or issue mirror; it captures the durable-captures ceiling, the missing controller/model/projection/correction loops, the review-and-revise scenario, the CPS-before-breadth bet, the preserved invariants, the FE-1480 open question, and the no-Linear-writes issue adoption. COORDINATION correctly demotes itself to a projection of STEERING, and INDEX registers the new document. I verified the load-bearing status-table claims against source: `targetDocumentId` is derived from `conversationId` ([petrinaut-chat.ts#L33-L44](file:///Users/lunelson/Code/hashintel/hash/apps/brunch-agent/src/petrinaut-chat.ts#L33-L44)), the transport refuses machine tool-result follow-ups pending FE-1438 ([transport-aisdk/src/index.ts#L261-L291](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/transport-aisdk/src/index.ts#L261-L291)), and `Plugin` is identity plus exactly one proposal type ([core/src/plugin.ts#L32-L35](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts#L32-L35)). - -Three findings should change the text before it ships. All are S-sized doc edits. - -## Blocking findings - -**1. The sweep — the declared single point of semantic failure — has no proof owner in the frontiers.** -The proof spine's arrow `reviewer question and scoped correction -> superseding capture` is the write-time sweep, which ADR-0003 and the plugin-contract spec (strain 7) name as the single point of semantic failure, and which the status table itself says cannot currently see active capture IDs or propose a supersession. Yet no frontier proof asserts it. Frontier 1's proof ("one source-grounded supersession changes the expected model field") can be passed with a hand-inserted superseding capture; Frontier 2's proof ("submits a scoped correction, and sees the returned apply result") can be passed by a controller that changes the projection without any superseding capture at all — its "no test-only injection" clause covers *wiring*, not *semantic extraction*. That is exactly the tracer-as-done shape the document warns against, surviving one layer deeper. -**Smallest fix:** one sentence added to the Frontier 2 proof (and mirrored in Frontier 1's join expectation): the applied net delta must trace to a superseding capture produced by the production sweep from the reviewer's actual utterance — not injected by the test or fabricated by the controller. - -**2. Frontier 0 doesn't constrain how the prebuilt fixture gets its register-1 content.** -Demo beat 3 is "captures to an exact source utterance," but a "prebuilt requirements-model/net fixture" authored directly at register 2 or 3 has no genuine source utterances — forward/reverse provenance queries would run against fabricated evidence, violating the doc's own "no layer may be a fixture masquerading as production wiring" rule at the layer the demo most depends on. Frontier 0 says "freeze one fixture" without stating this constraint. -**Smallest fix:** add to Frontier 0 that the fixture is a valid store state — source conversation plus captures produced through (or validated against) the production capture/fold path — not a hand-authored register-2/3 artifact, so that both provenance directions and the correction supersession run on real data. - -**3. FE-1331's deferral silently contradicts accepted ADR-0004.** -ADR-0004's consequences explicitly state "FE-1331 (start elicitation from create-new-net) is **un-deferred** — in-Petrinaut initiation is now the September topology, not the post-September one." STEERING says FE-1331 "is outside the current reviewer-against-existing-target scenario," and COORDINATION's exceptional-roots entry re-defers it — neither names the ADR. STEERING's own charter says it "may expose a conflict with a specification or ADR, but cannot silently override one," and this is a silent override: the review-and-revise scenario reverses ADR-0004's initiation-topology consequence. -**Smallest fix:** in the issue-projection bullet, name the conflict explicitly ("ADR-0004 un-deferred FE-1331; the FE-1476 scenario re-defers it — exposed conflict pending a dated ADR-0004 amendment or Dora's use-case confirmation") rather than treating the deferral as unremarkable. - -## Non-blocking follow-ups - -- **Controller's F1 dependency is implicit.** Frontier 2 mounts a controller that reads "the active model and selected region," which only exists once Frontier 1's read path lands; the frontiers "start in parallel" text could let F2 stall on it unnoticed. One clause noting that F2's controller-mounting step waits on F1's active-model read path (while session binding and tool-result admission proceed independently) would make the parallelism honest. -- **The petrinaut-integration-spec's user stories are now partially stale** (cold-start framing: "interview me about my process before building," "watch the net appear"). STEERING doesn't need to amend it, but a reconciliation note belongs on the next arc-close pass so the `active` spec doesn't contradict the demo claim. -- The other files in this diff (git-workflow, issue-writing, legibility protocol additions; import-plan `gt` wording) are unrelated to the steering model and raise no concerns. - -## Tracer-as-done assessment - -Apart from finding 1, the frontiers genuinely close the loops rather than repeating the failure: Frontier 1 proves semantic purity and bidirectional provenance at the production fold/projection APIs; Frontier 2 explicitly requires the real HTTP handler and panel with no test-only target or tool wiring; Frontier 3 requires a clean-browser run against the deployed surface, a reload survival, and the optimisation handoff. The status table's "Demo website" row and the belief-table replan condition ("seek an explicit demo-surface decision rather than quietly falling back to test-only wiring") show the failure mode is understood. Findings 1 and 2 are the two remaining seams where a fixture or hand-authored capture could still stand in for the real semantic path; closing them in the text closes the loop definition completely. -``` - -## Assistant - -The Oracle found two places where a “green” proof could still cheat: a hand-authored prebuilt model could fake provenance, and a hand-inserted supersession could fake correction. It also caught a real conflict with ADR-0004 around the create-new-net entrypoint. I’m tightening those acceptance boundaries rather than adding more plan structure. - -**Tool Use:** `apply_patch` - -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n@@\n Confirm the business use case, freeze one representative prebuilt requirements-model/net fixture,\n and name the optimisation handoff artifact. On that fixture, settle the FE-1480 authority question:\n which steps are write-time semantic capture, pure model fold, pure SDCPN projection, and document\n application?\n+\n+The prebuilt fixture must be a valid register-1 store state with a source conversation and\n+quote-anchored captures produced through, or independently validated against, the production\n+capture/fold path. A hand-authored register-2 model or register-3 net cannot prove provenance and\n+cannot serve as the correction baseline.\n \n **Proof:** one reviewed worked transformation in which every SDCPN element needed by the scenario\n traces to model fields and captures, with every non-mechanical judgment assigned to a write-time\n producer. If this cannot be drawn honestly, implementation should not freeze an interface.\n@@\n **Proof:** from the production fold/projection APIs, one source-grounded supersession changes the\n expected model field and corresponding SDCPN elements, leaves an unrelated region stable, and\n answers both forward and reverse provenance queries. A YAML or Markdown rendering of the model is\n-enough for inspection at this frontier.\n+enough for inspection at this frontier. The proving proposal must have the shape the production\n+sweep will emit; the cross-frontier join is not accepted until that sweep produces it from the\n+reviewer's actual utterance rather than a test inserting it directly.\n@@\n Allow a new conversation to bind to an existing target document. Admit the machine client-tool\n results needed to apply and diagnose a net change. Mount the narrow `review-and-revise` runbook and\n controller so that the active model and selected region, rather than the raw transcript alone,\n drive three to five questions.\n+\n+Session binding and client-tool admission may proceed in parallel with Frontier 1. Mounting the\n+controller against active model/issues waits for Frontier 1's production read path; do not replace\n+that dependency with request-shaped model context.\n \n **Proof:** through the real Brunch HTTP handler and Petrinaut panel, a reviewer selects the prepared\n region, receives a grounded explanation, submits a scoped correction, and sees the returned apply\n-result resume the same durable session. No test-only injection supplies the target or tool wiring.\n+result resume the same durable session. The net delta must trace to a superseding capture produced\n+by the production sweep from the reviewer's utterance, not one inserted by the test or fabricated\n+by the controller. No test-only injection supplies the target or tool wiring.\n@@\n-- FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n- scenario.\n+- FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n+ scenario, but ADR-0004 explicitly un-deferred it as September topology. The FE-1476 scenario\n+ therefore creates an exposed conflict pending Dora's confirmation and, if review-and-revise\n+ stands, a dated ADR-0004 amendment; this steering document does not silently re-defer it.\n - FE-1438, FE-1439, FE-1440, FE-1423 (pre-remote gates), and FE-1441 (deployment) remain real\n implementation obligations; the new issues state user outcomes rather than replacing these\n substrate and release seams.\n - FE-1402, FE-1403, FE-1406, and FE-1431 should produce only what the CPS runbook and domain\n contract consume. Their old standalone completion must not become a hidden prerequisite.\n+- The active Petrinaut integration spec still describes a cold-start interview in some user\n+ stories. Reconcile those stories with the confirmed scenario rather than treating this plan as a\n+ silent specification amendment.\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@\n-- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** Deferred post-September\n- consumer topology; returns after in-Petrinaut staging proves itself.\n+- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n+ September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n+ the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n*** End Patch" -} -``` - -## User - -**Tool Result:** `TU-034CejRiYoQEn9tyZlBKRM` - -``` -{ - "files": [ - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n@@ -169,8 +169,13 @@\n and name the optimisation handoff artifact. On that fixture, settle the FE-1480 authority question:\n which steps are write-time semantic capture, pure model fold, pure SDCPN projection, and document\n application?\n \n+The prebuilt fixture must be a valid register-1 store state with a source conversation and\n+quote-anchored captures produced through, or independently validated against, the production\n+capture/fold path. A hand-authored register-2 model or register-3 net cannot prove provenance and\n+cannot serve as the correction baseline.\n+\n **Proof:** one reviewed worked transformation in which every SDCPN element needed by the scenario\n traces to model fields and captures, with every non-mechanical judgment assigned to a write-time\n producer. If this cannot be drawn honestly, implementation should not freeze an interface.\n \n@@ -182,20 +187,28 @@\n \n **Proof:** from the production fold/projection APIs, one source-grounded supersession changes the\n expected model field and corresponding SDCPN elements, leaves an unrelated region stable, and\n answers both forward and reverse provenance queries. A YAML or Markdown rendering of the model is\n-enough for inspection at this frontier.\n+enough for inspection at this frontier. The proving proposal must have the shape the production\n+sweep will emit; the cross-frontier join is not accepted until that sweep produces it from the\n+reviewer's actual utterance rather than a test inserting it directly.\n \n ### Frontier 2 — close the reviewer control loop\n \n Allow a new conversation to bind to an existing target document. Admit the machine client-tool\n results needed to apply and diagnose a net change. Mount the narrow `review-and-revise` runbook and\n controller so that the active model and selected region, rather than the raw transcript alone,\n drive three to five questions.\n \n+Session binding and client-tool admission may proceed in parallel with Frontier 1. Mounting the\n+controller against active model/issues waits for Frontier 1's production read path; do not replace\n+that dependency with request-shaped model context.\n+\n **Proof:** through the real Brunch HTTP handler and Petrinaut panel, a reviewer selects the prepared\n region, receives a grounded explanation, submits a scoped correction, and sees the returned apply\n-result resume the same durable session. No test-only injection supplies the target or tool wiring.\n+result resume the same durable session. The net delta must trace to a superseding capture produced\n+by the production sweep from the reviewer's utterance, not one inserted by the test or fabricated\n+by the controller. No test-only injection supplies the target or tool wiring.\n \n ### Frontier 3 — converge on the deployed demo\n \n Wire provider/mode routing, browser principal and private session lookup, remote transport,\n@@ -244,14 +257,19 @@\n \n - FE-1387 (second target and plugin-contract freeze) follows the CPS proof instead of preceding the\n demo.\n - FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n- scenario.\n+ scenario, but ADR-0004 explicitly un-deferred it as September topology. The FE-1476 scenario\n+ therefore creates an exposed conflict pending Dora's confirmation and, if review-and-revise\n+ stands, a dated ADR-0004 amendment; this steering document does not silently re-defer it.\n - FE-1438, FE-1439, FE-1440, FE-1423 (pre-remote gates), and FE-1441 (deployment) remain real\n implementation obligations; the new issues state user outcomes rather than replacing these\n substrate and release seams.\n - FE-1402, FE-1403, FE-1406, and FE-1431 should produce only what the CPS runbook and domain\n contract consume. Their old standalone completion must not become a hidden prerequisite.\n+- The active Petrinaut integration spec still describes a cold-start interview in some user\n+ stories. Reconcile those stories with the confirmed scenario rather than treating this plan as a\n+ silent specification amendment.\n \n ## Beliefs, risks, and replan conditions\n \n | Current belief | Confidence and evidence | Replan when |\n", - "type": "update", - "additions": 21, - "deletions": 3 - }, - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@ -128,10 +128,11 @@\n ## Exceptional roots\n \n These project issues intentionally have no parent and are roots under the registry rule:\n \n-- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** Deferred post-September\n- consumer topology; returns after in-Petrinaut staging proves itself.\n+- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n+ September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n+ the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n - **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n no owning map yet; closest to the motif/quiver strategy work.\n - **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n", - "type": "update", - "additions": 3, - "deletions": 2 - } - ], - "summary": "update: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md (+21/-3)\nupdate: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md (+3/-2)" -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "cat docs/planning/_shared/STEERING.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "cat docs/planning/_shared/COORDINATION.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '63,80p' docs/INDEX.md; git diff --check -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CejrJFCxLm7ojcPqke4` - -``` -{ - "output": "# Brunch steering model\n\nThis is the current strategic understanding of Brunch: what outcome matters now, what the system\ncan actually do, which beliefs remain weak, and where to spend the next unit of time. Its immediate\ndecision horizon is the September demo, but it steers the whole context rather than one package or\none Linear map.\n\nThis is not a roadmap, a specification, or an issue mirror. Specifications and accepted ADRs define\nobligations and settled architecture. Linear is canonical for issue state, hierarchy, and hard\nblockers. [COORDINATION](./COORDINATION.md) projects this strategy onto the mechanically available\nwork. This document owns the judgment between them: which objective to pursue under the current\ntime, evidence, risk, and product pressure; what to defer; and what new evidence would change that\nchoice. It may expose a conflict with a specification or ADR, but cannot silently override one.\n\nThe document remains live. A steering pass orients from current evidence, chooses a frontier,\nexecutes to a named proof, reconciles the result with specifications and issues, then replans only\nwhere the evidence changed. Git carries the history; this file carries only the current model. Its\nshape is intentionally specific to the present effort. Do not extract a generic template or skill\nuntil a second real planning cycle shows which parts recur.\n\n## The September outcome\n\nAs of **2026-08-24**, the working constraint is roughly two human weeks. The event date and final\nbusiness use case are not yet recorded here; Dora's confirmation is a decision gate, not an excuse\nto leave the technical spine vague.\n\nFE-1476 (the September demo delivery) supplies the working scenario:\n\n1. A reviewer opens a prebuilt cyber-physical-process requirements model and its generated SDCPN in\n Petrinaut.\n2. The reviewer selects or describes a net element and asks why it was modelled that way.\n3. Brunch traces the answer through the requirements model and captures to an exact source\n utterance.\n4. The reviewer scopes one correction and conducts three to five focused chat turns.\n5. New or superseding captures change the elicited model; reprojection changes the corresponding\n part of the live net without rebuilding unrelated parts.\n6. The resulting artifact is handed to the existing optimisation experiment flow.\n\nThis is a **review-and-revise** demonstration. It does not need to prove that Brunch can elicit an\nentire CPS model from a blank conversation. It does need to prove a closed semantic and interaction\nloop. A chat transcript beside a static fixture, an unexplained net mutation, or a test that injects\nwiring absent from the deployed entrypoint does not satisfy the outcome.\n\nThe proof spine is therefore:\n\n```text\nsource utterance\n -> active typed capture\n -> folded CPS requirements model\n -> SDCPN element + provenance\n -> reviewer question and scoped correction\n -> superseding capture\n -> changed folded model\n -> changed live SDCPN\n -> optimisation handoff\n```\n\n## Where the system actually stands\n\nThe package topology is in place and the implemented tracer is real, but the September loop is not\nan incremental extension of an almost-finished product. Most of the contract-bearing middle is\nabsent.\n\n| Surface | Evidence now | September consequence |\n| --- | --- | --- |\n| Ask, suspend, return | A user answer to `brunch_ask` survives the AI SDK/Flue boundary and resumes durable history. | Reuse; do not redesign the ask protocol. |\n| Settlement and capture | A settled range is privately swept into quote-anchored captures and applied atomically. Supersession and active-head validation exist in the store. | Preserve as the evidence foundation, but expose active state to the controller. |\n| Plugin SDK | The exported `Plugin` is deliberately only identity plus exactly one proposal type. Gherkin captures one verbatim statement. | There is no implemented fold, demand runner, model, projection, or useful hard-target plugin to extend. |\n| Elicitation control | The agent receives general ask/sweep instructions. Sweep extraction sees a conversation range and proposal names only. No production path reads the active capture set or a derived model back into the interview. | Brunch cannot yet choose a next question from what it has learned or conduct a targeted correction. |\n| CPS semantics | The three-register design and provisional two-schema/two-table plugin contract are desk-designed. No `plugin-cps` exists. | The critical semantic path must be built against a concrete CPS case, not inferred from Gherkin completeness. |\n| Correction | The store can represent supersession, but extraction cannot see active capture IDs, model issues, or the target region; Gherkin cannot propose a supersession. | Targeted re-elicitation is structurally unreachable despite the storage mechanics being present. |\n| Petrinaut transport | Local panel streaming and human ask-return work. Machine client-tool-result follow-ups are explicitly refused pending FE-1438 (the client-tool round-trip). | The agent cannot yet apply a projection to the live document and receive the result. |\n| Session target | The current application derives `targetDocumentId` from `conversationId`. | A new reviewer session cannot address a pre-existing elicitation target without changing this identity boundary. |\n| Demo website | The production website still uses its stock assistant route. The `/brunch` Actual Mode is a separate read-only fixture/SSE surface. | Local tracer proof must not be mistaken for deployed integration. |\n\nThe decisive reading is that the current design is not too rigorous in its preservation of\nevidence, correction, or register boundaries. It is too broad and too generic for the remaining\ntime. Completing generic plugin machinery, a second target, a full CPS ontology, and a cold-start\ninterviewer before crossing the real reviewer loop would optimize the library while leaving the\ndemo hollow.\n\n## The strategic bet\n\nBuild the smallest honest **CPS review-and-revise loop** through all three registers and the real\nPetrinaut entrypoint. Let that concrete implementation discover the minimum plugin interface, then\ngeneralize only what the CPS case and existing Gherkin case both need.\n\nThis is not permission to take another thin tracer as the definition of done. The vertical proof is\ncontract-bearing: it includes model assembly, provenance, targeted correction, reprojection,\napplication, and the deployed route. Breadth inside each layer may be narrow; no layer in that loop\nmay be a fixture masquerading as production wiring.\n\nThe bet preserves these load-bearing decisions:\n\n- Captures remain the durable, source-grounded assertion register.\n- Every semantic inference happens at write time and is recorded as a contestable capture.\n- The elicited model is a pure fold over active captures and every model part names its supporting\n capture IDs.\n- SDCPN projection consumes the elicited model without rereading the transcript or making hidden\n semantic judgments.\n- Petrinaut application and diagnostics are separate from semantic projection: the application may\n use client tools to apply a projected artifact, but it does not become the authority that invents\n the model.\n- A correction supersedes or adds assertions and re-runs the fold and projection; it does not patch\n an unexplained net element directly.\n\nFE-1480 (requirements-model-to-SDCPN inference) challenges the third and fourth decisions by\nassuming the projection itself requires LLM inference. That assumption is unresolved. If a worked\nCPS case proves that the register-2 model is insufficient for pure projection, the honest choices\nare to record the missing semantic judgment as a capture before folding or to amend ADR-0003 (the\nthree-register IR) explicitly. Hiding inference inside a read-time projection is not an available\nshortcut.\n\n## The elicitor architecture under this load\n\nThe discussion began with four parts; the current model has five responsibilities across the\nharness and plugin layers, plus one per-engagement input. The missing responsibility is the\ncontroller that closes the loop between captured evidence and the next move.\n\n| Responsibility | Owner | What it contains | State and September obligation |\n| --- | --- | --- | --- |\n| Strategy repertoire | Harness | Orientations, motivations, conversational licences, interviewing techniques, and question-formulation guidance. | Partly researched, not operationally selected. Implement only the techniques used by the review-and-revise runbook. |\n| Evidence engine | Harness | Archive, settlement sweep, quote anchoring, durable captures, issues, conflict, supersession, and provenance primitives. | Strongest implemented layer. Add the active-model/issues read path needed by control and correction; do not broaden storage semantics without evidence. |\n| Elicitation controller | Harness | Reads the engagement brief, active folded model and issues, current runbook, and strategy repertoire; chooses `ask`, `propose`, `contrast`, `validate`, `project`, `explain`, or `stop`. | Absent. Build the narrow controller loop needed to explain and revise one selected region. |\n| Domain contract | Plugin | Proposal and model schemas; identity, fold, grade, demand, diagnostics, projection, and provenance rules for one target domain. | Designed but unimplemented. Build the CPS subset exercised by the fixture and correction; let it pressure the generic interface. |\n| Job runbooks | Plugin | Named jobs over the same domain: objectives, entry conditions, trajectories, demand/completion rules, checks, stopping, revision, boundaries, and handoff. | Absent. Implement `review-and-revise`; defer a complete cold-start runbook. |\n\nThe **engagement brief** is dynamic input, not plugin policy: target document, participant role,\nobjective, scope, known constraints, allowed actions, and time budget for this run. For September it\nbinds a reviewer to an existing target and one revisable region.\n\nA separate free-form “next-question ledger” should not become another authority. Most of it is a\nderived control trace:\n\n```text\nrunbook demand -> model gap or issue -> candidate move -> chosen move -> concrete ask\n```\n\nPersist only what replay, audit, or explicit user commitment requires. The controller must be able\nto explain its chosen move from the runbook and active model; it must not accumulate an independent\nshadow plan.\n\nThe September `review-and-revise` runbook is provisionally:\n\n```text\nentry:\n existing target + folded requirements model + projected net + reviewer scope\ntrajectory:\n orient -> select -> explain provenance -> frame correction\n -> ask/validate (3-5 turns) -> show semantic and net delta -> confirm -> hand off\ndone:\n scoped demands are met at the declared grade\n no open conflict blocks the selected projection\n reviewer confirms the intended delta\n every changed net element retains provenance\nboundary:\n do not expand into cold-start elicitation or unrelated net repair\n```\n\n## Proof frontiers and execution order\n\nThe work has four frontiers. They are ordered by learning dependency, not by which ticket is\ncurrently unblocked. The semantic and experience lanes start in parallel after Frontier 0, then\njoin as early as possible; they are not two long independent streams to integrate at the end.\n\n### Frontier 0 — make the demo claim decidable\n\nConfirm the business use case, freeze one representative prebuilt requirements-model/net fixture,\nand name the optimisation handoff artifact. On that fixture, settle the FE-1480 authority question:\nwhich steps are write-time semantic capture, pure model fold, pure SDCPN projection, and document\napplication?\n\nThe prebuilt fixture must be a valid register-1 store state with a source conversation and\nquote-anchored captures produced through, or independently validated against, the production\ncapture/fold path. A hand-authored register-2 model or register-3 net cannot prove provenance and\ncannot serve as the correction baseline.\n\n**Proof:** one reviewed worked transformation in which every SDCPN element needed by the scenario\ntraces to model fields and captures, with every non-mechanical judgment assigned to a write-time\nproducer. If this cannot be drawn honestly, implementation should not freeze an interface.\n\n### Frontier 1 — close the CPS semantic loop\n\nImplement only the CPS proposal kinds, model slots, identity/fold rules, demands, projection, and\nprovenance exercised by the fixture and one realistic correction. Carry capture IDs through every\nderived layer. Make active model issues and selected-region context available to the controller.\n\n**Proof:** from the production fold/projection APIs, one source-grounded supersession changes the\nexpected model field and corresponding SDCPN elements, leaves an unrelated region stable, and\nanswers both forward and reverse provenance queries. A YAML or Markdown rendering of the model is\nenough for inspection at this frontier. The proving proposal must have the shape the production\nsweep will emit; the cross-frontier join is not accepted until that sweep produces it from the\nreviewer's actual utterance rather than a test inserting it directly.\n\n### Frontier 2 — close the reviewer control loop\n\nAllow a new conversation to bind to an existing target document. Admit the machine client-tool\nresults needed to apply and diagnose a net change. Mount the narrow `review-and-revise` runbook and\ncontroller so that the active model and selected region, rather than the raw transcript alone,\ndrive three to five questions.\n\nSession binding and client-tool admission may proceed in parallel with Frontier 1. Mounting the\ncontroller against active model/issues waits for Frontier 1's production read path; do not replace\nthat dependency with request-shaped model context.\n\n**Proof:** through the real Brunch HTTP handler and Petrinaut panel, a reviewer selects the prepared\nregion, receives a grounded explanation, submits a scoped correction, and sees the returned apply\nresult resume the same durable session. The net delta must trace to a superseding capture produced\nby the production sweep from the reviewer's utterance, not one inserted by the test or fabricated\nby the controller. No test-only injection supplies the target or tool wiring.\n\n### Frontier 3 — converge on the deployed demo\n\nWire provider/mode routing, browser principal and private session lookup, remote transport,\ndeployment gates, and the optimisation handoff. Rehearse the exact scenario with a clean browser\nagainst the deployed demo surface.\n\n**Proof:** a screen-recordable run completes the six September beats, survives one reload, exposes\nthe before/after requirements-model delta, and hands the resulting SDCPN to the optimisation flow.\nDiagnostics show the source capture and projection identities needed to investigate a failure.\n\n## What is deliberately cut\n\nUntil the proof spine is closed:\n\n- Do not freeze a broad declarative plugin SDK or require a second hard target. Extract the shared\n contract after CPS has stressed it.\n- Do not make the Gherkin artifact path a prerequisite for the CPS demo.\n- Do not build a full requirements-graph UI. FE-1481's YAML or Markdown export is the selected\n fallback; a UI earns time only if the core loop is already green.\n- Do not build a complete cold-start CPS interview, general target gallery, every affordance type,\n voice input, surprising-scenario generation, or broad telemetry vocabulary.\n- Do not implement a comprehensive CPS ontology. Support the fixture, the correction, and the\n optimisation handoff while keeping the data model honest about what it omits.\n- Do not bypass provenance or write-time semantics to make a visually convincing net mutation.\n\nThese are sequencing cuts, not claims that the deferred obligations are unimportant.\n\n## Issue projection\n\nThe PM-authored issues are adopted here as the September delivery decomposition. Linear has not yet\nbeen changed; its current unparented state is recorded in COORDINATION until an explicitly approved\nregistry update. The recommended hierarchy is FE-1357 (September planning and plugin design) →\nFE-1476 (September delivery) → FE-1477 through FE-1482.\n\n| Issue | Strategic role | Reconciliation with existing work |\n| --- | --- | --- |\n| FE-1476 — prepare the September demo | Outcome owner and acceptance narrative. | Child of FE-1357 while that map remains active; owns rehearsal and handoff rather than implementation details. |\n| FE-1477 — route Petrinaut AI and Brunch | Experience-lane entry and mode selection. | Product acceptance overlaps FE-1440 (ship the elicitor in the demo site). Keep one implementation owner; do not build two switches. |\n| FE-1478 — trace a generated net to requirements | Provenance acceptance through registers 3 → 2 → 1 → utterance. | Must shape Frontier 1 from its first model/projection types, not arrive as post-hoc metadata. |\n| FE-1479 — targeted re-elicitation | Convergence issue for the reviewer loop. | Consumes FE-1438's machine client-tool/application path, FE-1439's session ownership, and the CPS correction path; it does not own a second mutation mechanism. |\n| FE-1480 — infer requirements model to SDCPN | Authority and projection decision, then the production projector. | Must be reconciled with ADR-0003 before implementation. FE-1438 owns browser application, not hidden semantic projection. |\n| FE-1481 — expose the requirements model | Inspection fallback and demo delta surface. | Select YAML/Markdown first. Defer FE-1442's broader live capture/completion UI unless the proof spine closes early. |\n| FE-1482 — add the CPS plugin | Semantic-lane owner and concrete pressure on the plugin boundary. | Pulls the demo-critical slices from FE-1402 (completion), FE-1403 (CPS guidance), FE-1406 (strategies), and FE-1431 (declarative contract). FE-1393 remains the generic/Gherkin path and no longer gates September. |\n\nOther consequences for the old graph:\n\n- FE-1387 (second target and plugin-contract freeze) follows the CPS proof instead of preceding the\n demo.\n- FE-1331 (start from create-new-net) is outside the current reviewer-against-existing-target\n scenario, but ADR-0004 explicitly un-deferred it as September topology. The FE-1476 scenario\n therefore creates an exposed conflict pending Dora's confirmation and, if review-and-revise\n stands, a dated ADR-0004 amendment; this steering document does not silently re-defer it.\n- FE-1438, FE-1439, FE-1440, FE-1423 (pre-remote gates), and FE-1441 (deployment) remain real\n implementation obligations; the new issues state user outcomes rather than replacing these\n substrate and release seams.\n- FE-1402, FE-1403, FE-1406, and FE-1431 should produce only what the CPS runbook and domain\n contract consume. Their old standalone completion must not become a hidden prerequisite.\n- The active Petrinaut integration spec still describes a cold-start interview in some user\n stories. Reconcile those stories with the confirmed scenario rather than treating this plan as a\n silent specification amendment.\n\n## Beliefs, risks, and replan conditions\n\n| Current belief | Confidence and evidence | Replan when |\n| --- | --- | --- |\n| A bounded review-and-revise scenario can carry the September product claim without cold-start elicitation. | Medium. It is the written FE-1476 scenario, but Dora has not confirmed the use case. | The confirmed use case requires model creation rather than review, or the optimisation handoff requires fields absent from the fixture. |\n| A concrete CPS implementation will discover a better minimum plugin contract faster than completing the generic design first. | Medium-high. Gherkin deliberately under-stresses the interface; CPS is the first real consumer. | The first worked CPS transformation cannot be expressed without a reusable harness primitive that must precede it. Build that primitive, then return immediately to the vertical proof. |\n| Register 2 can be rich enough for pure SDCPN projection. | Low-medium. ADR-0003 requires it, but no real fold or projector exists and FE-1480 asserts non-determinism. | The worked transformation identifies an unavoidable semantic choice not represented in captures/model. Record it earlier or explicitly revisit the ADR. |\n| Three to five turns can produce a meaningful scoped correction. | Low. No CPS runbook has been rehearsed. | Two rehearsals exceed the budget or require unrelated context. Narrow the region, preload explicit context, or revise the demo claim rather than script fake success. |\n| The proven ask suspension can extend to document-application client tools. | Medium-low. The suspension spike succeeded, but the production transport intentionally refuses machine tool-result follow-ups. | The first tool round-trip cannot preserve correlation, durability, or non-user evidence semantics. Treat this as a critical integration blocker, not a UI detail. |\n| The production website and remote server can be joined inside the timebox. | Medium-low. Local host seams exist; provider routing, session ownership, deployment, and release gates do not yet converge. | A deployable path is not proved by the end of Frontier 2. Preserve the real semantic loop and seek an explicit demo-surface decision rather than quietly falling back to test-only wiring. |\n| Requirements-model UI is unnecessary for comprehension. | Medium. The reviewer needs inspectability, but FE-1481 explicitly permits export. | Rehearsal shows that provenance and delta cannot be understood from chat plus a structured export. |\n\n## Current choice\n\nStop treating “unblocked” as “next.” The next strategic move is to run Frontier 0 immediately and\nopen Frontiers 1 and 2 in parallel: one worked CPS semantic slice and one existing-target reviewer\nsession/tool-return slice. Join them at the earliest correction, then drive the same bones through\nprovider routing and deployment. The generic Gherkin/plugin-freeze path and broad UI work wait for\nthat join.\n\nRevisit this choice as soon as Dora confirms the use case, the FE-1480 worked transformation lands,\nor either parallel frontier fails its first proof. A steering pass that only updates ticket status\ndoes not change this document; a new fact that changes the objective, proof spine, authority\nboundary, or cut line does.\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CejrKhsqzE7ZBmXWT1H` - -``` -{ - "output": "# Project coordination\n\nThis is the cross-map coordination surface for the `brunch-agent` project. FE-1383 owns the\nmilestone-one harness build; FE-1357 owns the September demo and process-model plugin design.\nLinear is canonical for issue state, parentage, and hard `blocks` relations. The\n[steering model](./STEERING.md) chooses the current objective, proof frontiers, and cuts under\npressure. This file projects that strategy onto mechanically available work: the current\nproject-wide recommendation, soft edges, unresolved seams, and exceptional roots.\n\nBefore revising the recommendation, run\n`turbo run linear:graph --filter '@hashintel/brunch-agent'`. Its compact projection supplies the\nfactual open-issue DAG; read the relevant issue bodies for semantic content, then infer the smallest\nhonest recommendation. Do not paste the generated graph here or mirror issue status.\n\n## Current sequencing recommendation\n\nAs of **2026-08-24**, FE-1476 (the September demo delivery) changes the recommendation from generic\npackage completion to a concrete CPS review-and-revise proof. After FE-1437 (the monorepo import)\nlands, open two fronts in parallel. The semantic front starts FE-1482 (the CPS plugin) against one\nworked fixture and settles FE-1480's requirements-model-to-SDCPN authority boundary before it\nimplements a projector; FE-1478 (net-to-requirements provenance) is part of that spine from its\nfirst types. The experience front advances FE-1438 (machine client-tool round-trip) and FE-1439\n(private sessions) far enough for a new reviewer conversation to target an existing document,\nwhile FE-1477/FE-1440 share one provider-routing implementation. Join the fronts at FE-1479\n(targeted re-elicitation), then drive the same path through FE-1423's pre-exposure gates and\nFE-1441 deployment.\n\nFE-1393's generic Gherkin artifact and FE-1387's second-target contract freeze no longer gate the\nSeptember proof. FE-1402, FE-1403, FE-1406, and FE-1431 supply only the completion, guidance,\nstrategy, and contract slices the CPS `review-and-revise` runbook consumes. FE-1481 selects\nYAML/Markdown export as the requirements-model inspection floor; broad UI follows only if the\nclosed loop is already proved.\n\n```text\nlegend:\n -[hard]-> native Linear blocker\n -[coord]-> either order; do not implement concurrently\n -[input]-> semantic input, not a blocker\n -[state-gate]-> condition in the world, not an issue edge\n\nnodes:\n FE-1437 [executed, landing] # history imported; HASH authoritative; PR pending\n FE-1476 [objective] # September reviewer demo\n FE-1482 [next, semantic] # concrete CPS plugin + review/revise runbook\n FE-1480 [decision, semantic] # model/projection authority, then projector\n FE-1478 [semantic proof] # provenance through all three registers\n FE-1438 [next, experience] # machine client-tool round-trip + application\n FE-1439 [next, experience] # existing-target reviewer session ownership\n FE-1477/FE-1440 [experience] # one provider-routing implementation\n FE-1479 [join] # targeted correction changes the live net\n FE-1481 [fallback] # structured model export before UI\n FE-1441 [deployed proof] # HASH deployment\n\nedges:\n FE-1449 -[hard]-> FE-1438\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n FE-1480 decision -[input]-> FE-1482, FE-1478\n FE-1402, FE-1403,\n FE-1406, FE-1431 -[input]-> FE-1482\n FE-1482, FE-1478,\n FE-1438, FE-1439 -[input]-> FE-1479\n FE-1479, FE-1440 -[input]-> FE-1441\n```\n\nHard-edge truth remains in Linear. The graph above is a deliberately focused recommendation,\nnot a second issue database.\n\n## Repository handoff threshold\n\nFE-1437 (the monorepo import; [execution plan](./hash-monorepo-import-plan.md)) was the authority\ncutover, not a general freeze on harness work. It was crossed on 2026-08-21:\n\n```text\nbrunch-lite authoritative (until 2026-08-21)\n FE-1434 + FE-1435 verdicts landed\n FE-1388/1389/1390/1399 review stack merged\n |\n v\n == FE-1437 import (executed) ==\n |\n v\nhashintel/hash authoritative (now)\n FE-1440 website wiring + FE-1441 deployment\n```\n\nThe standalone repository is frozen at SHA `43a0022918861846344b96a32cb94f92e2ee96ae` and is\nread-only reference material. All further work — including FE-1438 and FE-1439, which were not\nimport gates — happens in `hashintel/hash`. Do not run both repositories as writable authorities.\nClosing out the standalone repository's shared state (archival, access) is deferred and requires\nexplicit approval from Lu.\n\n## Open seams\n\n- **Projection authority — FE-1480.** The ticket assumes non-deterministic LLM inference from the\n requirements model to SDCPN, while ADR-0003 requires write-time-only semantic inference and a\n pure projection. A worked CPS transformation must assign every judgment to capture, fold,\n projection, or document application before the interface freezes.\n- **Controller and runbook.** The harness does not read the folded model or open issues back into\n the agent, and no plugin defines a job trajectory or stopping rule. FE-1482 must exercise the\n narrow `review-and-revise` loop; FE-1406 and FE-1402/FE-1403 are inputs, not parallel products.\n- **Reviewer target identity — FE-1439 × FE-1479.** The current host derives target-document\n identity from conversation identity. September requires a new reviewer conversation against an\n existing target without weakening owner isolation.\n- **Contract freeze — FE-1387.** The CPS target must stress the plugin contract before it freezes.\n The freeze follows the September semantic proof rather than gating it.\n- **Absence locator.** An absence capture carries no payload, but the fold needs a field-specific\n coordinate (anchor × slot). The plugin-contract spec records three worked cases; any envelope\n amendment belongs to the harness side of this seam.\n- **Structured-tap evidence — FE-1395 × capture store.** `resolve-conflict` currently rejects\n `user-affordance-payload` evidence. FE-1395 must decide the transport fact before the store\n rule can settle.\n- **Guidance placement — FE-1403 × FE-1406.** Plugin cards and the harness-shipped generic\n strategy quiver share authoring methods but not ownership. Packages export; hosts register.\n- **Telemetry vocabulary — FE-1385 × FE-1404 × FE-1423.** The probe surface, experiment\n accounting, and remote telemetry should share span vocabulary rather than invent it three\n times.\n- **Living-prototype charter.** The deployed elicitor as a cumulative record of proved and\n unproved behavior remains pre-charter until the infrastructure conversation settles.\n\nSettled seam decisions still governing open work: FE-1392 established the plugin-declared\nverbatim proposal floor, and ask accounting remains a read-time relation rather than an envelope\nfield. ADR-0003 keeps field-level structure below the capture's single epistemic status.\n\n## Exceptional roots\n\nThese project issues intentionally have no parent and are roots under the registry rule:\n\n- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n- **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n no owning map yet; closest to the motif/quiver strategy work.\n- **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n map has been chosen.\n- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n under FE-1357.\n- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n ownership boundaries are recorded in STEERING's issue projection before external mutation.\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CejrLBy1mv7nq81RC0k` - -``` -{ - "output": "sed: docs/INDEX.md: No such file or directory\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '63,80p' docs/INDEX.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --check -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md; git diff --stat -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md | sed -n '1,340p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cek53htAlJQWEntJALf` - -``` -{ - "output": "| [notes/deep-read-fe-1389](planning/process-model-elicitation/notes/deep-read-fe-1389.md) | active | FE-1401 / findings in FE-1420 | Deep-read of the walking skeleton: builder's account, spec-discharge table (issues 10/13 capabilities discharged; markdown floor contradicted in the UI), 12 findings; source of PR #10's backfilled record |\n| [notes/deep-read-fe-1390](planning/process-model-elicitation/notes/deep-read-fe-1390.md) | active | FE-1401 / probes on FE-1419 | Deep-read of the capture store: spec-discharge table, write-time tiering assessment (penciled item 7), the FE-1405 status-arity answer, and live-probed confirmation of FE-1419's capture-store claims plus one new aliasing hole; source of PR #11's backfilled record |\n| [plugin-contract-spec](planning/process-model-elicitation/plugin-contract-spec.md) | active | FE-1431 (spec issue); decided on FE-1405 | Provisional spec: a plugin is two schemas and two tables (model schema, proposal catalog, fold table, demand table) over the three-register IR (ADR-0003) — harness-machinery typology, standard-interiors library, grade-as-narrowing, derived fold rules; strains 4–7 and envelope pressure #2 held open with owners |\n\n## planning/\\_shared (cross-effort control documents)\n\n| Document | Status | Linear | Digest |\n| -------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [COORDINATION](planning/_shared/COORDINATION.md) | active | cross-project; maintained by arc-close | Current sequencing recommendation, soft cross-map edges, unresolved seams, and exceptional roots; hard blockers, state, and hierarchy remain in Linear |\n| [hash-monorepo-import-plan](planning/_shared/hash-monorepo-import-plan.md) | active until FE-1437 lands | FE-1437 | Native HASH assimilation plan: preserved history and child package workspaces under one Brunch context root, explicit authority cutover, exhaustive repository-material disposition, toolchain port, boundary gates, and verification |\n| [SPEC-LEDGER](planning/_shared/SPEC-LEDGER.md) | active until milestone-one closure | FE-1383 | Obligation-level status and evidence ledger for the elicitation-kernel specification; settles when the milestone closes |\n| [STEERING](planning/_shared/STEERING.md) | active | FE-1357 / FE-1476 | Current objective-driven strategy for the September CPS review-and-revise proof: evidence, architectural pressure, proof frontiers, cuts, issue projection, risks, and replan conditions |\n| [flue-architecture-cheatsheet](planning/_shared/flue-architecture-cheatsheet.md) | active | commented on FE-1383; feeds docs/agents/flue-routing.md | Architect's consolidation of all 21 Flue guide pages: direct structured generation uses `harness.prompt`; model-delegated work uses `useSubagent`; three-lane boundary summary and ranked divergence risks; reconciled against installed Flue 2.0.3 source |\n| [topology](planning/_shared/topology.md) | active | ratified → ADR-0002; N1 discharged by FE-1422 + FE-1392; local N5 implemented by FE-1391; N3 amended by FE-1437 | Pseudo-style verification of the package/app tree against the three-lane model and spec §12.2: portable ask/sweep protocols, Flue binding wiring, package boundaries, and application-only Brunch–Petrinaut composition |\n\n## planning/legibility-sweep (FE-1401 arc records)\n\n| Document | Status | Linear | Digest |\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cek55eiK2UBnapC75mu` - -``` -{ - "output": " libs/@hashintel/brunch-agent/docs/INDEX.md | 1 +\n .../docs/planning/_shared/COORDINATION.md | 93 ++++++++++++++--------\n 2 files changed, 59 insertions(+), 35 deletions(-)\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cek56pKeADZNxtxu42g` - -``` -{ - "output": "diff --git a/libs/@hashintel/brunch-agent/docs/INDEX.md b/libs/@hashintel/brunch-agent/docs/INDEX.md\nindex ca6ea309c0..b0834155a7 100644\n--- a/libs/@hashintel/brunch-agent/docs/INDEX.md\n+++ b/libs/@hashintel/brunch-agent/docs/INDEX.md\n@@ -71,6 +71,7 @@ _(empty — items settle out via the arc-close inbox sweep)_\n | [COORDINATION](planning/_shared/COORDINATION.md) | active | cross-project; maintained by arc-close | Current sequencing recommendation, soft cross-map edges, unresolved seams, and exceptional roots; hard blockers, state, and hierarchy remain in Linear |\n | [hash-monorepo-import-plan](planning/_shared/hash-monorepo-import-plan.md) | active until FE-1437 lands | FE-1437 | Native HASH assimilation plan: preserved history and child package workspaces under one Brunch context root, explicit authority cutover, exhaustive repository-material disposition, toolchain port, boundary gates, and verification |\n | [SPEC-LEDGER](planning/_shared/SPEC-LEDGER.md) | active until milestone-one closure | FE-1383 | Obligation-level status and evidence ledger for the elicitation-kernel specification; settles when the milestone closes |\n+| [STEERING](planning/_shared/STEERING.md) | active | FE-1357 / FE-1476 | Current objective-driven strategy for the September CPS review-and-revise proof: evidence, architectural pressure, proof frontiers, cuts, issue projection, risks, and replan conditions |\n | [flue-architecture-cheatsheet](planning/_shared/flue-architecture-cheatsheet.md) | active | commented on FE-1383; feeds docs/agents/flue-routing.md | Architect's consolidation of all 21 Flue guide pages: direct structured generation uses `harness.prompt`; model-delegated work uses `useSubagent`; three-lane boundary summary and ranked divergence risks; reconciled against installed Flue 2.0.3 source |\n | [topology](planning/_shared/topology.md) | active | ratified → ADR-0002; N1 discharged by FE-1422 + FE-1392; local N5 implemented by FE-1391; N3 amended by FE-1437 | Pseudo-style verification of the package/app tree against the three-lane model and spec §12.2: portable ask/sweep protocols, Flue binding wiring, package boundaries, and application-only Brunch–Petrinaut composition |\n \ndiff --git a/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md b/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\nindex 54e343fb83..8c2d3d1ca4 100644\n--- a/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n+++ b/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@ -2,9 +2,10 @@\n \n This is the cross-map coordination surface for the `brunch-agent` project. FE-1383 owns the\n milestone-one harness build; FE-1357 owns the September demo and process-model plugin design.\n-Linear is canonical for issue state, parentage, and hard `blocks` relations. This file owns\n-only the judgment Linear cannot express: the current project-wide recommendation, soft edges,\n-unresolved seams, and exceptional roots.\n+Linear is canonical for issue state, parentage, and hard `blocks` relations. The\n+[steering model](./STEERING.md) chooses the current objective, proof frontiers, and cuts under\n+pressure. This file projects that strategy onto mechanically available work: the current\n+project-wide recommendation, soft edges, unresolved seams, and exceptional roots.\n \n Before revising the recommendation, run\n `turbo run linear:graph --filter '@hashintel/brunch-agent'`. Its compact projection supplies the\n@@ -13,19 +14,22 @@ honest recommendation. Do not paste the generated graph here or mirror issue sta\n \n ## Current sequencing recommendation\n \n-As of **2026-08-21**, the FE-1437 authority cutover has been executed: the full brunch-lite\n-history is imported on `ln/fe-1437-hash-monorepo-import` in `hashintel/hash` (frozen standalone\n-SHA `43a0022918861846344b96a32cb94f92e2ee96ae`), every import gate re-verified. `hashintel/hash`\n-is authoritative; the standalone repository accepts no further implementation work. FE-1437\n-closes when the branch lands on `main` (squash merge, per convention). Part of FE-1440's website\n-wiring (the Brunch interactive-tool panel in `apps/petrinaut-website`) travelled with the import\n-branch; FE-1440 was trimmed on 2026-08-21 to the remaining mode wiring (mode switch, browser\n-identifier bootstrap, remote transport swap). After landing, advance FE-1438 (client-tool round-trip)\n-beside FE-1393 (plugin SDK and first projection); FE-1439 (private durable sessions) proceeds in\n-parallel. The integration stream joins at FE-1440 and deployment follows at FE-1441 (which also\n-waits on FE-1423's pre-exposure gates), while the harness stream reaches its contract-freeze\n-decision at FE-1387. FE-1402/FE-1403 form a parallel content/evaluation stream, without\n-displacing the two convergence edges.\n+As of **2026-08-24**, FE-1476 (the September demo delivery) changes the recommendation from generic\n+package completion to a concrete CPS review-and-revise proof. After FE-1437 (the monorepo import)\n+lands, open two fronts in parallel. The semantic front starts FE-1482 (the CPS plugin) against one\n+worked fixture and settles FE-1480's requirements-model-to-SDCPN authority boundary before it\n+implements a projector; FE-1478 (net-to-requirements provenance) is part of that spine from its\n+first types. The experience front advances FE-1438 (machine client-tool round-trip) and FE-1439\n+(private sessions) far enough for a new reviewer conversation to target an existing document,\n+while FE-1477/FE-1440 share one provider-routing implementation. Join the fronts at FE-1479\n+(targeted re-elicitation), then drive the same path through FE-1423's pre-exposure gates and\n+FE-1441 deployment.\n+\n+FE-1393's generic Gherkin artifact and FE-1387's second-target contract freeze no longer gate the\n+September proof. FE-1402, FE-1403, FE-1406, and FE-1431 supply only the completion, guidance,\n+strategy, and contract slices the CPS `review-and-revise` runbook consumes. FE-1481 selects\n+YAML/Markdown export as the requirements-model inspection floor; broad UI follows only if the\n+closed loop is already proved.\n \n ```text\n legend:\n@@ -36,29 +40,30 @@ legend:\n \n nodes:\n FE-1437 [executed, landing] # history imported; HASH authoritative; PR pending\n- FE-1438 [next] # client-tool round-trip\n- FE-1439 [next, parallel] # private durable sessions\n- FE-1440 [join, partly landed] # website elicitor mode; panel wiring on import branch\n- FE-1441 [post-landing] # HASH deployment\n- FE-1393 [next] # plugin SDK + first projection\n- FE-1387 [after-FE-1393] # second pack + contract freeze\n- FE-1395 [coordination] # full affordance set\n- FE-1402 [parallel, content] # completion contract\n- FE-1403 [parallel, content] # interviewing guidance\n- FE-1404 [after-content] # armed baseline\n+ FE-1476 [objective] # September reviewer demo\n+ FE-1482 [next, semantic] # concrete CPS plugin + review/revise runbook\n+ FE-1480 [decision, semantic] # model/projection authority, then projector\n+ FE-1478 [semantic proof] # provenance through all three registers\n+ FE-1438 [next, experience] # machine client-tool round-trip + application\n+ FE-1439 [next, experience] # existing-target reviewer session ownership\n+ FE-1477/FE-1440 [experience] # one provider-routing implementation\n+ FE-1479 [join] # targeted correction changes the live net\n+ FE-1481 [fallback] # structured model export before UI\n+ FE-1441 [deployed proof] # HASH deployment\n \n edges:\n FE-1449 -[hard]-> FE-1438\n- FE-1392 -[hard]-> FE-1393\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n- FE-1393 -[hard]-> FE-1387\n- FE-1402, FE-1403 -[hard]-> FE-1404\n- FE-1395 -[coord]-> FE-1438\n FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n- FE-1387 -[input]-> FE-1440\n+ FE-1480 decision -[input]-> FE-1482, FE-1478\n+ FE-1402, FE-1403,\n+ FE-1406, FE-1431 -[input]-> FE-1482\n+ FE-1482, FE-1478,\n+ FE-1438, FE-1439 -[input]-> FE-1479\n+ FE-1479, FE-1440 -[input]-> FE-1441\n ```\n \n Hard-edge truth remains in Linear. The graph above is a deliberately focused recommendation,\n@@ -90,8 +95,18 @@ explicit approval from Lu.\n \n ## Open seams\n \n-- **Contract freeze — FE-1387.** The process-model target must stress the plugin contract before\n- it freezes. FE-1393 makes the contract programmable; FE-1387 remains the cross-map hinge.\n+- **Projection authority — FE-1480.** The ticket assumes non-deterministic LLM inference from the\n+ requirements model to SDCPN, while ADR-0003 requires write-time-only semantic inference and a\n+ pure projection. A worked CPS transformation must assign every judgment to capture, fold,\n+ projection, or document application before the interface freezes.\n+- **Controller and runbook.** The harness does not read the folded model or open issues back into\n+ the agent, and no plugin defines a job trajectory or stopping rule. FE-1482 must exercise the\n+ narrow `review-and-revise` loop; FE-1406 and FE-1402/FE-1403 are inputs, not parallel products.\n+- **Reviewer target identity — FE-1439 × FE-1479.** The current host derives target-document\n+ identity from conversation identity. September requires a new reviewer conversation against an\n+ existing target without weakening owner isolation.\n+- **Contract freeze — FE-1387.** The CPS target must stress the plugin contract before it freezes.\n+ The freeze follows the September semantic proof rather than gating it.\n - **Absence locator.** An absence capture carries no payload, but the fold needs a field-specific\n coordinate (anchor × slot). The plugin-contract spec records three worked cases; any envelope\n amendment belongs to the harness side of this seam.\n@@ -114,9 +129,17 @@ field. ADR-0003 keeps field-level structure below the capture's single epistemic\n \n These project issues intentionally have no parent and are roots under the registry rule:\n \n-- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** Deferred post-September\n- consumer topology; returns after in-Petrinaut staging proves itself.\n+- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n+ September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n+ the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n - **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n no owning map yet; closest to the motif/quiver strategy work.\n - **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n+- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n+ map has been chosen.\n+- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n+ under FE-1357.\n+- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n+ steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n+ ownership boundaries are recorded in STEERING's issue projection before external mutation.\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `apply_patch` - -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@\n- FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n+ FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439\n@@\n-These project issues intentionally have no parent and are roots under the registry rule:\n+These project issues currently have no parent. Some are intentional roots; the temporary or\n+unresolved roots are named here until their Linear parentage is settled:\n*** End Patch" -} -``` - -## User - -**Tool Result:** `TU-034CekGiiRgw1pZ1J1qAGT` - -``` -{ - "files": [ - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n@@ -56,9 +56,9 @@\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n- FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439, FE-1393\n+ FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439\n FE-1480 decision -[input]-> FE-1482, FE-1478\n FE-1402, FE-1403,\n FE-1406, FE-1431 -[input]-> FE-1482\n FE-1482, FE-1478,\n@@ -126,9 +126,10 @@\n field. ADR-0003 keeps field-level structure below the capture's single epistemic status.\n \n ## Exceptional roots\n \n-These project issues intentionally have no parent and are roots under the registry rule:\n+These project issues currently have no parent. Some are intentional roots; the temporary or\n+unresolved roots are named here until their Linear parentage is settled:\n \n - **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n", - "type": "update", - "additions": 3, - "deletions": 2 - } - ], - "summary": "update: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md (+3/-2)" -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '44,74p' docs/planning/_shared/COORDINATION.md; sed -n '120,155p' docs/planning/_shared/COORDINATION.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CekNwpeOY5YQMx9PhLB` - -``` -{ - "output": " FE-1482 [next, semantic] # concrete CPS plugin + review/revise runbook\n FE-1480 [decision, semantic] # model/projection authority, then projector\n FE-1478 [semantic proof] # provenance through all three registers\n FE-1438 [next, experience] # machine client-tool round-trip + application\n FE-1439 [next, experience] # existing-target reviewer session ownership\n FE-1477/FE-1440 [experience] # one provider-routing implementation\n FE-1479 [join] # targeted correction changes the live net\n FE-1481 [fallback] # structured model export before UI\n FE-1441 [deployed proof] # HASH deployment\n\nedges:\n FE-1449 -[hard]-> FE-1438\n FE-1438, FE-1439,\n FE-1437 -[hard]-> FE-1440\n FE-1437, FE-1439,\n FE-1423 -[hard]-> FE-1441\n FE-1437 branch on main -[state-gate]-> FE-1438, FE-1439\n FE-1480 decision -[input]-> FE-1482, FE-1478\n FE-1402, FE-1403,\n FE-1406, FE-1431 -[input]-> FE-1482\n FE-1482, FE-1478,\n FE-1438, FE-1439 -[input]-> FE-1479\n FE-1479, FE-1440 -[input]-> FE-1441\n```\n\nHard-edge truth remains in Linear. The graph above is a deliberately focused recommendation,\nnot a second issue database.\n\n## Repository handoff threshold\n\nFE-1437 (the monorepo import; [execution plan](./hash-monorepo-import-plan.md)) was the authority\n times.\n- **Living-prototype charter.** The deployed elicitor as a cumulative record of proved and\n unproved behavior remains pre-charter until the infrastructure conversation settles.\n\nSettled seam decisions still governing open work: FE-1392 established the plugin-declared\nverbatim proposal floor, and ask accounting remains a read-time relation rather than an envelope\nfield. ADR-0003 keeps field-level structure below the capture's single epistemic status.\n\n## Exceptional roots\n\nThese project issues currently have no parent. Some are intentional roots; the temporary or\nunresolved roots are named here until their Linear parentage is settled:\n\n- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n- **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n no owning map yet; closest to the motif/quiver strategy work.\n- **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n map has been chosen.\n- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n under FE-1357.\n- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n ownership boundaries are recorded in STEERING's issue projection before external mutation.\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "jq -r '.scripts | to_entries[] | select(.key|test(\"^(lint|test|build|fix:format)\")) | \"\\(.key)=\\(.value)\"' package.json | sed -n '1,160p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "yarn oxfmt --help | sed -n '1,180p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git status --short; printf '%s\\n' '--- manifest'; git status --short -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "find docs/inbox -maxdepth 1 -type f -not -name '.DS_Store' -not -name '.gitkeep' -print 2>/dev/null; printf '%s\\n' '--- spec ledger references to affected obligations'; rg -n \"FE-147|FE-148|CPS|projection|Petrinaut|plugin\" docs/planning/_shared/SPEC-LEDGER.md | head -100", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CekYfG1MyOTg5hrDmVz` - -``` -{ - "output": "fix:format=oxfmt --write\nlint=npm-run-all --continue-on-error \"lint:*\"\nlint:constraints=yarn constraints\nlint:eslint=CARGO_TERM_PROGRESS_WHEN=never turbo --continue=always lint:eslint --\nlint:format=oxfmt --check\nlint:license-in-workspaces=yarn workspace @local/repo-chores exe scripts/check-license-in-workspaces.ts\nlint:markdownlint=mise exec --env dev markdownlint-cli2 -- markdownlint-cli2\nlint:skill=yarn agents:skill-management validate\nlint:taplo=taplo fmt --check\nlint:tsc=mise run lint:tsc\nlint:yarn-deduplicate=yarn dedupe --strategy highest --check\ntest=npm-run-all --continue-on-error \"test:*\"\ntest:integration=CARGO_TERM_PROGRESS_WHEN=never turbo run test:integration --env-mode=loose --\ntest:playwright=CARGO_TERM_PROGRESS_WHEN=never turbo run test:integration --env-mode=loose --filter @tests/hash-playwright --\ntest:unit=CARGO_TERM_PROGRESS_WHEN=never turbo run test:unit --env-mode=loose --\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CekYg2JVLrzgKw7ueZr` - -``` -{ - "output": "Usage: [-c=PATH] [PATH]...\n\nMode Options:\n --init Initialize `.oxfmtrc.json` with default values\n --migrate=SOURCE Migrate configuration to `.oxfmtrc.json` from specified source\n Available sources: prettier, biome\n --lsp Start language server protocol (LSP) server\n --stdin-filepath=PATH Specify the file name to use to infer which parser to use\n\nOutput Options:\n --write Format and write files in place (default)\n --check Check if files are formatted, also show statistics\n --list-different List files that would be changed\n\nConfig Options\n -c, --config=PATH Path to the configuration file (.json, .jsonc, .ts, .mts, .cts, .js,\n .mjs, .cjs)\n --disable-nested-config Do not search for configuration files in subdirectories\n\nIgnore Options\n --ignore-path=PATH Path to ignore file(s). Can be specified multiple times. If not\n specified, .gitignore and .prettierignore in the current directory are\n used.\n --with-node-modules Format code in node_modules directory (skipped by default)\n\nRuntime Options\n --no-error-on-unmatched-pattern Do not exit with error when pattern is unmatched\n --threads=INT Number of threads to use. Set to 1 for using only 1 CPU core.\n\nAvailable positional items:\n PATH Single file, path or list of paths. Glob patterns are also supported.\n (Be sure to quote them, otherwise your shell may expand them before\n passing.) Exclude patterns with `!` prefix like `'!**/fixtures/*.js'`\n are also supported. If not provided, current working directory is used.\n\nAvailable options:\n -h, --help Prints help information\n -V, --version Prints version information\n\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CekYgQWoicWIdAeBogw` - -``` -{ - "output": " M libs/@hashintel/brunch-agent/docs/INDEX.md\n M libs/@hashintel/brunch-agent/docs/agents/git-workflow.md\n M libs/@hashintel/brunch-agent/docs/agents/issue-writing.md\n M libs/@hashintel/brunch-agent/docs/agents/legibility.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/hash-monorepo-import-plan.md\n?? libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n--- manifest\n M libs/@hashintel/brunch-agent/docs/INDEX.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\n?? libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CekYgod3wfyc7qMtIAB` - -``` -{ - "output": "--- spec ledger references to affected obligations\n20:| Shell separation: plugins→core only; core imports no substrate; binding imports both | §4, §12.2 | **discharged** | boundary gates (FE-1388/FE-1399, `test/boundaries.test.ts`); `workspacePackages()` derives groups from manifest; `plugin-gherkin` imports `@brunch/core` only |\n21:| Package topology | §12.2 | **partial** | `core` (+`testing` subpath), `binding-flue`, `transport-aisdk`, `plugin-gherkin`, `apps/brunch-agent` exist; `plugin-assurance` pending. FE-1437 imported them to their native HASH destinations and preserved the existing dependency boundaries. Topology pin derives from §12.2 itself (FE-1400 `ef00201`); FE-1436 adds the transport-only dependency gate. |\n26:| Host-authored thin agent calling `useElicitation(plugin, session)` | §12.1 | **discharged** | `apps/brunch-agent/src/agents/gherkin-elicitor.ts`; FE-1392 adds host-owned immutable session/document and transport wiring |\n29:| Remote-parity constraints (pinned agentName, storage outside plugin, no dynamic agents) | §12.5 | **discharged** | pinned-identity gates (FE-1399/FE-1400); storage port in binding (FE-1390) |\n40:| Confidence qualitative, never a scalar | §5 | **partial** | non-empty string only; `\"0.93\"` accepted. Vocabulary is settled by the plugin-contract spec as `firm | hedged | speculative`; its proposed store refusal rule for numeric-parsing strings remains to implement |\n43:| One epistemic status per capture | §5 | **discharged**, with named friction | Status is the proposal union's discriminant, coupled to provenance shape — per-field status is unrepresentable, and payload-smuggling it breaks dedup identity. This was FE-1405's central input (deep-read FE-1390, tiering section); the arc consumed it _without_ amendment — one status per capture survives, and the structure that wanted per-field status lives below it in proposal interiors (ADR-0003, plugin-contract spec) |\n49:| `project` + typed loss report; `validate`; optional `reconcile`; purity (C2) | §6.1 | **pending** | FE-1392 adds only the plugin-declared `statement-noted` verbatim proposal floor; operations remain FE-1393 |\n52:| Duplicate detection free for flat-record plugins | §6.2 | **partial** | near-identical advisory fires for string payloads only; a flat record gets none |\n55:| Cadence as policy (§6.4) | §6.4 | **partial** | FE-1392 makes successful sweep the cadence boundary and keeps projection/validation read-time-only, leaving sweep outcome unchanged. Concrete operations remain absent until FE-1393 |\n90:| Only the true user's side is evidence; injected entries structurally non-user | §9.4 | **partial** | FE-1391 verifies role/purpose against the public projection, refuses signal/advisory text, and classifies affordance replies only from the harness-owned reply-binding signal. The kickoff remains a machine-authored `user` entry until FE-1420/FE-1385 move it to `useInitialData`; FE-1396 still owns briefing-never-evidence |\n92:| Storage port: harness-defined, binding-implemented, plugin-blind (C1) | §9.6 | **discharged for the local target** | core owns capture/archive/anchoring semantics; `binding-flue` owns the file implementation; plugins cannot import the binding (FE-1390 + FE-1391) |\n94:| Compaction vs. durable log | §9.7 | **partial — source-settled, behavioral pin open** | Flue 2.0.3's append-only stream contract and implementation show compaction appends a canonical record, rewrites only model context, preserves the public message projection, and leaves `state_write` reduction untouched. The source-read record reshapes FE-1386 to one upgrade pin; `test/open-gaps.ts` remains until behavioral proof lands |\n101:| Caps 6–8, incl. entry-projection read | §10 | **discharged** | FE-1391 supplies the public reader/archive. FE-1392 uses direct structured `harness.prompt` inside a durable tool whose peek/extract/refresh/apply boundaries use `step.do`; the mounted runtime pins same-response finish-hook steering and refresh-before-apply |\n107:| Plugin ownership: packs, forms, validators | §11.1 | **partial** | `plugin-gherkin` now owns its one FE-1392 proposal declaration/schema and target identity; packs, forms, validators, fold, and demand table remain FE-1393/FE-1387 work |\n109:| Smallest honest plugin as a standing bar | §11.3 | **partial** | `statement-noted.test.ts` and the core plugin fixture encode the one-type verbatim floor and reject undeclared parsed/pointer shape; the standing bar must grow with FE-1393's operations |\n122:| Invariants 3, 8 (projection loss, equivalent projection) | §14.1 | **pending** | no `project` exists |\n124:| Gating tests (reprojection, minimal pairs, black-box authoring) | §14.3 | **pending** | — |\n", - "exitCode": 0 -} -``` - -## Assistant - -The reviewed plan now makes the production sweep itself part of “done”: the correction must originate in the reviewer’s utterance and become a real superseding capture. It also requires the prebuilt demo baseline to begin from valid source conversation/capture state, not a hand-authored model with decorative provenance. I’m moving through the package’s verification and control-surface closeout now. - -**Tool Use:** `shell_command` - -```json -{ - "command": "yarn markdownlint-cli2 libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md && turbo run lint:eslint --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CekpIOxnYwkta6op8sD` - -``` -{ - "output": "Usage Error: Couldn't find a script named \"markdownlint-cli2\".\n\n$ yarn run [--inspect] [--inspect-brk] [-T,--top-level] [-B,--binaries-only] [--require #0] <scriptName> ...\n", - "exitCode": 1 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "mise exec --env dev markdownlint-cli2 -- markdownlint-cli2 libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md && turbo run lint:eslint --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034Cel1wEvg9lhiTrj1jis` - -``` -{ - "output": "markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)\nFinding: libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md *.md **/*.md **/*.mdc !**/LICENSE*.md !**/AGENTS*.md !node_modules/** !target/** !**/_temp/** !**/_ai/** !**/.cursor/plans/** !libs/@hashintel/ds-components/** !libs/@hashintel/ds-helpers/** !libs/@hashintel/brunch-agent/docs/**\nLinting: 256 file(s)\nSummary: 0 error(s)\nturbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running lint:eslint in 1 packages\n• Remote caching disabled\n@local/eslint:build: cache miss, executing 721d5b9a378f6a70\n@hashintel/brunch-agent:lint:eslint: cache miss, executing 59f21353e1c3ce33\n@hashintel/brunch-agent:lint:eslint: Found 0 warnings and 0 errors.\n@hashintel/brunch-agent:lint:eslint: Finished in 651ms on 29 files with 110 rules using 16 threads.\n\n Tasks: 2 successful, 2 total\nCached: 0 cached, 2 total\n Time: 5.875s \n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "yarn oxfmt --check libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CelM4ZMYxG1P5RAJY93` - -``` -{ - "output": "(node:57738) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/lunelson/Code/hashintel/hash/oxfmt.config.ts?cache=1787567470038 is not specified and it doesn't parse as CommonJS.\nReparsing as ES module because module syntax was detected. This incurs a performance overhead.\nTo eliminate this warning, add \"type\": \"module\" to /Users/lunelson/Code/hashintel/hash/package.json.\n(Use `node --trace-warnings ...` to show where the warning was created)\nChecking formatting...\n\nExpected at least one target file. All matched files may have been excluded by ignore rules.\n", - "exitCode": 2 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "yarn bin prettier || true; yarn bin markdownlint-cli2 || true; rg -n \"brunch-agent/docs|\\.md\" oxfmt.config.ts .prettierignore 2>/dev/null | head -80", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CelVd9SekIht8fTjx82` - -``` -{ - "output": "Usage Error: Couldn't find a binary named \"prettier\" for package \"hash@workspace:.\"\n\n$ yarn bin [-v,--verbose] [--json] [name]\nUsage Error: Couldn't find a binary named \"markdownlint-cli2\" for package \"hash@workspace:.\"\n\n$ yarn bin [-v,--verbose] [--json] [name]\noxfmt.config.ts:61: \"**/LICENSE*.md\",\noxfmt.config.ts:64: \"libs/@hashintel/brunch-agent/docs/**\",\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run lint:tsc --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034Celf5ywUscVmI5cF36u` - -``` -{ - "output": "turbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running lint:tsc in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:lint:tsc: cache miss, executing 50584d5be80cc7ec\n\n Tasks: 1 successful, 1 total\nCached: 0 cached, 1 total\n Time: 2.681s \n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run test:unit --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CelqMiYvo5Mj62t4N5A` - -``` -{ - "output": "turbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running test:unit in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:test:unit: cache miss, executing d4c5393b6af87011\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: RUN v4.1.10 /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ✓ test/architecture/boundaries.test.ts (29 tests) 33ms\n@hashintel/brunch-agent:test:unit: ✓ test/naming.test.ts (11 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/capture-store.test.ts (24 tests) 21ms\n@hashintel/brunch-agent:test:unit: ✓ test/anchoring.test.ts (6 tests) 4ms\n@hashintel/brunch-agent:test:unit: ✓ test/session-log.test.ts (4 tests) 8ms\n@hashintel/brunch-agent:test:unit: ✓ test/sweep-protocol.test.ts (9 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/workspace.test.ts (7 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/linear-project-graph.test.ts (2 tests) 14ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/docs-index.test.ts (7 tests) 12ms\n@hashintel/brunch-agent:test:unit: ✓ test/ask-protocol.test.ts (9 tests) 2ms\n@hashintel/brunch-agent:test:unit: stderr | test/architecture/open-gaps.test.ts\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ⚠ 3 verification gaps are open (spec §14.5 and friends):\n@hashintel/brunch-agent:test:unit: · restart-durability — FE-1396 (spec §14.5)\n@hashintel/brunch-agent:test:unit: · compaction-vs-durable-history — FE-1386 (spec §9.7, §14.5)\n@hashintel/brunch-agent:test:unit: · interpretation-render-plugin-seam — FE-1394 (spec §7.6, §14.5)\n@hashintel/brunch-agent:test:unit: Closing one means deleting its entry in the commit that lands its proof.\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ✓ test/architecture/open-gaps.test.ts (2 tests) 1ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/baseline-runner.test.ts (4 tests) 1867ms\n@hashintel/brunch-agent:test:unit: ✓ checkpoints a truncated expert reply and stops before another interviewer call 756ms\n@hashintel/brunch-agent:test:unit: ✓ resume regenerates a trailing truncated expert reply before continuing 636ms\n@hashintel/brunch-agent:test:unit: ✓ continues a truncated final delivery without sending checkpoint metadata 311ms\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: Test Files 12 passed (12)\n@hashintel/brunch-agent:test:unit: Tests 114 passed (114)\n@hashintel/brunch-agent:test:unit: Start at 12:31:31\n@hashintel/brunch-agent:test:unit: Duration 2.08s (transform 76ms, setup 0ms, import 155ms, tests 1.97s, environment 0ms)\n@hashintel/brunch-agent:test:unit: \n\n Tasks: 1 successful, 1 total\nCached: 0 cached, 1 total\n Time: 4.748s \n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run build --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034Cem7UfNqOgfA1Uva9tF` - -``` -{ - "output": "turbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running build in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:build: cache bypass, force executing d9f7ac2bc077ccba\n@hashintel/brunch-agent:build: vite v8.1.0 building client environment for production...\n@hashintel/brunch-agent:build: \u001b[2K\r@hashintel/brunch-agent:build: transforming...✓ 13 modules transformed.\n@hashintel/brunch-agent:build: rendering chunks...\n@hashintel/brunch-agent:build: computing gzip size...\n@hashintel/brunch-agent:build: dist/client-tools.js 0.12 kB │ gzip: 0.12 kB\n@hashintel/brunch-agent:build: dist/storage.js 0.20 kB │ gzip: 0.15 kB\n@hashintel/brunch-agent:build: dist/plugin-BlyisbcK.js 0.68 kB │ gzip: 0.41 kB │ map: 3.09 kB\n@hashintel/brunch-agent:build: dist/testing/index.js 0.69 kB │ gzip: 0.41 kB │ map: 2.13 kB\n@hashintel/brunch-agent:build: dist/naming-DhqH8ada.js 0.78 kB │ gzip: 0.48 kB │ map: 4.29 kB\n@hashintel/brunch-agent:build: dist/session-log-1mbM2HlS.js 6.44 kB │ gzip: 2.22 kB │ map: 20.44 kB\n@hashintel/brunch-agent:build: dist/index.js 25.31 kB │ gzip: 7.67 kB │ map: 80.29 kB\n@hashintel/brunch-agent:build: \n@hashintel/brunch-agent:build: ✓ built in 23ms\n\n Tasks: 1 successful, 1 total\nCached: 0 cached, 1 total\n Time: 2.689s \n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `skill` - -```json -{ - "name": "arc-close", - "arguments": "Close the steering-model control-surface arc. Manifest: docs/planning/_shared/STEERING.md, docs/planning/_shared/COORDINATION.md, docs/INDEX.md. Do not mutate Linear or unrelated worktree files." -} -``` - -## User - -**Tool Result:** `TU-034CemKIXqnng601WefzlI` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"arc-close\">\n# arc-close Skill\n\n\nRead `docs/agents/arc-close.md` from the Brunch context root and execute its five steps in order.\nThis wrapper carries no duplicate procedure. Report the required checks and whether each\nconditional control surface changed; never persist a no-op evaluation.\n\n\nARGUMENTS: Close the steering-model control-surface arc. Manifest: docs/planning/_shared/STEERING.md, docs/planning/_shared/COORDINATION.md, docs/INDEX.md. Do not mutate Linear or unrelated worktree files.\n\nBase directory for this skill: file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/.agents/skills/arc-close\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run linear:graph --filter '@hashintel/brunch-agent' 2>/dev/null | sed -n '/project brunch-agent/,$p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --check -- libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md; if rg -n '[[:blank:]]+$' libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md; then exit 1; fi; printf '%s\\n' 'no whitespace errors'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "printf '%s\\n' '--- inbox'; find docs/inbox -maxdepth 1 -type f -not -name '.DS_Store' -not -name '.gitkeep' -print 2>/dev/null; printf '%s\\n' '--- manifest status'; git status --short -- docs/INDEX.md docs/planning/_shared/COORDINATION.md docs/planning/_shared/STEERING.md; printf '%s\\n' '--- unrelated status retained'; git status --short -- docs/agents/git-workflow.md docs/agents/issue-writing.md docs/agents/legibility.md docs/planning/_shared/hash-monorepo-import-plan.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CemaoZZtzEmqq2jtwmT` - -``` -{ - "output": "@hashintel/brunch-agent:linear:graph: project brunch-agent open=35 hard=16\n@hashintel/brunch-agent:linear:graph: legend: L=hard-dependency layer; p=parent; <=blocked by; =>blocks; *=outside project\n@hashintel/brunch-agent:linear:graph: L0 FE-1331 [Todo root] | Start elicitation from Petrinaut's create-new-net flow\n@hashintel/brunch-agent:linear:graph: L0 FE-1334 [Todo root] | Offer user a surprising scenario of their model\n@hashintel/brunch-agent:linear:graph: L0 FE-1357 [In progress root] | Plan the September elicitation demo and plugin specification\n@hashintel/brunch-agent:linear:graph: L0 FE-1383 [In progress root] | Build the first complete elicitation interview\n@hashintel/brunch-agent:linear:graph: L0 FE-1386 [Backlog p:FE-1383] | Test durable history across transcript compaction\n@hashintel/brunch-agent:linear:graph: L0 FE-1393 [Todo p:FE-1383] =>FE-1384,FE-1387,FE-1394 | Produce the first Gherkin artifact through the plugin SDK\n@hashintel/brunch-agent:linear:graph: L0 FE-1395 [Todo p:FE-1383] =>FE-1385 | Add choices, questionnaires, and explicit absence replies\n@hashintel/brunch-agent:linear:graph: L0 FE-1402 [Next up p:FE-1357] =>FE-1404 | Define and rehearse the elicitation completion contract\n@hashintel/brunch-agent:linear:graph: L0 FE-1403 [Next up p:FE-1357] =>FE-1404 | Assemble and test the CPS interview guidance\n@hashintel/brunch-agent:linear:graph: L0 FE-1406 [Next up root] | Design reusable elicitation strategies\n@hashintel/brunch-agent:linear:graph: L0 FE-1407 [Next up p:FE-1357] | Catalogue elicitor failures that published measures miss\n@hashintel/brunch-agent:linear:graph: L0 FE-1420 [Next up p:FE-1383] | Make affordance handling safe under retries and abandonment\n@hashintel/brunch-agent:linear:graph: L0 FE-1431 [Todo p:FE-1357] | Define declarative plugin authoring\n@hashintel/brunch-agent:linear:graph: L0 FE-1437 [Ready for review p:FE-1433] =>FE-1440,FE-1441 | Move brunch-agent into hashintel/hash with its history\n@hashintel/brunch-agent:linear:graph: L0 FE-1438 [Todo p:FE-1433] =>FE-1440 | Build and repair Petrinaut nets through client tools\n@hashintel/brunch-agent:linear:graph: L0 FE-1439 [Todo p:FE-1433] =>FE-1440,FE-1441 | Keep elicitation sessions private and durable per browser\n@hashintel/brunch-agent:linear:graph: L0 FE-1448 [Ready for review p:FE-1433] | Let Petrinaut hosts render interactive chat tools\n@hashintel/brunch-agent:linear:graph: L0 FE-1472 [Triage root] | Evaluate the cost of pinning bedrock-sdk's nested Anthropic SDK\n@hashintel/brunch-agent:linear:graph: L0 FE-1476 [Todo root] | Prepare September demo\n@hashintel/brunch-agent:linear:graph: L0 FE-1477 [Next up root] | Define the routing logic between Petrinaut AI and the brunch elicitor\n@hashintel/brunch-agent:linear:graph: L0 FE-1478 [Todo root] | Provide provenance from a generated net back to the requirements graph\n@hashintel/brunch-agent:linear:graph: L0 FE-1479 [Todo root] | Update a section of the net through targeted re-elicitation\n@hashintel/brunch-agent:linear:graph: L0 FE-1480 [Todo root] | Infer requirements graph to SDCPN in Petrinaut editor\n@hashintel/brunch-agent:linear:graph: L0 FE-1481 [Todo root] | Show the requirements graph in UI OR export as YAML or Markdown\n@hashintel/brunch-agent:linear:graph: L0 FE-1482 [Next up root] | Add Cyber-physical process modelling plugin to brunch agent\n@hashintel/brunch-agent:linear:graph: L1 FE-1384 [Backlog p:FE-1383] <=FE-1393 | Generate replay tests for the harness rules\n@hashintel/brunch-agent:linear:graph: L1 FE-1387 [Backlog p:FE-1383] <=FE-1393 | Choose a second target and stabilize the plugin interface\n@hashintel/brunch-agent:linear:graph: L1 FE-1394 [Todo p:FE-1383] <=FE-1393 =>FE-1385,FE-1396 | Preserve conflicts until the user resolves them\n@hashintel/brunch-agent:linear:graph: L1 FE-1404 [Next up p:FE-1357] <=FE-1402,FE-1403 | Run the third baseline with completion and interview guidance\n@hashintel/brunch-agent:linear:graph: L1 FE-1440 [Todo p:FE-1433] <=FE-1437,FE-1438,FE-1439 =>FE-1442 | Ship the elicitor in demo.petrinaut.org’s chat panel\n@hashintel/brunch-agent:linear:graph: L2 FE-1385 [Backlog p:FE-1383] <=FE-1394,FE-1395 | Expand the dev app into a target gallery and diagnostic view\n@hashintel/brunch-agent:linear:graph: L2 FE-1396 [Todo p:FE-1383] <=FE-1394 =>FE-1423 | Restore interview context after resume and restart\n@hashintel/brunch-agent:linear:graph: L2 FE-1442 [Todo p:FE-1433] <=FE-1440 | Show live captures and completion accounting in the demo\n@hashintel/brunch-agent:linear:graph: L3 FE-1423 [Todo p:FE-1357] <=FE-1396 =>FE-1441 | Require safe remote access to the elicitor server\n@hashintel/brunch-agent:linear:graph: L4 FE-1441 [Todo p:FE-1433] <=FE-1423,FE-1437,FE-1439 | Deploy the elicitor server behind the remote-release checks\n@hashintel/brunch-agent:linear:graph: cycles: none\n\n Tasks: 1 successful, 1 total\nCached: 0 cached, 1 total\n Time: 3.73s \n\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cemap9xVYDSej2U9SVL` - -``` -{ - "output": "no whitespace errors\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cemape2wKP2Oug8G2WK` - -``` -{ - "output": "--- inbox\n--- manifest status\n M docs/INDEX.md\n M docs/planning/_shared/COORDINATION.md\n?? docs/planning/_shared/STEERING.md\n--- unrelated status retained\n M docs/agents/git-workflow.md\n M docs/agents/issue-writing.md\n M docs/agents/legibility.md\n M docs/planning/_shared/hash-monorepo-import-plan.md\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git add libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md && git status --short && printf '%s\\n' '--- staged paths' && git diff --cached --name-only", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cemxbk8eOgW09TrYLmd` - -``` -{ - "output": "M libs/@hashintel/brunch-agent/docs/INDEX.md\n M libs/@hashintel/brunch-agent/docs/agents/git-workflow.md\n M libs/@hashintel/brunch-agent/docs/agents/issue-writing.md\n M libs/@hashintel/brunch-agent/docs/agents/legibility.md\nM libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\nA libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/hash-monorepo-import-plan.md\n--- staged paths\nlibs/@hashintel/brunch-agent/docs/INDEX.md\nlibs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\nlibs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --cached --check && git diff --cached --stat && git commit -m \"Add September steering model\"", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CenBB5thhw0PWVBUWVn` - -``` -{ - "output": " libs/@hashintel/brunch-agent/docs/INDEX.md | 1 +\n .../docs/planning/_shared/COORDINATION.md | 98 ++++---\n .../brunch-agent/docs/planning/_shared/STEERING.md | 296 +++++++++++++++++++++\n 3 files changed, 358 insertions(+), 37 deletions(-)\n╭──────────────────────────────────────╮\n│ 🥊 lefthook v2.0.0 hook: pre-commit │\n╰──────────────────────────────────────╯\n│ yarn (skip) no matching staged files\n│ toml (skip) no files for inspection\n│ sqlfluff (skip) no files for inspection\n│ rust (skip) no files for inspection\n┃ oxfmt ❯ \n\n(node:65832) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/lunelson/Code/hashintel/hash/oxfmt.config.ts?cache=1787567541808 is not specified and it doesn't parse as CommonJS.\r\nReparsing as ES module because module syntax was detected. This incurs a performance overhead.\r\nTo eliminate this warning, add \"type\": \"module\" to /Users/lunelson/Code/hashintel/hash/package.json.\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\nExpected at least one target file. All matched files may have been excluded by ignore rules.\r\n\n┃ markdownlint ❯ \n\nmarkdownlint-cli2 v0.22.1 (markdownlint v0.40.0)\r\nFinding: libs/@hashintel/brunch-agent/docs/INDEX.md libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md !**/LICENSE*.md !**/AGENTS*.md !node_modules/** !target/** !**/_temp/** !**/_ai/** !**/.cursor/plans/** !libs/@hashintel/ds-components/** !libs/@hashintel/ds-helpers/** !libs/@hashintel/brunch-agent/docs/**\r\nLinting: 0 file(s)\r\nSummary: 0 error(s)\r\n\n \n ────────────────────────────────────\nsummary: (done in 0.93 seconds) \n✔️ oxfmt (0.40 seconds)\n✔️ markdownlint (0.85 seconds)\n[ln/fe-xxxx-whats-next d4e61bb23b] Add September steering model\n 3 files changed, 358 insertions(+), 37 deletions(-)\n create mode 100644 libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git status --short; git show --stat --oneline --summary HEAD", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git show --format= --name-only HEAD; git show --format= --check HEAD", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "nl -ba docs/planning/_shared/STEERING.md | sed -n '1,330p' | tail -20; nl -ba docs/planning/_shared/COORDINATION.md | sed -n '1,170p' | tail -45", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CenRkjCL9VDpfTx9Umd` - -``` -{ - "output": " M libs/@hashintel/brunch-agent/docs/agents/git-workflow.md\n M libs/@hashintel/brunch-agent/docs/agents/issue-writing.md\n M libs/@hashintel/brunch-agent/docs/agents/legibility.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/hash-monorepo-import-plan.md\nd4e61bb23b Add September steering model\n libs/@hashintel/brunch-agent/docs/INDEX.md | 1 +\n .../docs/planning/_shared/COORDINATION.md | 98 ++++---\n .../brunch-agent/docs/planning/_shared/STEERING.md | 296 +++++++++++++++++++++\n 3 files changed, 358 insertions(+), 37 deletions(-)\n create mode 100644 libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CenRl7UIjQsnLZeIYR3` - -``` -{ - "output": "libs/@hashintel/brunch-agent/docs/INDEX.md\nlibs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md\nlibs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CenRltj53l8AQQst4wC` - -``` -{ - "output": " 277\t| A bounded review-and-revise scenario can carry the September product claim without cold-start elicitation. | Medium. It is the written FE-1476 scenario, but Dora has not confirmed the use case. | The confirmed use case requires model creation rather than review, or the optimisation handoff requires fields absent from the fixture. |\n 278\t| A concrete CPS implementation will discover a better minimum plugin contract faster than completing the generic design first. | Medium-high. Gherkin deliberately under-stresses the interface; CPS is the first real consumer. | The first worked CPS transformation cannot be expressed without a reusable harness primitive that must precede it. Build that primitive, then return immediately to the vertical proof. |\n 279\t| Register 2 can be rich enough for pure SDCPN projection. | Low-medium. ADR-0003 requires it, but no real fold or projector exists and FE-1480 asserts non-determinism. | The worked transformation identifies an unavoidable semantic choice not represented in captures/model. Record it earlier or explicitly revisit the ADR. |\n 280\t| Three to five turns can produce a meaningful scoped correction. | Low. No CPS runbook has been rehearsed. | Two rehearsals exceed the budget or require unrelated context. Narrow the region, preload explicit context, or revise the demo claim rather than script fake success. |\n 281\t| The proven ask suspension can extend to document-application client tools. | Medium-low. The suspension spike succeeded, but the production transport intentionally refuses machine tool-result follow-ups. | The first tool round-trip cannot preserve correlation, durability, or non-user evidence semantics. Treat this as a critical integration blocker, not a UI detail. |\n 282\t| The production website and remote server can be joined inside the timebox. | Medium-low. Local host seams exist; provider routing, session ownership, deployment, and release gates do not yet converge. | A deployable path is not proved by the end of Frontier 2. Preserve the real semantic loop and seek an explicit demo-surface decision rather than quietly falling back to test-only wiring. |\n 283\t| Requirements-model UI is unnecessary for comprehension. | Medium. The reviewer needs inspectability, but FE-1481 explicitly permits export. | Rehearsal shows that provenance and delta cannot be understood from chat plus a structured export. |\n 284\t\n 285\t## Current choice\n 286\t\n 287\tStop treating “unblocked” as “next.” The next strategic move is to run Frontier 0 immediately and\n 288\topen Frontiers 1 and 2 in parallel: one worked CPS semantic slice and one existing-target reviewer\n 289\tsession/tool-return slice. Join them at the earliest correction, then drive the same bones through\n 290\tprovider routing and deployment. The generic Gherkin/plugin-freeze path and broad UI work wait for\n 291\tthat join.\n 292\t\n 293\tRevisit this choice as soon as Dora confirms the use case, the FE-1480 worked transformation lands,\n 294\tor either parallel frontier fails its first proof. A steering pass that only updates ticket status\n 295\tdoes not change this document; a new fact that changes the objective, proof spine, authority\n 296\tboundary, or cut line does.\n 102\t- **Controller and runbook.** The harness does not read the folded model or open issues back into\n 103\t the agent, and no plugin defines a job trajectory or stopping rule. FE-1482 must exercise the\n 104\t narrow `review-and-revise` loop; FE-1406 and FE-1402/FE-1403 are inputs, not parallel products.\n 105\t- **Reviewer target identity — FE-1439 × FE-1479.** The current host derives target-document\n 106\t identity from conversation identity. September requires a new reviewer conversation against an\n 107\t existing target without weakening owner isolation.\n 108\t- **Contract freeze — FE-1387.** The CPS target must stress the plugin contract before it freezes.\n 109\t The freeze follows the September semantic proof rather than gating it.\n 110\t- **Absence locator.** An absence capture carries no payload, but the fold needs a field-specific\n 111\t coordinate (anchor × slot). The plugin-contract spec records three worked cases; any envelope\n 112\t amendment belongs to the harness side of this seam.\n 113\t- **Structured-tap evidence — FE-1395 × capture store.** `resolve-conflict` currently rejects\n 114\t `user-affordance-payload` evidence. FE-1395 must decide the transport fact before the store\n 115\t rule can settle.\n 116\t- **Guidance placement — FE-1403 × FE-1406.** Plugin cards and the harness-shipped generic\n 117\t strategy quiver share authoring methods but not ownership. Packages export; hosts register.\n 118\t- **Telemetry vocabulary — FE-1385 × FE-1404 × FE-1423.** The probe surface, experiment\n 119\t accounting, and remote telemetry should share span vocabulary rather than invent it three\n 120\t times.\n 121\t- **Living-prototype charter.** The deployed elicitor as a cumulative record of proved and\n 122\t unproved behavior remains pre-charter until the infrastructure conversation settles.\n 123\t\n 124\tSettled seam decisions still governing open work: FE-1392 established the plugin-declared\n 125\tverbatim proposal floor, and ask accounting remains a read-time relation rather than an envelope\n 126\tfield. ADR-0003 keeps field-level structure below the capture's single epistemic status.\n 127\t\n 128\t## Exceptional roots\n 129\t\n 130\tThese project issues currently have no parent. Some are intentional roots; the temporary or\n 131\tunresolved roots are named here until their Linear parentage is settled:\n 132\t\n 133\t- **FE-1331 — start elicitation from Petrinaut's create-new-net flow.** ADR-0004 un-deferred this as\n 134\t September topology, while FE-1476's new reviewer scenario starts from an existing target. Keep\n 135\t the conflict visible until Dora confirms the use case and the ADR is amended if necessary.\n 136\t- **FE-1334 — offer the user a surprising scenario of their model.** A validation gesture with\n 137\t no owning map yet; closest to the motif/quiver strategy work.\n 138\t- **FE-1406 — design reusable elicitation strategies.** The cross-map home for the\n 139\t harness-shipped generic strategy quiver; intentionally independent of either delivery map.\n 140\t- **FE-1472 — evaluate the nested Anthropic SDK pin.** Unrelated triage root; no owning delivery\n 141\t map has been chosen.\n 142\t- **FE-1476 — prepare the September demo.** Temporary delivery root pending the recommended fold\n 143\t under FE-1357.\n 144\t- **FE-1477 through FE-1482 — September outcome slices.** PM-authored issues adopted by the\n 145\t steering model but not yet folded in Linear. The recommended parent is FE-1476; overlaps and\n 146\t ownership boundaries are recorded in STEERING's issue projection before external mutation.\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n '^## |^### ' docs/planning/_shared/STEERING.md; rg -n '^## |^### ' docs/planning/_shared/COORDINATION.md; rg -n '\\[STEERING\\]' docs/INDEX.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CenaJhj5wq8tcm8LEb5` - -``` -{ - "output": "21:## The September outcome\n58:## Where the system actually stands\n82:## The strategic bet\n114:## The elicitor architecture under this load\n160:## Proof frontiers and execution order\n166:### Frontier 0 — make the demo claim decidable\n182:### Frontier 1 — close the CPS semantic loop\n195:### Frontier 2 — close the reviewer control loop\n212:### Frontier 3 — converge on the deployed demo\n222:## What is deliberately cut\n239:## Issue projection\n273:## Beliefs, risks, and replan conditions\n285:## Current choice\n15:## Current sequencing recommendation\n72:## Repository handoff threshold\n96:## Open seams\n128:## Exceptional roots\n74:| [STEERING](planning/_shared/STEERING.md) | active | FE-1357 / FE-1476 | Current objective-driven strategy for the September CPS review-and-revise proof: evidence, architectural pressure, proof frontiers, cuts, issue projection, risks, and replan conditions |\n", - "exitCode": 0 -} -``` - -## Assistant - -Created and committed the [Brunch steering model](file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/STEERING.md#L1-L296), including: - -- September objective, architecture inventory, strategic bet, proof frontiers, cuts, risks, and issue projection. -- Updated [coordination sequencing](file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md#L15-L69). -- Oracle-reviewed acceptance criteria preventing fake provenance or hand-inserted corrections. -- Explicit FE-1480/ADR-0003 and FE-1331/ADR-0004 conflicts. - -No Linear changes were made. Verification passed: lint, type-check, 114 tests, build, documentation index/link gates. - -Commit: `d4e61bb23b` (`Add September steering model`) - -The next substantive discussion should be Frontier 0: the CPS fixture and FE-1480 authority boundary. - -## User - -OK this is very helpful. A few thoughts: - -- a more compact overview, in nested bullet-list or perhaps tree form, collecting issues to the frontiers they support, and calling out where more issues might need to be mapped, would be helpful -- the proof obligations list must also call out legibility: a proof without a legible surface, and ideally a human witness, is the proverbial tree that falls in the forest with nobody around to hear it. This is the legibility protocol, which I need in order to maintain my shared understanding -- your confidence assignments in the "beliefs..." table are mostly what I would assign as well; I would say my low-confidence hovers around the following two cuts that you made, mainly because I'm not confident that belief row 1 will hold, i.e. we will not be asked to show a cold-start elicitation - -> - Do not build a complete cold-start CPS interview, general target gallery, every affordance type, voice input, surprising-scenario generation, or broad telemetry vocabulary. -> - Do not implement a comprehensive CPS ontology. Support the fixture, the correction, and the optimisation handoff while keeping the data model honest about what it omits. - -Let's talk about Frontier 0 next. (ASIDE: we have an unclear topology of planning docs now, in terms of canonicality, order-of-reading, authority, scope; and we have several documents which are effectively stale and should move to `docs/_archive/` or be deleted - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "cat docs/agents/legibility.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CfkNUto2uMtzB5jmd3F` - -``` -{ - "output": "# Legibility protocol: render, read the strain, reconcile\n\nHow work arcs with significant agent-generated output close: produce legibility artifacts that\naid review and re-establish shared understanding between the humans and the agents carrying the\nwork. Companion to `documentation.md` (which governs where documents live; this file governs\nhow understanding is checked and kept).\n\nThe protocol serves one thesis, the same one the CI gates and the capture store serve in code:\n**no claim without a way for it to fail.** A document read in its own register can nod along\nwith itself; re-rendered into a different register, every claim must survive translation, and\nthe places where it doesn't are findings.\n\n## The move: render and read the strain\n\nAt the close of an arc, re-render its central artifact into another register and instruct the\nrenderer to report **every place the source resisted plain rendering** — a dangling referent, a\nterm defined nowhere, a prohibition citing nothing, a causal claim whose causality had to be\ninferred. The strain report _is_ the review yield; the rendering itself is a byproduct (often a\nuseful one — a teammate-readable account).\n\nRun renderings as fork subagents carrying the strain-report instruction, so the main thread\nreviews the findings instead of doing the translation. Instrumenting the collection raises the\nyield: the ir-design plain rendering returned seven strain points where an uninstrumented\nround-2 read of the FE-1374 spec renderings had found four by accident (each of which fed a\nreal spec change — the practice predates its name).\n\n## The register dial\n\nThe register is a dial, not a single target. One practice, several grades — pick the cheapest\ngrade that can still fail:\n\n- **Plain prose** (Google/GOV.UK style): the default. Catches undefined terms, uncited rules,\n compressed allusions.\n- **STE grade** (controlled vocabulary, one instruction per sentence): for sources whose claims\n are dense or load-bearing enough that plain prose can still paper over them. Costs more;\n earns it when the source will govern implementation.\n- **Worked examples** (FE-1397's form): re-render a _definition_ into concrete instances and\n check what breaks. The strongest grade for type systems and contracts — a definition that\n survives three worked designs at different thicknesses has been tested, not admired.\n\n## Filings are render-and-read material too\n\nA sweep's own capture — its tickets, its accrual comments, its penciled directions — is itself\na rendering of the session's understanding, and gets the same treatment: expect a challenge\npass over the filings before the arc closes. The FE-1405/FE-1406 round came from re-reading the\nfirst round's own text (\"shapes-to-fill\" quoted back); the gaps were real and had been deepened\nby the filings meant to close them.\n\n## Point findings may reveal a recurrent class\n\nA point finding is evidence of a possible fault class, not proof that the whole codebase shares\nit. Promote the finding to an audit only when recurrence is plausible, the class is cheaply\nsearchable, and missed instances could fail silently. Search both the mechanical family and the\nownership boundaries where the same contract may be reimplemented. Keep finding and fixing as\nseparate steps so a human can confirm the class and scope before repair begins.\n\nWhen the class is confirmed, make its categorical part executable as a boundary or regression\ncheck. Put any judgment that cannot be mechanized into review guidance. Do not widen a one-off\nfinding into a repository sweep merely for symmetry.\n\n## Consolidation: capture-as-we-go, reconcile-before-landing\n\nCapture channels (accrual comments, pencil lists, strain appendices, handoffs) guard against\nevaporation, not fragmentation. Two rules keep the yield coherent:\n\n- **Every capture channel names its consolidation target** — accruals reconcile into the\n owning control surface, pencils graduate to issues or planning documents, and strain reports\n become document fixes. A channel with no named target is a leak with a delay.\n- **An arc is not closed until consolidation runs.** The closing step reconciles what the\n captures established into the durable artifacts (coordination, ledger, docs, issues) — a\n handoff note alone is a deferral, not a deposit.\n\n## Deposit: work describes itself at authoring time\n\nProse backfill is remediation, not workflow. A branch's commit message and PR body carry its\nsemantics when it lands — the record must not abstain exactly where description is most needed\n(FE-1390 landed 1,392 lines with an empty body; the deep-read that repaired it cost more than\nwriting it at authoring time would have). The same rule for tooling: a skill output written\ninto `docs/` passes through the documentation protocol — an `INDEX.md` row or an `AGENTS.md`\npointer — like any other document.\n\nReflections belong in work products, marked as `> **Reflection:**` blockquotes, distinct from\nthe captured facts — insight left only in chat evaporates with the context that produced it.\n", - "exitCode": 0 -} -``` - -## Assistant - -Agreed on all three adjustments. - -The two cuts should become **conditional bets**, not commitments: - -- Default to review-and-revise until Frontier 0 confirms whether cold-start is a demo obligation. -- Build a **minimum coherent CPS semantic closure**, not merely a fixture-shaped subset. It must support the demo, provenance, correction, completion, and optimisation handoff while explicitly inventorying omissions. - -A compact frontier map could look like: - -```text -FE-1476 September outcome -├─ Frontier 0 — make the claim decidable -│ ├─ FE-1480 projection-authority decision -│ ├─ FE-1478 provenance acceptance criteria -│ ├─ FE-1482 CPS semantic scope -│ ├─ FE-1402 completion criteria -│ ├─ FE-1331 cold-start conflict -│ └─ Missing mappings -│ ├─ confirmed demo claim/use case -│ ├─ valid source/capture fixture -│ ├─ optimisation handoff contract -│ └─ witnessed worked-example review -│ -├─ Frontier 1 — CPS semantic loop -│ ├─ FE-1482 CPS plugin -│ ├─ FE-1480 SDCPN projection -│ ├─ FE-1478 provenance -│ ├─ FE-1481 structured legibility surface -│ ├─ FE-1402 completion input -│ ├─ FE-1431 plugin-contract input -│ └─ Missing mappings -│ ├─ active-model/issues read path -│ └─ CPS semantic-coverage oracle -│ -├─ Frontier 2 — reviewer control loop -│ ├─ FE-1479 targeted re-elicitation -│ ├─ FE-1438 client-tool return/application -│ ├─ FE-1439 existing-target session identity -│ ├─ FE-1394 supersession/conflict behavior -│ ├─ FE-1403 / FE-1406 guidance and strategies -│ └─ Missing mappings -│ ├─ controller implementation -│ ├─ review-and-revise runbook -│ └─ selected-region identity -│ -└─ Frontier 3 — deployed witnessed proof - ├─ FE-1477 / FE-1440 routing and website integration - ├─ FE-1423 exposure gates - ├─ FE-1441 deployment - ├─ FE-1476 rehearsal and acceptance - └─ Missing mappings - ├─ optimisation-flow integration - └─ witnessed demo/legibility deposit -``` - -Each frontier’s proof should become a three-part bundle: - -1. **Executable evidence** — the behavior succeeds and can fail honestly. -2. **Legible rendering** — translate it into another register and record the strain. -3. **Human witness and deposit** — a named person reviews it; findings reconcile into the governing artifact. - -For example, Frontier 1 is not proved merely because projection tests pass. A human must be able to follow: - -```text -utterance → capture → model field → SDCPN element -``` - -…and understand the before/after correction without reading implementation code. - -For Frontier 0, I suggest four decisions: - -1. **What claim must the demo support?** - - Review/revise only - - Bootstrap one bounded region from cold start - - Elicit an entire model from cold start - -2. **What semantic closure is necessary?** - - Every demonstrated net construct has a model/capture source. - - Every runbook demand has a representable slot or issue. - - Optimisation inputs are represented. - - Omitted CPS concepts and their consequences are legible. - -3. **Where does inference belong?** - Work one concrete capture → model → SDCPN transformation and allocate every judgment to capture, fold, projection, or application. - -4. **Who witnesses it?** - Ideally you for architectural legibility, Dora for the product claim, and Chris/Yannis for the optimisation handoff. - -My first question is therefore: **what is the strongest cold-start claim you think stakeholders may expect—one bounded region, a recognisable partial model, or the complete process?** - -On the planning-doc aside: agreed. We need scoped authority rather than a single total order: - -```text -How we work AGENTS + protocols -Settled truth CONTEXT + ADRs + active specs -What matters now STEERING -What runs next COORDINATION + Linear -Why we believe it planning/reference records -``` - -Several records currently blur those roles. After Frontier 0, I’d run a separate inventory that classifies each document as current authority, supporting evidence, historical record, superseded/archive, or deletable—with link and Linear-reference repair before moving anything. - -## User - -Good. That kind of projection is much more legible and usable for me. Long-lived active documents like STEERING.md should also be designed with only as much prose as they need, in such a way as to aid rapid comprehension and reduce VCS churn and conflicts. legible artifacts can be of various forms, and would in at least some cases follow patterns such as dictated for prototyping in `ds-prototype`; but should not be throwaway, rather collected as documentation snapshots of decision/design, and even as regression oracles. - -To your question: I think it's safe to assume that time constraints for a live demo preclude eliciting a complete model *during the live demo*; but that doesn't mean that attendees will not ask to try it out, and it's also possible that external factors or stakeholder decisions will change the focus/substance of the demo with short notice, so I think an implementation that is reasonably capable of a full model elicitation, which works better than a "baseline" (see our earlier comparisons, where we had claude code elicit based on a bare flat prompt), has to be considered important, but it takes time and the judgement of the quality of the output takes time. It's the more bounded scenarios where we want it to be clear (legible) that the elicitation process results in consequent change - -## Assistant - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-prototype", - "arguments": "Use its artifact and proof conventions as a reference while shaping persistent legibility artifacts for Brunch Frontier 0; do not build a prototype yet." -} -``` - -## User - -**Tool Result:** `TU-034CfzGWw83L0SzcJU5WFJ` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-prototype\">\n# ds-prototype Skill\n\n\n<!-- Vendored from mattpocock/skills@84fdeff: skills/engineering/prototype -->\n\n# Prototype\n\nA prototype is **throwaway code that answers a question**. The question decides the shape.\n\n## Pick a branch\n\nIdentify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:\n\n- **\"Does this logic / state model feel right?\"** → [LOGIC.md](LOGIC.md). Build a single shareable HTML file — free-play buttons plus tabbed guided walkthroughs — that pushes the state machine through cases that are hard to reason about on paper, and that a non-developer can drive.\n- **\"What should this look like?\"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.\n\nThe two branches produce very different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.\n\n## Rules that apply to both\n\nUse /ds-subtraction after you understand the question — the first sufficient prototype, not a smaller product.\n\n1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure.\n2. **Trivial to run.** A UI prototype starts from one command in the project's task runner — `pnpm <name>`, `python <path>`, `bun <path>`, etc. A logic demo is a single HTML file the user double-clicks. Either way, no thinking required to start it.\n3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is _checking_, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear \"PROTOTYPE — wipe me\" name.\n4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast.\n5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.\n6. **Capture it when done.** Fold any validated decision into the real code, then capture the prototype itself as a **primary source**: commit it to a throwaway branch, out of main, and leave a context pointer to that branch on the implementation issue. Capture the answer too — the verdict and the question it settled — in the issue or a commit. The main branch keeps only the validated decision.\n\n\nARGUMENTS: Use its artifact and proof conventions as a reference while shaping persistent legibility artifacts for Brunch Frontier 0; do not build a prototype yet.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-prototype\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-prototype/LOGIC.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-prototype/UI.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-prototype/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "cat /Users/lunelson/.config/amp/plugins/dogsled/skills/ds-prototype/LOGIC.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CfzOJwx7qDvtPhxnrcK` - -``` -{ - "output": "# Logic Prototype\n\nA single, self-contained HTML file — a **shareable demo** — that lets anyone drive a state model by clicking buttons. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.\n\nBecause it's one file with nothing to install, you can hand it to a non-developer — a designer, a PM, a domain expert — and let them feel the model for themselves. So it speaks their language, not the code's.\n\n## When this is the right shape\n\n- \"I'm not sure if this state machine handles the edge case where X then Y.\"\n- \"Does this data model actually let me represent the case where...\"\n- \"I want to feel out what the API should look like before writing it.\"\n- Anything where someone wants to **press buttons and watch state change**.\n\nIf the question is \"what should this look like\" — wrong branch. Use [UI.md](UI.md).\n\n## Process\n\n### 1. State the question\n\nBefore writing code, write down what state model and what question you're prototyping. One paragraph, at the top of the demo (in a visible intro, not just a comment). A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.\n\n### 2. Isolate the logic in a portable module\n\nPut the actual logic — the bit that's answering the question — in a single `<script>` block written as a small, pure module that could be lifted out and dropped into the real codebase later. The page around it is throwaway; this module isn't.\n\nThe right shape depends on the question:\n\n- **A pure reducer** — `(state, action) => state`. Good when actions are discrete events and state is a single value.\n- **A state machine** — explicit states and transitions. Good when \"which actions are even legal right now\" is part of the question.\n- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations.\n- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.\n\nPick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a page. Keep it pure: no DOM, no `document`, no button handlers reaching inside it. The page calls into it; nothing flows the other direction. This is what makes the prototype useful past its own lifetime: once the question's answered, the validated reducer / machine / function set lifts into the real module on its own.\n\n### 3. Build the shareable HTML file\n\nOne file, plain HTML/CSS/JS — no framework, no bundler, no server, everything inline so it opens by double-click and survives being emailed around. Anyone should be able to run it by opening it.\n\nWrite it for a non-developer. Every label is in **domain language**, not code — buttons and state read like the business, not the reducer. Explain in plain words what's happening.\n\nLay it out with a clean hierarchy, top to bottom:\n\n1. **Title and one-line explanation** of what this demo lets you explore (the question from step 1).\n2. **Current state** — the full relevant state, rendered as a readable panel (labelled fields, not a raw JSON dump), re-rendered after every click so the change is visible. Where it helps a non-developer follow, call out what just changed.\n3. **Free-play buttons** — one button per action, always available, so anyone can poke at the model in any order. Each click dispatches its action and re-renders the state.\n4. **Guided walkthroughs** — a set of **scenarios**, one per tab. Each tab holds a short plain-language description of the scenario — the situation it sets up and what to watch for — and underneath it, the ordered **buttons to press** for that scenario. Each step is a real button: clicking it performs that action and moves to the next step. Starting a walkthrough resets to a known initial state so the scenario runs the same way every time.\n\nChoose scenarios that demonstrate the awkward cases — the happy path, a tricky edge case, an attempt at something that should be illegal — the ones hard to reason about on paper.\n\nKeep it beautiful but restrained: clean typography, generous spacing, one accent colour. No animations, no gimmicks — nothing that competes with the state and the buttons.\n\n### 4. Hand it over\n\nSend them the file, or open it for them. They'll click through the walkthroughs and free-play whenever they get to it; the interesting moments are when they say \"wait, that shouldn't be possible\" or \"huh, I assumed X would be different\" — those are the bugs in the _idea_, which is the whole point. If they want new actions or a new scenario, add them. Prototypes evolve.\n\n### 5. Capture the answer and the prototype\n\nOnce the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes. The logic-specific mapping: the validated reducer / machine / function set lifts into the real module (the decision, absorbed); the HTML shell rides along to the throwaway branch that keeps the prototype as a primary source — and being one self-contained file, it stays trivially re-runnable there.\n\n## Anti-patterns\n\n- **Don't add tests.** A prototype that needs tests is no longer a prototype.\n- **Don't wire it to the real database.** Use in-memory state unless the question is specifically about persistence.\n- **Don't generalise.** No \"what if we wanted to support X later.\" The prototype answers one question.\n- **Don't blur the logic and the page together.** If the pure module references the DOM, `document`, or button handlers, it's no longer liftable. Keep the page as a thin shell over a pure module.\n- **Don't reach for a framework, bundler, or server.** One file the recipient double-clicks; a React app or a dev server defeats \"shareable\".\n- **Don't ship the HTML shell into production.** The page is optimised for being clicked through by hand. The logic module behind it is the bit worth keeping.\n", - "exitCode": 0 -} -``` - -## Assistant - -That changes the cut materially: cold-start should not be the live-demo spine, but it also cannot be relegated to “after the proof.” I’m treating it as a contingency capability with its own slower quality oracle, sharing the same semantic and controller machinery as the bounded demo path. - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg --files docs/planning/process-model-elicitation/baseline | sort; printf '%s\\n' '--- baseline summaries'; rg -n '^#|baseline|Claude|prompt|score|verdict|finding|comparison|coverage|quality' docs/planning/process-model-elicitation/baseline --glob '*.md' | head -240", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"Production Scheduling|cold.chain|use case|scenario|situation|baseline\" docs/planning/process-model-elicitation docs/reference --glob '*.md' | head -240", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cg01qJ5oV2sSHOLsV1Z` - -``` -{ - "output": "docs/planning/process-model-elicitation/baseline/opening-message.md\ndocs/planning/process-model-elicitation/baseline/protocol.md\ndocs/planning/process-model-elicitation/baseline/readout.md\ndocs/planning/process-model-elicitation/baseline/run.ts\ndocs/planning/process-model-elicitation/baseline/situation-pack.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1-model.txt\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.raw.json\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2-model.txt\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.raw.json\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md\n--- baseline summaries\ndocs/planning/process-model-elicitation/baseline/protocol.md:1:# Baseline control — experiment protocol (FE-1361)\ndocs/planning/process-model-elicitation/baseline/protocol.md:3:What does one-shot / lightly-prompted AI elicitation already achieve? Two conditions of the\ndocs/planning/process-model-elicitation/baseline/protocol.md:7:## Conditions\ndocs/planning/process-model-elicitation/baseline/protocol.md:9:| # | Interviewer | System prompt | Approximates |\ndocs/planning/process-model-elicitation/baseline/protocol.md:11:| 1 | `claude-opus-5` | none | the incumbent: a strong model told to interview-then-build (the Petrinaut assistant's prompt already mandates interview-first, per the FE-1358 survey) |\ndocs/planning/process-model-elicitation/baseline/protocol.md:12:| 2 | `claude-opus-5` | [v0-prompt.md](v0-prompt.md) | the degenerate plugin: the seven-category elicitation surface as pure guidance, no machinery |\ndocs/planning/process-model-elicitation/baseline/protocol.md:15:([opening-message.md](opening-message.md)); the v0 system prompt is the only difference, so\ndocs/planning/process-model-elicitation/baseline/protocol.md:18:## Subject and interviewee\ndocs/planning/process-model-elicitation/baseline/protocol.md:21:reference model — FE-1363 retained it as the flat-baseline testbed). The interviewee is a\ndocs/planning/process-model-elicitation/baseline/protocol.md:30:## Mechanics ([run.ts](run.ts))\ndocs/planning/process-model-elicitation/baseline/protocol.md:33: situation pack; the expert never sees the v0 prompt.\ndocs/planning/process-model-elicitation/baseline/protocol.md:41: at 24. Delivering only at the forced wrap is itself a stopping-discipline finding.\ndocs/planning/process-model-elicitation/baseline/protocol.md:42:- The interviewer keeps the model's default adaptive thinking (part of \"vanilla Claude\"); the\ndocs/planning/process-model-elicitation/baseline/protocol.md:51:`turbo run baseline:run --filter '@hashintel/brunch-agent' -- 1` /\ndocs/planning/process-model-elicitation/baseline/protocol.md:52:`turbo run baseline:run --filter '@hashintel/brunch-agent' -- 2` (needs `ANTHROPIC_API_KEY`).\ndocs/planning/process-model-elicitation/baseline/protocol.md:55:## Instruments (scored in the read-out)\ndocs/planning/process-model-elicitation/baseline/protocol.md:60: scored per LLMREI practice: Question Formulation, Question Omission, Order of Interview,\ndocs/planning/process-model-elicitation/baseline/protocol.md:63:2. **Seven-category surface coverage**: per category — asked? probed past the first answer?\ndocs/planning/process-model-elicitation/baseline/protocol.md:75:## Threats to validity (acknowledged)\ndocs/planning/process-model-elicitation/baseline/protocol.md:80: would inflate coverage in both conditions equally, but absolute coverage numbers should not\ndocs/planning/process-model-elicitation/baseline/protocol.md:82:- The v0 prompt was written by the same team that will score the transcripts. The mistake\ndocs/planning/process-model-elicitation/baseline/protocol.md:85: (different provider/model, no tools). The FE-1358 survey's prompt excerpt is the bridge; the\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:1:# v0 elicitation prompt (condition 2 system prompt)\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:15:## The elicitation surface\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:33: entity (age, quality, setup state). Ask what distinctions matter — two items are \"the same\"\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:45: compatibilities, regulatory and quality rules. Then ask separately for the unwritten ones:\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:51:## How to interview\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:76:## The deliverable\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:1:# Situation pack — Vestera Coatings (baseline control, FE-1361)\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:3:**Private to the simulated interviewee.** This file is the system prompt for the agent playing\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:4:the user in the baseline-control interviews. It is authored from the operational prose of the\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:9:## Role instructions\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:19: a real person naturally would. Never enumerate your knowledge unprompted.\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:44:## Who you are\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:53:## What you want (surfaces only if asked about goals / what the model should answer)\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:65:## The plant, as you'd describe it\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:85: Worse after the big washdowns. _(doesn't know)_ exact scrap per changeover type; quality\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:109:## The demand side\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:131:## Things you plainly don't know (say so if asked)\ndocs/planning/process-model-elicitation/baseline/opening-message.md:1:# Opening message (shared by both conditions)\ndocs/planning/process-model-elicitation/baseline/opening-message.md:4:difference between condition 1 and condition 2 is the presence of the v0 system prompt.\ndocs/planning/process-model-elicitation/baseline/readout.md:1:# Baseline control — read-out (FE-1361)\ndocs/planning/process-model-elicitation/baseline/readout.md:8:## Headline findings\ndocs/planning/process-model-elicitation/baseline/readout.md:10:**1. The baseline is far stronger than the positioning assumed.** Bare Claude — no system\ndocs/planning/process-model-elicitation/baseline/readout.md:11:prompt at all — opened objectives-first, walked the process end to end, probed retractions and\ndocs/planning/process-model-elicitation/baseline/readout.md:14:using the model politically before validation, and delivered its model with an unprompted\ndocs/planning/process-model-elicitation/baseline/readout.md:18:Bano instrument scores as better than the human novices the taxonomy was built from. Any\ndocs/planning/process-model-elicitation/baseline/readout.md:20:the frontier model already works upstream unprompted. The differentiation argument must rest\ndocs/planning/process-model-elicitation/baseline/readout.md:34:data-pull specs and scenario probes. ReqElicitGym's finding (\"models overwhelmingly lack\ndocs/planning/process-model-elicitation/baseline/readout.md:38:**3. The v0 prompt buys real, specific improvements** — see the 1→2 delta below — but not the\ndocs/planning/process-model-elicitation/baseline/readout.md:40:materialize, because bare Claude already keeps a register. What guidance actually bought:\ndocs/planning/process-model-elicitation/baseline/readout.md:45:silent hardening of vague statements into \"confirmed\" constants, coverage blind spots\ndocs/planning/process-model-elicitation/baseline/readout.md:48:is a thing a prompt cannot fix and the harness/plugin design claims to. That is the\ndocs/planning/process-model-elicitation/baseline/readout.md:51:## Bano questionnaire scores\ndocs/planning/process-model-elicitation/baseline/readout.md:55:scoring notes; the table gives the scores.\ndocs/planning/process-model-elicitation/baseline/readout.md:88:Both conditions score dramatically better than Bano's student cohorts (where e.g. 19/28\ndocs/planning/process-model-elicitation/baseline/readout.md:89:groups failed to summarize and 16/28 built no rapport) and in line with LLMREI's finding that\ndocs/planning/process-model-elicitation/baseline/readout.md:97:relevant questions\" is the one item where condition 2 scored _worse_ (3 vs 2): it never asked\ndocs/planning/process-model-elicitation/baseline/readout.md:98:about ramp scrap, maintenance, margins, or minimum run sizes — see the coverage blind-spot\ndocs/planning/process-model-elicitation/baseline/readout.md:99:finding.\ndocs/planning/process-model-elicitation/baseline/readout.md:101:## Seven-category surface coverage\ndocs/planning/process-model-elicitation/baseline/readout.md:105:| Objectives & questions-to-answer | yes / yes / yes — but penalty weights never pursued numerically; design sidesteps via KPI-vector comparison | yes / yes / yes — weights co-constructed from betting questions, fitted ratio flagged as fitted |\ndocs/planning/process-model-elicitation/baseline/readout.md:113:## Excavation against the situation pack's tiers\ndocs/planning/process-model-elicitation/baseline/readout.md:136:coverage is materially luck — which is itself the argument for harness-computed completion\ndocs/planning/process-model-elicitation/baseline/readout.md:139:## Silent-assumption audit\ndocs/planning/process-model-elicitation/baseline/readout.md:141:Both conditions produced explicit assumption registers — bare Claude unprompted (15\ndocs/planning/process-model-elicitation/baseline/readout.md:161:harness-computed coverage are for.\ndocs/planning/process-model-elicitation/baseline/readout.md:163:## Output artifacts\ndocs/planning/process-model-elicitation/baseline/readout.md:187:## The 1→2 delta — what pack content alone buys\ndocs/planning/process-model-elicitation/baseline/readout.md:204:6. **Ledger quality** (not existence): per-entry source attribution, load-bearing flags,\ndocs/planning/process-model-elicitation/baseline/readout.md:207:What the delta was expected to include but didn't: register existence (bare Claude already\ndocs/planning/process-model-elicitation/baseline/readout.md:209:refusing invention (both refuse — neither offered the incumbent prompt's \"make it up\" escape\ndocs/planning/process-model-elicitation/baseline/readout.md:212:## Residual gaps in condition 2 — the evidence-derived plugin requirements\ndocs/planning/process-model-elicitation/baseline/readout.md:231:5. **Computed coverage.** The ramp-scrap hole was invisible to C2's own gap accounting —\ndocs/planning/process-model-elicitation/baseline/readout.md:241:## Notes on the instrument itself (for reruns and condition 3)\ndocs/planning/process-model-elicitation/baseline/readout.md:254: scores in particular are one judge's reading. Fine for design evidence; don't quote them\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1:# Baseline control — condition 1 (bare)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:4:- Interviewer: claude-opus-5 (no system prompt)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:35:## 1. What are you trying to learn from it?\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:41:## 2. The process route\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:48:## 3. Equipment\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:55:## 4. Batches and time\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:62:## 5. Changeovers — probably the crux\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:69:## 6. QC and rework\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:75:## 7. Demand and disruption\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:82:## 8. Scoring the schedule\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:86:3. How do you actually sequence today — a spreadsheet, a rule of thumb, gut feel? I'd like the model to be able to reproduce your current approach as a baseline, so you can compare alternatives against it.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:98:## 1. What I want to know\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:110:## 2 & 3. Process and equipment — quick corrections\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:118:## 5. Changeovers — since you want detail here\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:124:## 6. Quality — correction to your model\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:126:No mid-process sample-adjust-retest loop. What happens is: batch finishes fill & pack, then sits in QA hold — about four hours typically, specialty can be a day. Lab's two people, backs up end of week. I don't have first-pass-yield numbers off the top of my head; I don't think we track it that granularly, quality just reports scrap as a monthly percentage across the board.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:130:## 8. Scoring and how I do it today\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:136:Today I sequence off a spreadsheet — I map the week's demand book onto the three lines, rough rate per product per line, add changeover by feel, and I re-juggle it out loud at the 07:30 huddle most mornings. That sheet is the baseline you should try to reproduce first, honestly — if your model can't match what the sheet does, I won't trust what it says about doing better.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:150:## Flag 1: your Q1 isn't a simulation question, quite\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:158:## Flag 2: I can't settle the Line 1 tank argument, and neither can the model\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:169:## Flag 3: the numbers you don't have\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:171:You flagged three: first-pass yield, ramp scrap per changeover type, and quality only reporting monthly scrap in aggregate.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:181:## What I actually need\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:201:**One validation question:** should the baseline match _the sheet's plan_, or _what actually happened_? They differ, and the gap is usually your improvising at the huddle. I'd rather match actuals — that's what makes it credible to your boss — but I'd use the sheet for the sequencing logic.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:253:## Three things your answers changed\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:263:## The model\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:267:### Colour sets\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:278:### Places\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:284:### Transitions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:300:### Changeover matrix (minutes, techs in brackets)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:314:### Policy layer — the part you'll actually tune\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:316:The baseline reproduces you:\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:327:### KPIs\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:333:## Assumptions register — all UNVALIDATED\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:351:## What I still need\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:367:## PNML\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:371:Send the shift pattern and I'll start wiring the baseline against your ERP export. First thing I'll report is whether the model can reproduce your actual weekly output per line — if it can't, that's the finding, and we fix the model before it tells you anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:387:Go ahead and wire the baseline against the ERP export once you've got it, and I'll chase the shift confirmation on Line 3 overtime frequency if you need it — right now \"rare\" is genuinely the best I've got, I don't have a count.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:397:## The headline question, which I should have asked two turns ago\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:407:## The tech-hour budget\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:430:## The 11:00 problem, and what it does to Meridian\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:446:## Your Q2 changes shape\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:456:## KPI definition, tightened before your boss sees it\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:470:## Model updates\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:476:Line 3 overtime: don't go counting it. Treat as zero in baseline and use it as a **lever** — \"what is it worth to approve Line 3 overtime twice a week?\" is a better question than \"how often does it happen,\" and it takes work off you.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:482:## Standing asks\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:490:I'll wire the baseline the day the ERP export lands. First report is whether the model reproduces your actual weekly output per line, before it says anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:520:## What the day actually looks like now\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:535:**A new rule falls out of that:** don't commit a morning family switch unless there's enough work in that family to fill through 22:00. Switch Line 2 to white at 09:00 with four hours of white orders behind it and you get four good hours and then a dead line until morning — you've spent a 3-hour wash _and_ stranded the evening. Call it the fill-the-shift rule; I'll add it as a policy variant and test it against the baseline.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:539:## Capacity or timing? — the question your ops director's answer hangs on\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:545:That's a hypothesis, not a finding — it could equally be that the mix is big-wash-heavy and eight _is_ the ceiling in practice. But it's exactly what the tick sheet will settle, and it's the difference between \"we need another head\" and \"we need the existing heads at different hours.\" Worth knowing which before the conversation, not during.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:551:## One fix to the measurement design\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:564:## Model updates\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:569:- **Policy variants to race:** baseline (you) / fill-the-shift rule / time-of-day wait-rule / tech shift 06:00–14:00 vs 10:00–18:00 vs split / Line 3 overtime as a purchasable lever.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:575:## Where this stands\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:581:**From me, on ERP arrival:** baseline wired and the reproduction check — actual weekly output per line, and roughly eight family switches a week. If it can't hit those, that's the finding and I fix the model before it says anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:621:**When the export lands** I'll wire the baseline and run the reproduction check first — weekly output per line, and the switch count. If it misses, that's the interesting result and I'll come back with what the model thinks is happening versus what your ERP says happened. That's normal, not a setback; the gap is usually where an assumption is wrong, and we have eleven of those on the register waiting to be caught.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:643:One thing not worth waiting a fortnight for: if the tick sheet shows an operator doing a family switch in week one, send it straight over. That single row would retire a hard guard in the model and change what the baseline predicts, so I'd rather rework early than build two weeks on it.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:836:# Coatings Plant Scheduling Model — v1.0\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:844:# PAGE ONE — ASSUMPTIONS REGISTER\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:846:## A. Confirmed by you in interview\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:866:## B. UNVALIDATED — my assumptions, not your statements\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:887:**A19 is the one to watch.** A single contrary row retires a hard guard and changes what the baseline predicts. Send it the same day it appears.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:889:## C. What this model cannot do\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:897:# 1. Scope and resolution\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:911:# 2. Colour sets\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:926:# 3. Places\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:928:### 3.1 Shared\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:945:### 3.2 Per line L ∈ {1,2,3}\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:962:# 4. Transitions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:964:## 4.1 Calendar subnet (drives everything)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:976:## 4.2 Production\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:987:## 4.3 Changeover — the detailed part\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:997:### Changeover matrix\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1010:### Latest feasible start (derived from C4 + C8)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1021:## 4.4 Failure and QA\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1033:# 5. Policy layer\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1037:## 5.1 Baseline — reproduces you\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1050:## 5.2 Variants to race against it\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1066:# 6. KPIs\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1068:## 6.1 Service (primary)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1077:## 6.2 Changeover — reported as three separate numbers\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1093:## 6.3 Secondary\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1099:# 7. How to use it\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1101:## 7.1 Run this first — the reproduction check\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1112:**If it misses, that is the finding.** The gap points at a register row and we fix the model before it says anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1114:## 7.2 Then, in order\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1121:## 7.3 The Line 1 tank inversion — after the stopwatch, not before\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1132:# 8. Parameter file\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1137:# === lines.csv ===\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1143:# === skus.csv ===\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1150:# === rates.csv === units/hr, PLACEHOLDER = A14\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1153:# === changeover.csv ===\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1166:# === ramp_scrap.csv === A5 — swept, not asserted\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1173:# === resources.csv ===\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1178:# === tiers.csv ===\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1187:# 9. PNML\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1291: <lever>count = 2 (baseline) | 3 (third-tech arm)</lever>\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1607:# 10. Building Lines 2 and 3\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1623:# 11. What happens next\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1635:# 12. Two lines that don't depend on any of this\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1:# Baseline control — condition 2 (v0 prompt)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:4:- Interviewer: claude-opus-5 + v0-prompt.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:64:2. **Meridian.** Roughly what share of your volume and of your order count is Meridian? Is _every_ Meridian order phone-call-worthy, or is it specific products/sites? And when you say \"risk Meridian\" — is the risk lateness, or is it something else (quality, a specific short lead time, an audit)?\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:80:2. **Meridian.** They're maybe a third of our volume by units — almost all base whites, a couple of tint SKUs too. Order count is smaller than that, they order in bigger batches than most. Not every single Meridian order gets a call — it's specifically anything that's going to miss the ship window, doesn't matter which SKU. The risk is purely lateness — they've got a vendor scorecard and a fine schedule, and if you're late enough often enough they start pulling shelf space. It's not a quality thing for them, it's a contract thing.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:116:**11. Coastal instead.** Yeah, this changes things. Coastal's not on a scorecard, they're not going to fine us. If it's their whole week's order I'd still care — a full week's requirement matters even for a small account, because if I blow it I've got nowhere to slot them next week either. But I'd take the tint job even at six hours late for Coastal, honestly, as long as six hours doesn't turn into \"we missed by two days.\" Somewhere around a day late is where I'd start to feel bad about it. So there really are two different curves in my head — Meridian's is a cliff, everyone else's is a slope.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:124:That's not blunter than an exchange rate — it's _more_ informative. A cliff and a slope is a perfectly implementable penalty structure, and it's a real finding: it means your policy is lexicographic (protect Meridian's window absolutely, then optimise everything else), not a weighted sum. I'll build it that way.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:157:**Ships.** Once released, warehouse pulls it for the Thursday truck. If QA release comes in Wednesday evening, it comfortably makes Thursday. If it had slipped — say the filler jam had been two hours instead of twenty minutes, or QA had a backlog — Thursday morning becomes tight, and that's the point where I'd be making calls, maybe asking QA to prioritize the pallet, maybe finding out if there's a later truck.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:171:3. **The three lines.** What can each run, and what can't it? You said Meridian white \"always goes Line 2, no debate\" — is that a hard qualification (Line 1 physically can't), a quality rule, or just habit? And what makes Line 3 different besides day-shift-only?\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:186:- **Line 3** is newest and quick, but it's product-by-product qualified — there's a list, and I know two tint SKUs specifically aren't on it yet, someone in quality is slowly working through validating it for more. Day-shift-only is a separate constraint on top — that one's a staffing/overtime decision, not a capability one.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:323:- One row per order line: order ID, account, SKU, family if it's a field, quantity, date the order entered the book, promise date (the one on-time is scored against), actual ship date, and line it ran on if that's captured.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:414:1. **Lateness scored in hours against promise date**, not days. _Assumed_ — your board is binary, but a binary metric can't distinguish your own cliff/slope answer. _Check:_ ship-date vs promise-date fields in the ERP pull.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:437:**D.** Behind by about a shift is roughly the trigger — if I can see Wednesday that a Meridian order won't make it without extra hours, I ask then. Asking Thursday or later usually doesn't help, not enough runway to get approval and staff it. I've had a case where I didn't ask even though I was tempted — a slip that would've needed Saturday overtime, but it was Coastal, not Meridian, and I judged it wasn't worth the argument with the ops director for a non-scorecard account. That one shipped a day late and nobody outside the plant noticed.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:459:**And one finding from D worth testing, which I don't think you said out loud:** you get Saturday overtime approved on Line 1 or 2 — but the lab is day-shift, weekdays. So Saturday production may just queue for Monday's lab and arrive no earlier than if it hadn't run. Unless the Saturday ask _includes_ a lab person, or the Friday-evening trap catches it anyway. Worth asking the ops director whether lab overtime has ever been part of that approval — if it hasn't, some of those four or five Saturdays this year may have bought you nothing.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:637:# A. THE MODEL\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:641:## A.0 Global switches (set these per experiment)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:653:## A.1 Colour sets\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:693:## A.2 Places\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:738:## A.3 Transitions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:740:### Arrivals and release\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:757:### Production stages (per line, per batch)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:797:### Changeover\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:825:### QA\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:837:### Shipping and scoring\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:846:### Breakdowns\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:855:## A.4 Calendars (gate sub-nets)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:866:## A.5 The policy layer — conflict resolution\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:895:## A.6 Scoring\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:917:## A.7 Experiments mapped to your three questions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:930:# B. ASSUMPTION LEDGER\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:934:### From the interview, provisionally agreed\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:936:1. **Lateness scored in hours vs promise date.** Your board is binary but binary can't express your own cliff/slope. _Check:_ ship-date vs promise-date in the ERP pull.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:953:# B. ASSUMPTION LEDGER\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:957:### Objectives and scoring\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:959:1. **Lateness scored in hours against promise date**, not days. Your board is binary, but a binary metric cannot express the cliff/slope you described. _Check:_ promise-date vs actual-ship-date fields in the ERP pull.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:967:### Structure and taxonomy\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:973:5. **Line 3's unqualified list = 2 tint SKUs.** Your number, but I picked _which_ two. _Check:_ the quality validation list.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:977:### Durations\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:989:### Policies\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:998:### Boundary conditions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1011:# C. WHAT THE MODEL DELIBERATELY LEAVES OUT\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1017:2. **The unwritten rules.** We never covered week-one mistakes, always/nevers, or rules that exist because of one bad incident. In my experience this category produces two or three constraints that invalidate otherwise-attractive schedules — the model will likely propose something that makes you say \"we'd never do that,\" and each time it does, that's an unwritten rule surfacing. **Treat those moments as findings, not bugs**, and send them to me.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1032:**If you do three things before the first real run, do these:** load the ERP order book (kills assumptions 31–33, the ones that can make the model lie comfortably); answer the batch-contiguity question in one sentence (unblocks your run-size question); and run `RELEASE_POLICY = STANDING_MERIDIAN` against the default (tests whether your headline dilemma is manufactured upstream of you, which is the cheapest finding available and doesn't need the data at all).\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1046:# COATINGS PLANT SCHEDULING MODEL — FINAL DELIVERABLE\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1052:# A. THE MODEL\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1056:## A.0 Experiment switches\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1068:## A.1 Colour sets\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1084:## A.2 Places\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1108:## A.3 Transitions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1110:### Arrival and release\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1127:### Production (per line, per batch)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1157:### Changeover\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1179:### QA\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1189:### Ship, score, break\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1201:## A.4 Calendars\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1212:## A.5 Policy layer (every rule swappable)\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1232:## A.6 Scoring — unit is one line-hour\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1252:## A.7 Experiments → your three questions\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1265:# B. ASSUMPTION LEDGER\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1279:**Structure** 8. Four stages, identical topology on all three lines (you walked me through L2 only). 9. **Tank caps: L1 mill→fill = 1 batch, others = 2.** _Check: nameplate ÷ batch size._ 10. Vessels 1,100 / 1,650 / 1,350 — **L3 is the one you said not to quote.** _Check: 30 seconds at the vessel._ 11. 14 SKUs as 4 white / 8 tint / 2 specialty, tint depths assigned arbitrarily. _Check: SKU master — also tells me which tints are dark._ 12. L3's two unqualified tint SKUs — your count, my choice of which. _Check: quality's validation list._ 13. **Specialty on L1 only. I never asked whether L3 can run specialty — if it can, the mill-motor answer changes materially.** 14. Meridian ⇒ L2 is absolute, no emergency override. _Check: has it ever been broken?_\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1289:# C. WHAT THE MODEL LEAVES OUT\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1295:2. **The unwritten rules.** Never covered. Expect the model to propose schedules that make you say \"we'd never do that.\" **Treat each of those as a finding, not a bug** — that's an unwritten rule surfacing.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:1314:3. **Run `RELEASE_POLICY = STANDING_MERIDIAN` against the default** — tests whether your headline dilemma is manufactured upstream of you. It's the cheapest finding available and needs no data at all.\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cg01rBYBbLv96l18gJv` - -``` -{ - "output": "is ready pack content for FE-1403; the baseline did not test it directly (neither condition ran an incident timeline), which makes it one of the untested imports.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:21:**The ACTA knowledge audit.** Applied Cognitive Task Analysis, from Militello and Hutton — a deliberately simplified cognitive task analysis for practitioners, built as a sequence: task diagram, then knowledge audit, then simulation interview. The knowledge audit is eight probes designed to surface knowledge experts have but never volunteer: past-and-future (seeing where a situation came from and is heading), the big picture, noticing what others miss, tricks of the trade, improvising, self-monitoring, anomalies (\"can you remember a time you knew something was amiss?\"), and information difficulties (\"when did the data point one way and your judgment another?\"). Two details carry as much value as the probes. First, a universal follow-up after every answer: \"how would you know this? what cues are you relying on? how would this be hard for someone less experienced?\" — the expert–novice contrast used routinely as a cue extractor. Second, probe 8 is, for this project, a data-binding question in disguise: it surfaces where the ERP or historian is systematically wrong. Evidence grade: the catalogue is verbatim from published applications; ACTA's own validation is practitioner-grade, not experimental. Ready pack content for FE-1403; untested in the baseline.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:25:**The question typology.** A 2021 requirements-engineering paper (Zaremba and Liaskos) assembled a cross-disciplinary typology of interview question forms — around 35 types across dimensions of time, content, form, style, and probing style. The effort imported it less as a taxonomy than as a phrasebook. The individually valuable moves: the **consistency probe** (\"you said earlier that X, but then you told me Y — how do you explain that?\"), which an LLM is unusually well placed to execute because it holds the whole transcript; the **clearinghouse probe** (\"what have I not asked that is important?\"), used as a closing ritual before any completion claim; the **negative balance question** (\"you seem very efficient — do you remember occasions when problems slowed you down?\"), built to counteract the tendency to describe the idealized version of the work; and the teachback family (restating an interpretation for confirmation). The typology also names the anti-patterns — leading probes, forced choices — for the do-not-do list. All of this is pack-card material; the baseline showed the bare model already does consistency probing and teachback unprompted, so FE-1403's redundancy verdicts will likely retire some of these.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:27:**Laddering, card sorting, and triadic elicitation.** The knowledge-engineering toolkit for eliciting taxonomies — the \"what kinds of things are there, and what distinctions matter\" questions. Laddering here means the laddered grid: move down (\"can you give examples of X?\"), across (\"what alternatives to X are there?\"), and up (\"what do these have in common?\") through the domain's class structure, plus two quietly powerful probes — \"how can you tell it is X?\" (which elicits the operational recognition criteria nobody wrote down) and \"what is the key difference between X and Y?\" (which turns a flat list into a typed taxonomy). One correction the research pass made: this is _not_ the \"why is that important?\" laddering from consumer research — the two traditions share a name and nothing else. Triadic elicitation (from Kelly's repertory-grid tradition) presents three items and asks how two are alike and one differs, surfacing the attributes an expert uses without naming them. In this effort these feed the taxonomy card in FE-1403 and ground the CPS `entity-type` kind's interview strategy. Untested in the baseline.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:29:**Work-as-imagined versus work-as-done.** The framing for unwritten rules, from resilience engineering (Hollnagel): what procedures say happens differs systematically from what happens, and the gap is normal, not deviant — people trade thoroughness for efficiency as a matter of course. The practical consequence is a reframing of the question: not \"what are the undocumented rules?\" (which invites denial) but \"where does the written procedure not survive contact with the day?\" (which invites description). The same distinction arrives independently from two other imported directions: the say–do problem in requirements engineering (\"people know how to do many things that they cannot describe… don't believe the answers\"), and process mining's de jure versus de facto models (below). Three fields converging on one distinction is the strongest kind of confirmation this review found. In this effort it became the `prescribed | practiced` source-regime attribute in the IR — the design decision that there is one model with a regime tag, never two parallel models, and that a prescribed/practiced divergence is recorded as an ordinary conflict issue, because such divergences are elicitation gold. The baseline validated the underlying premise: both conditions surfaced real unwritten rules (the tint veto, the lateness hierarchy) only under deliberately-shaped probes.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:31:**Anchored hypotheticals, and hypothetical bias.** Can you ask an expert \"what would happen if…\"? The literature splits usefully. CDM endorses hypotheticals — but always anchored to a real incident already narrated, varying a case rather than inventing one. The stated-preference literature supplies the warning for the free-floating kind: when people answer about imagined situations, they systematically overstate (a meta-analysis found hypothetical answers exceeding real ones by a median factor of 1.35, with severe skew). The mechanism transfers to elicitation: an unanchored \"what would you do if\" returns the expert's _policy_ — the idealized self — rather than their practice, fluently and confidently, which makes it hard to detect. The mitigations: anchor to a narrated incident first, and ask what the expert would be _looking at_ rather than what they would decide. In this effort: the scenario-probe style the v0 prompt prescribes for conflict points (\"two lines need the crew at the same moment — what actually happens?\"), and a precondition attached to the hypothetical-escalation card in FE-1403.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:33:**Value-focused devices for objectives and weights.** Penalty weights and trade-off rates are almost never written down and cannot be asked for directly (\"what weight would you give lateness?\" produces noise). The imported toolkit is Keeney's: ask for a wish list with constraints removed, name a particularly good and particularly bad outcome and what makes each so, enumerate shortcomings of the status quo, ask what other stakeholders would want. For the weights themselves, swing weighting: have the expert compare the swing from worst to best on each attribute rather than state a number. One honest note on provenance: the baseline's standout weight excavation — condition 2's betting-framed questions that produced \"Meridian's penalty is a cliff, everyone else's is a slope\" — used a device (willingness-to-bet framing) that none of the imported sources prescribe and the v0 prompt never mentions. The model improvised it. It has a respectable ancestry in probability elicitation, but as pack content it is currently uncredited folklore; FE-1403 should either adopt it deliberately or note it as model disposition.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:37:**The quantile protocol, and the case against min/mode/max.** The flagship quantitative finding. Simulation practice commonly elicits a minimum, most-likely, and maximum for a duration and fits a triangular distribution to them. A published comparison against measured data (emergency-department length of stay) found this habit overstated the true mean by about 69 percent — while a distribution that read the same expert's middle value as a _mean_ rather than a mode landed within 1 percent. Two mechanisms: experts' stated middle values behave like means, not modes, and a triangular distribution structurally cannot represent the long right tail real service times have. The prescription: elicit quantiles instead — \"typical?\", \"one time in ten, worse than?\", \"one time in ten, better than?\" — and never fit a triangular to a volunteered three-point estimate. Evidence grade: the 69%/1% contrast is a single conference study in one domain; the quantile prescription itself is independently the settled format of the whole structured-expert-judgment field (the TU Delft studies, EFSA guidance), so the practice rests on more than the headline number. In this effort: v0 prompt category 4, the IR's quantity attribute (\"quantile-elicited, never min/mode/max\"), and the baseline's cleanest technique delta — condition 1 fell into exactly the warned-against triangular; condition 2 executed quantiles throughout. This entry also supplies the citation the IR document currently lacks for its prohibition.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:39:**The IDEA four-step question.** The most transplantable script in the structured-judgment literature, from research on reducing expert overconfidence (Speirs-Bridge et al.): ask for the lowest plausible value, then the highest, then the best guess, then \"how confident are you that your interval captures the true value — give a number between 50 and 100 percent.\" The order matters: interval before best guess, because leading with the best guess anchors the interval too narrow. The confidence step lets intervals be standardized across experts afterward. Evidence grade: solid — the format measurably widens intervals toward honesty. In this effort the v0 prompt adopted the quantile idea but _not_ the IDEA script: its \"typical, then one-in-ten\" phrasing leads with the central value and drops the calibration step. That simplification worked in the baseline, but the divergence between what the docs call \"the quantile/IDEA protocol\" and what the prompt actually says should be resolved deliberately when FE-1403 writes the card.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:41:**The clairvoyant test.** Ron Howard's discipline from decision analysis: a quantity is well-defined only if a clairvoyant — someone with perfect knowledge but exercising no judgment — could answer it. \"Cycle time\" fails until you say what's in and out of it (does it include setup?). Cheap and constantly applicable: much apparent disagreement between experts is definitional, not factual, and the test separates the two. In this effort: the definitions-first rule in the outside-the-net checklist, and the prescribed first move when two sources conflict. Untested in the baseline.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:43:**Handling expert disagreement, and Cooke's classical model.** When experts disagree, the literature's recommendation cuts against instinct: do not seek consensus, and above all do not average silently, because averaging manufactures false certainty and destroys the most valuable signal in the interview. Two formal traditions exist — behavioral aggregation (facilitate toward what a rational impartial observer would believe) and mathematical aggregation (Cooke's classical model: score experts on seed questions with known answers, then weight them by calibration). The empirical record favors performance weighting decisively, but the method needs prepared seed variables, so it is not available to a live interviewer. What the effort imports instead is the format (quantiles) and the discipline (preserve disagreement as a first-class contested fact, with both positions and both reasonings recorded). In the IR this is why conflicts become typed issues rather than merged values, and why resolution requires an explicit user-cited record. The baseline touched this only lightly (condition 1 refused to arbitrate a within-pack dispute and designed a measurement to settle it — exemplary behavior, already dispositional).\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:51:**Motifs as obligations, and the exception sweep.** The usable core of the catalogue idea: each motif carries obligatory questions. A buffer obliges capacity and full-behavior (block, spill, or divert); a resource pool obliges size, claim discipline, and a contention rule; failure/repair obliges a trigger type, a repair-time distribution, and a repair resource. The workflow exception-patterns study adds a ready sweep: for each of five exception types (work-item failure, deadline expiry, resource unavailability, external trigger, constraint violation), ask what happens to the work item, to the case, and what recovery runs — with the empirical note that tooling almost universally ignores resource-unavailability exceptions, so elicitation must ask explicitly. Pack content for FE-1403; the ramp-scrap miss in the baseline is exactly the class of hole an obligation sweep exists to catch.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:53:**Soundness as cross-examination.** The most implementable finding in the review: the formalism generates its own interview questions. Workflow-net soundness — every reachable state can still reach completion, completion is clean, no transition is dead — converts mechanically into questions (\"you described this step but nothing can trigger it; when does it actually happen?\"). A shared input place _forces_ the contention question; token conservation asks for the invariants; Robinson's factor-versus-response distinction mechanically detects a mis-scoped quantity (throughput offered as an input). None of this requires interviewing skill — the structure obliges the questions — which is precisely the differentiation-by-machinery argument. In this effort this is ProjectionPack territory (`validate` plus issue generation) and the readout's finding 6 in reverse: both baseline artifacts contained structural bugs that no soundness check existed to catch.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:57:**The four cognitive stopping rules.** From requirements-engineering research on when analysts actually stop eliciting (Pitts and Browne): two judgment rules (stop when belief in sufficiency crosses a threshold; stop when the latest information adds too little) and two representation rules (stop when a mental checklist is exhausted; stop when your internal model of the problem stops changing). The empirical finding: analysts stop too soon — in one study, professional analysts captured 57 percent of available requirement categories before stopping — and the rule an LLM naturally implements (stop when the representation stabilizes) is one of the two associated with premature stopping. The imported counter-measure: make stopping criterion-based (the category set plus the questions table), never stability-based, and fire the clearinghouse probe before closing. The baseline extended this finding in a direction the literature had not documented: at the frontier, the failure inverts from stopping too soon to being _unable to stop at all_ — condition 1's pleasantry loop and condition 2's phantom second session are new entries for the stopping-failure catalogue, not instances of the documented one.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:59:**The assumptions document and the structured walkthrough.** From Law's simulation-practice tutorials: the output of the information-gathering phase is an _assumptions document_ — not a model — and its acceptance test is social: project it and walk it bullet by bullet with the experts, because pre-circulating for silent reading demonstrably does not produce the collective challenge that catches errors. Robinson adds the load-bearing distinction between assumptions (limited knowledge — an elicitation backlog) and simplifications (deliberate abstraction — design decisions to defend), which have different lifecycles and must not be merged into one \"limitations\" section. Scope decisions get recorded in include/exclude/justification tables, where every exclusion carries a reason. In this effort: the walkthrough is the ancestor of the interpretation-render affordance (show the captured state, get correction), the assumptions/simplifications split maps onto epistemic status, and the include/exclude tables are candidate ProjectionPack output shapes. The baseline validated the register idea beyond expectation (both conditions kept good ones unprompted) while exposing what registers cannot do — which is the machinery argument.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:67:**The Bano taxonomy and its questionnaire.** The effort's principal scoring instrument. Bano, Zowghi, Ferrari, Spoletini, and Donati observed 110 students in 28 role-played requirements interviews and catalogued 34 interviewer mistakes in seven categories — question formulation (vague questions was the most frequent mistake overall, in 21 of 28 groups), question omission (no probing questions: 11), interview order (no closing summary: 19; bad opening: 15), communication, analyst behaviour, customer interaction, and teamwork. The taxonomy ships with an operationalized Likert questionnaire (\"The analyst asked vague questions\", 1–5), which has become the de facto evaluation instrument for machine interviewers — LLMREI evaluated against it directly. The baseline scored both conditions with it and both dramatically outperformed the student cohorts, which produced the audit's most important instrument-level insight: the taxonomy was built from _novice human_ failures, and frontier-model failures are simply elsewhere (stopping, silent hardening, coverage blindness). The instrument still earns its keep as a floor check and as a mutation library for adversarial testing, but a frontier-elicitor failure catalogue is a genuine open gap this effort will have to fill itself. A related finding from the same research line: training fixes the mechanical mistakes (no summary, no probing) and barely touches the behavioural ones — practice, not instruction, is the active ingredient — which is an early version of the disposition/technique split the baseline landed on.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:69:**The LLM-interviewer studies.** Four systems shaped the baseline's design. **LLMREI** (an LLM elicitation chatbot evaluated against the Bano questionnaire) made a similar number of mistakes to human interviewers but _ended interviews too readily when users signalled impatience_ — one of the two documented stopping failures. **ReqElicitGym** (an evaluation environment with simulated users) found the opposite: models \"overwhelmingly lack effective stopping criteria\" and exhaust their turn budgets — and also that the best models elicit under a third of implicit requirements, and that effective questions arrive _late_ in dialogues. The baseline's impatience probe plus turn budget exists precisely to make both failure modes observable in one run; both reproduced, in novel forms. **Shen, Singhal and Breaux** showed that follow-up questions generated _with the mistake taxonomy in the prompt_ beat human interviewers' questions in blind preference — direct evidence that taxonomy-guided prompting works, which is the mechanism FE-1403's cards rely on. **Görer and Aydemir** found LLM-generated interview scripts lack depth — breadth is cheap, probing is the differentiator. And a fine-tuning cautionary tale from LLMREI: training on novice-interviewer transcripts failed outright, degrading the model — worth remembering whenever \"just fine-tune on transcripts\" comes up.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:71:**Mental models emerge late.** From a large study of AI-conducted qualitative interviews (381 interviews, with behavioural follow-up eight months later): interviewees' first responses differ systematically from their later ones, with mental models surfacing consistently late in the conversation. Three consequences the effort adopted: depth of probing is the differentiator, not breadth; question batching is suspect because it optimizes throughput on first-pass answers, the least valuable kind; and the first-response-versus-post-probe content delta is a measurable evaluation proxy that needs no ground-truth model — one of two literature-grounded candidates for the evaluation-proxy problem (the other being propositions-per-minute yield rates). The v0 prompt's compromise — batch two to four survey questions, never batch depth — is a deliberate softening of this finding, and the baseline suggests the compromise landed well (condition 2's batching read as an improvement over condition 1's opening barrage).\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:73:**Ambiguity as a resource.** From Ferrari, Spoletini and Gnesi's interview studies: an ambiguity in the conversation is not just a defect but a trigger — each detected ambiguity marks a spot where the speaker's mental model and the listener's diverge, so each is a follow-up question waiting to be asked, and every _undetected_ one is a missed discovery. The companion work lists the linguistic cues a listening interviewer should react to: vague terms, underspecified terms, quantifiers (\"usually\", \"mostly\"), pronouns without referents, unexplained domain terms. In this effort this grounds the v0 prompt's \"probe vague quantifiers\" rule and is the design seed for clarification hints in the pack; the baseline showed the disposition already present (both conditions chased quantifiers unprompted).\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:85:1. **\"Betting questions\" is not an import.** The baseline's standout excavation (C2's cliff/slope penalty weights) is credited to a technique no source prescribes; the v0 prompt says only \"expect to co-construct\". The method was model improvisation under prompt-directed attention — the 1→2 delta's item 5 is partly misattributed. FE-1403 should adopt the technique deliberately (decision-analysis willingness-to-bet ancestry) or reclassify it as disposition-plus-attention.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:89:5. **The premortem's \"~30%\" is one 1989 lab result** (student scenario experiments); Klein's operationalization carries no effect size of its own. Treat as indicative.\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:59:Robinson's first framework activity is \"understand the problem situation\" **[V]**, and Law's\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:86:1. understand the problem situation;\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:281:| Standard scenarios | \"Does this case fit a standard or typical scenario?\" · \"Does it fit a scenario you were trained to deal with?\" |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:288:| Situation assessment | \"If you were asked to describe the situation to a relief officer at this point, how would you summarize the situation?\" |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:290:| Hypotheticals | \"If a key feature of the situation had been different, what difference would it have made in your decision?\" |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:315:| 1 | Past and future | \"Can you remember entering a coaching situation when you knew how things got there and where they were headed?\" |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:317:| 3 | Noticing | \"Can you remember any element of a situation popping out at you that others did not notice?\" |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:342:- **Incident-probe card (CDM, verbatim above)**: narrate one real incident, timeline it, then sweep per event. Prioritise _basis of choice_ (→ conflict policy), _situation assessment via handover_ (→ marking content), _options_ and _errors_ (→ guards and exception paths).\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:356:on what happens when people answer about imagined situations, and it finds systematic\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:602:scenarios, elaborating with instances, generating counterarguments), which outperformed a\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:717:- **Comparison/contrast**: \"Can you tell me about the situation in department X…? How is it the same or different from what happens in your department?\"\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:764:construct a situation where the readings predict different behaviour and ask which happens. Note\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:797:the individual distributions as a width baseline, **do not show it to the experts**, and be\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:812:- Where the disagreement concerns a model parameter, the honest projection is a range or a scenario pair, with the model's answer reported under both.\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:858:| **Live data feed** | The _use_ is reactive — re-run from observed current state. A property of the use case, not the system. | The use is design-time or comparative. |\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:890:### 6.3 Which use case earns the showcase?\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:906:cases will not earn dynamic colouring**, so a showcase demo needs either a purpose-chosen use case\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:1008:\"vanilla LLM baseline\" has already been measured to do.\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:1075:publishable experiment and it is nearly a by-product of the September demo if any use case has\ndocs/planning/process-model-elicitation/research/elicitation-strategy-literature.md:1149:_situation assessment_ handover framing elicits what the marking must record; and the\ndocs/planning/process-model-elicitation/baseline/protocol.md:21:reference model — FE-1363 retained it as the flat-baseline testbed). The interviewee is a\ndocs/planning/process-model-elicitation/baseline/protocol.md:23:defined by [situation-pack.md](situation-pack.md). The pack was authored from the use case's\ndocs/planning/process-model-elicitation/baseline/protocol.md:33: situation pack; the expert never sees the v0 prompt.\ndocs/planning/process-model-elicitation/baseline/protocol.md:51:`turbo run baseline:run --filter '@hashintel/brunch-agent' -- 1` /\ndocs/planning/process-model-elicitation/baseline/protocol.md:52:`turbo run baseline:run --filter '@hashintel/brunch-agent' -- 2` (needs `ANTHROPIC_API_KEY`).\ndocs/planning/process-model-elicitation/baseline/protocol.md:70: FE-1358 survey (scenario-or-dead-net, PascalCase identifiers, no timing fields, arc shape).\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md:43: scenarios (\"two lines need the crew at the same moment — what actually happens?\").\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:1:# Situation pack — Vestera Coatings (baseline control, FE-1361)\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:4:the user in the baseline-control interviews. It is authored from the operational prose of the\ndocs/planning/process-model-elicitation/baseline/situation-pack.md:5:Production Process Scheduling use case (Notion DB entry), **never** from any net outline or\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:27:- ⚠️ **Premise correction:** the familiar six-category taxonomy (traditional/group/prototyping/model-driven/cognitive/contextual) is **not** theirs — it is **Nuseibeh & Easterbrook, \"Requirements Engineering: A Roadmap,\" ICSE 2000, pp. 35–46, DOI 10.1145/336512.336523**. Zowghi & Coulin list ~20 techniques (interviews, questionnaires, task analysis, domain analysis, introspection, repertory grids, card sorting, laddering, group work, brainstorming, JAD, workshops, ethnography, observation, protocol analysis, apprenticing, prototyping, goal-based approaches, scenarios, viewpoints) and reduce them to a **core eight** (interviews, domain analysis, groupwork, ethnography, prototyping, goals, scenarios, viewpoints) in two tables: Table 2.1 maps techniques × the five activities (interviews/domain/groupwork suit all five); Table 2.2 marks each pair Complementary vs Alternative (e.g., interviews complementary with goals/scenarios/viewpoints, alternative to groupwork/ethnography/prototyping).\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:29:- On matching technique to situation: they cite Hickey & Davis's four reasons analysts actually choose ((a) only technique known, (b) favorite, (c) prescribed by methodology, (d) intuition), and close: \"**requirements elicitation still remains more of an art than a science**.\" Ethnographic techniques are flagged as \"very expensive… requir[ing] significant skill,\" with the observer effect noted.\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:34:- **Hickey & Davis, \"Requirements Elicitation and Elicitation Technique Selection: A Model for Two Knowledge-Intensive Software Development Processes,\" HICSS-36, 2003, DOI 10.1109/HICSS.2003.1174229** (read in full). The model: elicitation as iterated function application — **elicitᵢ(Rᵢ, Sᵢ, tᵢ) → Rᵢ₊₁, Sᵢ₊₁**, where Rᵢ = current knowledge of requirements, Sᵢ = situation (problem-domain + solution-domain + project characteristics), tᵢ ∈ T (all known techniques). Selection: **σ(Rᵢ, Sᵢ, χ(T)) → {applicable techniques}**, where χ(T) = static technique characteristics; then a personal selector **π({t}, P) → tᵢ** applying analyst preferences. Composed: elicitᵢ(Rᵢ, Sᵢ, π(σ(Rᵢ,Sᵢ,χ(T)),P)). A methodology is then just a fixed sequence of elicit steps — and their critique of methodologies is that fixing tᵢ a priori assumes Sᵢ and Rᵢ in advance: \"one size [methodology] fits all.\"\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:36:- The situational-characteristics ontology is in **Hickey & Davis, \"A Tale of Two Ontologies: The Basis for Systems Analysis Technique Selection,\" AMCIS 2003, paper 386** (read in full): 24 printed coded situational characteristics in five categories (problem domain: FUZZ, CPLX, CNFL, MATU, SECU, RESP, SAFE, RELI; solution domain: TYPE, COTS, OUTS; stakeholders: #STK, STEX, STCM, STCP, STTV, STAC, STDV; solution builders: SOEX, SOCO, SOSW, SOTO; bridge-builders: BBEX, BBTE, BBCO) out of \"over fifty\" isolated; plus a **ten-dimension technique attribute vector** (physical co-location, temporal co-location, record-keeping, analyst role, convergence/divergence, anonymity, stakeholder count, tool-based, product/human focus, direct/indirect). Notable empirical observation: \"a moderately good technique for a specific situation in the hands of an experienced 'master' can become an ideal technique for that situation.\" ⚠️ No numbered \"propositions\" exist in any of the three 2003 papers; if JMIS 2004 adds them, that is unverified.\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:46:- On matching to situation: they recommend a \"**zooming**\" method — cheap methods broadly, \"the more expensive but detailed methods… only employed selectively for problems that have been determined by other techniques to be especially important\"; recommended sequence: ethnography first, then interviews, then conversation/interaction analysis on selected hot spots. ⚠️ RE'93 contains **no table or figure**; the requirements-are-emergent claim (\"requirements… gradually emerge from interactions\") is verbatim only in Goguen's 1994 chapter \"Requirements Engineering as the Reconciliation of Technical and Social Issues\" (in Jirotka & Goguen, _Requirements Engineering: Social and Technical Issues_, Academic Press, 1994).\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:144:- **IEEE Std 830-1998** §4.3.3: \"An SRS is complete if, and only if, it includes… a) All significant requirements… b) Definition of the responses of the software to all realizable classes of input data in all realizable classes of situations… c) Full labels and references to all figures, tables… and definition of all terms and units of measure.\" §4.3.3.1: \"Any SRS that uses the phrase 'to be determined' (TBD) is not a complete SRS.\" (Status: superseded by 29148:2011.)\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:171:- **Korn, A., Gorsch, S., Vogelsang, A., \"LLMREI: Automating Requirements Elicitation Interviews with LLMs,\" RE'25, pp. 19–30, DOI 10.1109/RE63999.2025.00013 (arXiv:2507.02564)** (read in full). GPT-4o chatbot, 33 simulated stakeholder interviews, evaluated **directly against the Bano et al. mistake questionnaire** (Question Formulation / Question Omission / Order of Interview / Communication Skills / Customer Interaction; 2 of the human categories dropped as inapplicable). Results: LLMREI made a similar number of mistakes to human interviewers; raters even scored its communication skills _better_; long \"least-to-most\" prompt embedding Ferrari et al. guidelines beat a 3-sentence zero-shot prompt on mistake avoidance (64.2% vs 59.1% disagree-mistake-present); elicited up to 60.9% of ground-truth requirements fully + 12.8% partially (73.7% total; ground truth = 12- and 8-requirement scenarios); ~50% of questions context-dependent. **What failed:** fine-tuning GPT-3.5 on 50 transcripts of Ferrari et al.'s student interviews failed entirely (\"incoherent responses,\" lost focus — attributed to poor quality of novice-interviewer training data); hallucination (bot invented a project price estimate); boundary-crossing (asked for the user's email); and the bot **ended interviews too readily** when users signaled impatience, \"missed opportunities to gather crucial information\" — i.e., no stopping discipline.\ndocs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md:173:- **Jin, D., Jin, Z., et al., \"ReqElicitGym: An Evaluation Environment for Interview Competence in Conversational Requirements Elicitation,\" arXiv:2602.18306 (2026; under submission to TOSEM)** (read in full). 101 website scenarios, LLM oracle user + evaluator (κ = 0.72 vs experts); metrics **IRE** (implicit-requirements elicitation ratio), ESR, **TKQR** (nDCG-style, rewards early key questions). Findings: best LLM reaches **IRE 0.32**; models \"overwhelmingly favor probing over clarification and **often lack effective stopping criteria**\" (GPT-5.2 exhausts the 20-turn budget); effective questions emerge late; style-related requirements consistently missed.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:5:- Simulated expert: claude-sonnet-5 + situation-pack.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:86:3. How do you actually sequence today — a spreadsheet, a rule of thumb, gut feel? I'd like the model to be able to reproduce your current approach as a baseline, so you can compare alternatives against it.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:136:Today I sequence off a spreadsheet — I map the week's demand book onto the three lines, rough rate per product per line, add changeover by feel, and I re-juggle it out loud at the 07:30 huddle most mornings. That sheet is the baseline you should try to reproduce first, honestly — if your model can't match what the sheet does, I won't trust what it says about doing better.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:154:What it _will_ do: let you set up the Tuesday-morning-Line-2-is-dead scenario and race three or four candidate responses against each other (push everything to Line 1 / hold the tint block and pull whites forward / split across 1 and 3 and eat the specialty changeover). You get lateness and changeover hours for each. Over enough simulated breakdowns you can find which _rule_ wins on average, and that's the thing you'd actually take to the huddle — not a schedule, a reflex.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:201:**One validation question:** should the baseline match _the sheet's plan_, or _what actually happened_? They differ, and the gap is usually your improvising at the huddle. I'd rather match actuals — that's what makes it credible to your boss — but I'd use the sheet for the sequencing logic.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:295:| `T_Fail_L` / `T_Repair_L` | stochastic / scenario | — |\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:316:The baseline reproduces you:\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:343:| A7 | Line 1 mill motor modelled as scenario, not random | too rare to sample meaningfully in a 6-week run; you'd get noise, not signal |\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:371:Send the shift pattern and I'll start wiring the baseline against your ERP export. First thing I'll report is whether the model can reproduce your actual weekly output per line — if it can't, that's the finding, and we fix the model before it tells you anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:387:Go ahead and wire the baseline against the ERP export once you've got it, and I'll chase the shift confirmation on Line 3 overtime frequency if you need it — right now \"rare\" is genuinely the best I've got, I don't have a count.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:476:Line 3 overtime: don't go counting it. Treat as zero in baseline and use it as a **lever** — \"what is it worth to approve Line 3 overtime twice a week?\" is a better question than \"how often does it happen,\" and it takes work off you.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:490:I'll wire the baseline the day the ERP export lands. First report is whether the model reproduces your actual weekly output per line, before it says anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:535:**A new rule falls out of that:** don't commit a morning family switch unless there's enough work in that family to fill through 22:00. Switch Line 2 to white at 09:00 with four hours of white orders behind it and you get four good hours and then a dead line until morning — you've spent a 3-hour wash _and_ stranded the evening. Call it the fill-the-shift rule; I'll add it as a policy variant and test it against the baseline.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:569:- **Policy variants to race:** baseline (you) / fill-the-shift rule / time-of-day wait-rule / tech shift 06:00–14:00 vs 10:00–18:00 vs split / Line 3 overtime as a purchasable lever.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:581:**From me, on ERP arrival:** baseline wired and the reproduction check — actual weekly output per line, and roughly eight family switches a week. If it can't hit those, that's the finding and I fix the model before it says anything about Tuesdays.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:621:**When the export lands** I'll wire the baseline and run the reproduction check first — weekly output per line, and the switch count. If it misses, that's the interesting result and I'll come back with what the model thinks is happening versus what your ERP says happened. That's normal, not a setback; the gap is usually where an assumption is wrong, and we have eleven of those on the register waiting to be caught.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:643:One thing not worth waiting a fortnight for: if the tick sheet shows an operator doing a family switch in week one, send it straight over. That single row would retire a hard guard in the model and change what the baseline predicts, so I'd rather rework early than build two weeks on it.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:875:| **A7** | Line 1 mill motor modelled as a **named scenario**, not a random failure | Too rare to sample in a 6-week run — you'd get noise, not signal | n/a — design choice, but say so out loud |\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:887:**A19 is the one to watch.** A single contrary row retires a hard guard and changes what the baseline predicts. Send it the same day it appears.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1025:| `T_Fail_L` | `Exp(MTBF_L)` | A6/A8. Line 1 mill motor is a **scenario injection**, not sampled (A7) |\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md:1291: <lever>count = 2 (baseline) | 3 (third-tech arm)</lever>\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:38: only. A generated net that should start with tokens must emit a `scenario`, or\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:131: via Babel, metric and scenario expressions via `new Function()` — so it needs\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:215:check `extensions` before using optional SDCPN features, use scenario parameters\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:221:scenario initial state, an auto-layout policy, and finally:\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:225:> continuous dynamics, parameters, visualizer code, and scenarios:\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:460: `{ places, transitions, types, differentialEquations, parameters, scenarios?,\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:545: scenarios?: Scenario[];\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:556:`scenarioSchema`, `subnetSchema`, `componentInstanceSchema`), plus\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:557:`src/schemas/{entity,metric,scenario}-schema.ts`.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:602:name but is unrelated: it migrates _positional scenario rows_ when a user edits a\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:605:notes \"Name-only renames are NOT represented here: scenario rows are positional,\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:611: `metric-schema.ts`, `scenario-schema.ts`. Every entity is `z.strictObject` with\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:668:`scenarioParameters`, `initialState`, with `parameterOverrides` defaulting to\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:669:`{}`; scenario parameter `type` admits a fourth value, **`ratio`**, that net\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:672:and notably has **no** `scenarios`/`metrics`/nested `subnets`.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:684:`differentialEquations`, `parameters`, `scenarios`, `metrics`, `subnets`, and\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:731:- **a scenario** — `scenario.initialState`, a discriminated union\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:732: (`schemas/scenario-schema.ts:42-80`):\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:734: _string expressions_ with `parameters` and `scenario` in scope (e.g.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:735: `\"scenario.population * (1 - scenario.infected_ratio)\"`, `Math.round`ed and\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:741:`scenario`.** A bare `places` + `transitions` file loads as an empty-marking net\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:791:- Also in-format: global `parameters`, named `scenarios` (parameter overrides +\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:811:> **TikZ** — […] Token types, dynamics, read/inhibitor arcs, scenarios, and\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:851: `id` with no `scenarios`/`metrics`.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:1006: `scenario` has no initial marking, so it opens but simulates to nothing. These\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:1034: `action-schemas.ts` (561 lines) / `metric-schema.ts` / `scenario-schema.ts`.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:1131:- Optimization via Optuna over a scenario's flat parameters, streamed trials,\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:5:- Simulated expert: claude-sonnet-5 + situation-pack.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:249:**21. Variability — white batch, Line 2 fill.** Typical, like I said, an hour to an hour and a half. One time in ten it's worse — that's your filler jam scenario, tack on 20 minutes to half an hour for a small jam, or if it's a bad one, could eat the rest of the shift, but that's rarer than one-in-ten, that's more like once every couple weeks across the whole line, not per batch. One time in ten it's better — honestly not much better, maybe you shave ten minutes if everything's smooth, there's not a lot of upside slack in fill, it runs close to its rate when nothing's wrong. So the bad tail is real and fat, the good tail is thin. The cause of the bad tail is almost always the filler — jams, or waiting on packaging materials running low, that happens too, cases or labels not being restocked fast enough, that's more a warehouse problem than a line problem but it still stops the fill head.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:311:Blocked in — tomorrow, fifteen minutes, items 1, 4 and 5. I'll come with those as concrete scenarios rather than open questions so we don't burn time.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:361:1. **Conflict points** — I'll put four concrete scenarios to you: two lines wanting the crew simultaneously; how you sequence within a line; whether QA can be jumped and who pays; what triggers and what blocks the Saturday ask.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:384:I'll come tomorrow with the four conflict scenarios pre-written, so you're reacting to concrete cases rather than generating from scratch. Fifteen minutes should hold if we don't wander.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:402:So you're not generating from cold tomorrow, here are the four scenarios in advance — no need to prepare, just useful if you happen to read them before we talk. Answer them the way you actually behave, not the way the procedure would read.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:418:5. **Changeover crew = one indivisible 2-person server**, one job at a time, day shift with discretionary flex. _From Q22_, but I've assumed the quick rinses also consume the full crew. _Check:_ scenario A will probably settle it.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:823:**The punitive mechanism (from your Scenario A answer):** because the crew requires `P_CrewShift`, a line that misses the crew window doesn't wait for the crew to _finish_ — it waits until the crew's next shift _starts_. Line 1 in your scenario doesn't lose 2 hours, it loses the whole overnight. This falls out of the net structure; no special rule needed.\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md:974:6. **Specialty runs on Line 1 only.** Follows from L2 not being piped and L3's list — but I never asked whether L3 can run specialty. _Check:_ one question. **If L3 can, the mill-motor scenario changes materially**, because specialty currently has nowhere to go.\n", - "exitCode": 0 -} -``` - -## Assistant - -That implies a **two-track commitment**, not a cold-start cut: - -```text -Shared CPS capability -├─ Semantic core -│ ├─ captures → model → SDCPN -│ ├─ provenance and correction -│ └─ controller + completion -│ -├─ Live-demo runbook -│ └─ bounded review-and-revise with conspicuous consequences -│ -└─ Cold-start runbook - ├─ capable of reaching objective-complete models - ├─ available for attendee experimentation - └─ evaluated offline against the existing baseline -``` - -Issue projection: - -- **Shared core:** FE-1482, FE-1480, FE-1478, FE-1431. -- **Bounded live path:** FE-1479, FE-1438, FE-1439, FE-1477/1440. -- **Cold-start quality path:** FE-1402, FE-1403, FE-1404, FE-1406. -- **Likely missing:** controller/runbook implementation, full-model quality evaluation, and explicit legibility/witness ownership. - -The earlier cuts should become: - -- Do not require complete cold-start elicitation **during the live demo**. Do implement and evaluate a `build-from-scratch` runbook offline. -- Do not attempt an exhaustive CPS ontology. Implement the smallest **coherent, objective-complete** domain contract: every demanded slot is stated, absent, conflicted, or explicitly unresolved—not merely omitted because the fixture did not exercise it. - -“Better than baseline” must also be specific. The baseline showed that bare Claude already interviews surprisingly well. Brunch needs to win on machinery: - -- computed coverage rather than apparent conversational completeness; -- no silent hardening of uncertain statements; -- preserved conflict and absence; -- exact provenance; -- valid, reproducible projection; -- disciplined stopping and explicit residual gaps. - -For legibility, I suggest every frontier produces a persistent proof bundle: - -```text -Proof bundle -├─ Instrument — runnable, domain-language surface exposing state and transitions -├─ Snapshot — immutable record of what a human reviewed and decided -└─ Oracle — executable regression check preserving the mechanical claim -``` - -This adapts `ds-prototype`’s strongest patterns—explicit question, guided scenarios, free play, visible state—without treating the result as disposable. - -And agreed: the current `STEERING.md` is too prose-heavy for its long-term role. After Frontier 0, it should collapse into: - -1. objective and constraints; -2. compact frontier/issue tree; -3. active bets and confidence; -4. replan triggers; -5. links to immutable decision/proof snapshots. - -The next Frontier 0 decision is practical: **should Vestera Coatings remain the cold-start benchmark even if Dora selects a different live-demo use case?** It already has the situation pack and two baseline conditions, making it our cheapest comparative oracle. - -## User - -I think it can remain the benchmark for now; but there are specific scenarios, corresponding to levels of PN modelling complexity, which have been outlined in Dora's blog post and which Yannis is preparing as petri nets as well. Eventually we may want to switch to one of those, so we're sharing a reference surface with the rest of the team. In any case I think the "baseline" should be re-established soon, as we can probably improve the base flat prompt for Claude as well. - -And BTW we need a better convention for where to keep our fixture material, for the various kinds of evaluations and tests we are running - -## Assistant - -That suggests the benchmark should have a stable **evaluation role** independent of whichever domain scenario currently supplies its content. I’m checking the existing scenario levels and fixture sprawl before proposing a storage convention, so we don’t encode today’s Vestera shape into the directory taxonomy. - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n '^#{1,4} |Level|level|scenario|use case|complex' 'docs/reference/2026-08 SDCPNs for cyber-physical systems.md' | sed -n '1,240p'", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "printf '%s\\n' '--- fixture-like paths in context root'; rg --files | rg '(^|/)(fixtures?|golden|snapshots?|examples?|baseline|scenarios?|test-data|mocks?)(/|$)|\\.(fixture|golden|snap)\\.' | sort | sed -n '1,320p'", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "printf '%s\\n' '--- fixture-like paths in app'; rg --files apps/brunch-agent | rg '(^|/)(fixtures?|golden|snapshots?|examples?|baseline|scenarios?|test-data|mocks?)(/|$)|\\.(fixture|golden|snap)\\.' | sort | sed -n '1,260p'; printf '%s\\n' '--- petrinaut brunch fixture-like paths'; rg --files apps/petrinaut-website | rg 'brunch|fixture|golden|scenario' | sort | sed -n '1,320p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "printf '%s\\n' '--- test fixture imports/references'; rg -n \"fixture|golden|snapshot|situation-pack|condition-[12]|scenario\" packages/*/test packages/*/src ../../../apps/brunch-agent/src ../../../apps/brunch-agent/test 2>/dev/null | sed -n '1,300p'", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cg7w7Kqs6mbguihmZRl` - -``` -{ - "output": "1:# SDCPNs for cyber-physical systems\n5:# SDCPN as a Common Language for Physical and Digital Systems\n7:Most industrial systems have a physical part (machines, stock levels) and a digital part (control rules, schedulers). A Stochastic Dynamic Coloured Petri Net (SDCPN) can represent both cyber and physical in the same net, sharing the same state. A change on one side immediately affects the other: a physical event enables or blocks decisions, and decisions change the physical trajectory. There is no handoff between separate tools and no assumption that the other side behaves as expected.\n11:This blog post builds one such model through five levels of the formalism, then applies the full SDCPN to two further industries to explore how it can be used to model various domains and its limitations.\n13:# What is an SDCPN?\n15:A [Petri net](https://petrinaut.org/) is a directed graph of places, transitions, and arcs. Tokens sit in places, transitions fire and move tokens between places, and the arrangement of all tokens at a given moment represents the state of the system. A plain Petri net records what can happen and in what order, but without modelling durations, likelihoods, or what distinguishes one token from another, it cannot answer \"how often\" or \"how likely\" in a scenario.\n29:## Where SDCPNs come from\n33:The formalism was designed to support a safety case: a defensible and quantitative statement about how often something bad happens in a system too complex to test exhaustively. The ARIA Safeguarded AI programme chose the same formalism for the same reason. Its goal is a world model expressive enough to hold both the physical system and the AI that controls it, with a safety specification over both. The air traffic problem, with continuous dynamics, multiple agents, human and automated controllers, rare catastrophic events is exactly the class of system Safeguarded AI is building for. The only difference is the controller: the original use case modelled human operators and procedural automation; Safeguarded AI focuses on neural networks.\n35:# Modelling real world with SDCPNs\n37:The following sections model an industrial supply chain process progressively, adding one feature of the formalism at each level until the model is a full SDCPN. To illustrate the expressivity of SDCPNs, we model two further domains as full SDCPNs: truck fleet maintenance and semiconductor fabrication.\n39:## Industrial gas supply chain\n41:Taking an industrial gases supply chain as an example use case: a gas supplier delivers liquid gases (e.g. nitrogen, oxygen) to customer sites by road tankers. The supply chain operates in a standard practice where the supplier owns the liquid in each customer's tank, reads the level by telemetry, and decides when to send a refill. The customer draws product as needed and only pays for what they consumes, but does not place orders.\n51:### Plain Petri net\n57:As the customer consumes the nitrogen and some boils off, the level drops. When the level drops to 15 (representing the telemetry-based sensor in the tank), an order is placed. A tanker dispatches, arrives and delivers 12 units (only if 12 units of space exist in the tank). The permit and tanker return on delivery and cycle repeats.\n61:If the tank is completely full, a relief valve opens and reduces the level of gas (by 1 unit). Under this level trigger order policy, venting is unreachable since the maths of the reorder point and load size prevent it (gas only refills by 12 units when below 15 units). In later timed-extensions of the model, we introduce pressure-driven venting since in practice, venting is required when pressure gradually builds as the liquid warms.\n63:\\[BELOW\\] shows the net for a variant of the order policy based on consumption-trigger. Instead of reordering when the level drops below a threshold, the system reorders after every 8 units are drawn by the customer without accounting for any evaporation. This results in a failure mode whereby, If enough nitrogen boils off, the tank empties without the consumption counter ever reaching 8\\. The system reaches a deadlock: the tank is at zero, fewer than 8 units have been drawn since the last order, and nothing in the model can change the state in the system so the production line stops and never restarts.\n65:Without time accounted for in the model, the net picks any enabled transition to fire without any rules on ordering. There can be a scenario where the transition for consuming nitrogen is fired repeatedly and empties the contents without dispatching the tanker for refill. Adding durations fixes this so events happen according to rates rather than random choice, which we explore in the next progression to SPN.\n69:### SPN\n85:### SCPN\n95:### DCPN\n97:The Dynamic Coloured Petri Net (DCPN) replaces the stack of unit tokens with a single token governed by differential equations, to model the level of gas and pressure as real numbers that can fall or grow continuously. With the inclusion of dynamics, the net can now model scenarios that simpler nets couldn’t:\n105:### SDCPN\n113:## Truck fleet maintenance\n133:## Semiconductor wafer fabrication\n139:For this use case we modelled 16 chambers across 4 machine groups (4 lithography, 6 etch, 4 deposition, 2 inspection), 3 product types (logic, memory, analog) arriving stochastically, a capacity limit of 50 lots in progress, and 3 technicians shared between planned and unplanned work.\n141:Each lot token carries its product type, current layer, cumulative defects, age, and a customer due date. Each machine token holds data on its condition, particle count, hours since maintenance, machine group, qualification level, and batch counter. The degradation mechanisms use the following SDCPN features:\n147:- **Per-chamber process drift as stochastic dynamics.** Each chamber's process accuracy varies independently via a second diffusion process. Drift in either direction from zero increases defect rates. Maintenance recalibrates the chamber, but calibration is imperfect and each reset samples a small residual error. This means two chambers on the same tool can produce different defect rates even at identical condition and particle levels.\n155:- **Chamber-level recipes.** Processing time depends on the product being made. For example, a furnace step takes 5 hours at baseline; analog lots take 15% longer, memory lots 15% shorter. The same applies to lithography and etch steps.\n161:The model can help fabrication managers understand the interaction between maintenance policy,chamber calibration, batch sizing and in progress capacity in relation to yield and on-time delivery. The main simplifications are that each chamber processes one lot at a time and lots cannot be split for partial rework (lot-splitting). This means the model's absolute throughput figures are lower than a real foundry's , but relative comparisons between scenarios remain valid because all scenarios share the same simplification. Modelling lot-splitting would reduce the cost of contamination events by allowing partial recovery as a secondary effect, but does not change the fundamental question of when to maintain.\n163:## Why SDCPNs?\n167:### Structural guarantees\n177:These checks require a finite state space. Once tokens carry real-valued data (continuous levels, pressures, temperatures), the state graph cannot be exhaustively checked and these proofs do not apply directly. Extending formal guarantees to models with continuous state and stochastic dynamics is an open research problem, and one of the reasons the ARIA Safeguarded AI programme is investing in this formalism.\n179:### Probabilistic claims\n187:# Conclusion\n197:- Continuous dynamics replace discrete approximations with differential equations for flows like gas levels, machine degradation and contamination.\n201:What makes the formalism worth the added complexity is not that it can represent these interactions, but that it can quantify the frequency, costs and conditions that trigger them. When the controller is an AI making real-time decisions over a physical system, that quantification is what separates a deployment backed by evidence, and eventually verification, from one backed by assumption.\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cg7w81353sGji2BwSA0` - -``` -{ - "output": "--- fixture-like paths in context root\ndocs/planning/process-model-elicitation/baseline/opening-message.md\ndocs/planning/process-model-elicitation/baseline/protocol.md\ndocs/planning/process-model-elicitation/baseline/readout.md\ndocs/planning/process-model-elicitation/baseline/run.ts\ndocs/planning/process-model-elicitation/baseline/situation-pack.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1-model.txt\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-1.raw.json\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2-model.txt\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.md\ndocs/planning/process-model-elicitation/baseline/transcripts/condition-2.raw.json\ndocs/planning/process-model-elicitation/baseline/v0-prompt.md\npackages/core/test/architecture/fixtures/baseline-anthropic-stub.ts\npackages/transport-aisdk/test/fixtures/elicitor-initial.normalized.json\npackages/transport-aisdk/test/fixtures/panel-follow-up.sse\npackages/transport-aisdk/test/fixtures/panel-initial.post.json\npackages/transport-aisdk/test/fixtures/panel-initial.sse\npackages/transport-aisdk/test/fixtures/panel-tool-results.post.json\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cg7w8PKTWflM0m25oq9` - -``` -{ - "output": "--- fixture-like paths in app\n--- petrinaut brunch fixture-like paths\napps/petrinaut-website/scripts/brunch-sse-fixture.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-actual-mode-provider.tsx\napps/petrinaut-website/src/main/app/brunch-demo/brunch-actual-mode-route.tsx\napps/petrinaut-website/src/main/app/brunch-demo/brunch-definition.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-demo-app.tsx\napps/petrinaut-website/src/main/app/brunch-demo/brunch-endpoint.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-frame-parsers.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-petrinaut.tsx\napps/petrinaut-website/src/main/app/brunch-demo/brunch-protocol.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-route.ts\napps/petrinaut-website/src/main/app/brunch-demo/brunch-status-page.tsx\napps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cg7w8hS9Dj7f9TMhvU8` - -``` -{ - "output": "--- test fixture imports/references\n../../../apps/brunch-agent/test/walking-skeleton.integration.ts:58: question: \"What outcome should the scenario describe?\",\n../../../apps/brunch-agent/test/walking-skeleton.integration.ts:287: \"What outcome should the scenario describe?\",\n../../../apps/brunch-agent/test/walking-skeleton.integration.ts:293: \"What outcome should the scenario describe?\",\n../../../apps/brunch-agent/test/petrinaut-chat.test.ts:86: const golden = JSON.parse(\n../../../apps/brunch-agent/test/petrinaut-chat.test.ts:90: \"../../../libs/@hashintel/brunch-agent/packages/transport-aisdk/test/fixtures/elicitor-initial.normalized.json\",\n../../../apps/brunch-agent/test/petrinaut-chat.test.ts:95: expect(normalizedChunks(result.chunks, result.messageId)).toEqual(golden);\n../../../apps/brunch-agent/test/petrinaut-ask.test.ts:51: // exactly as the FE-1436 application golden streams its second step.\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:17: \"../../../libs/@hashintel/brunch-agent/packages/transport-aisdk/test/fixtures\",\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:20:const fixture = (name: string): string =>\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:321: test(\"encodes fixed harness events as the real-panel golden SSE\", async () => {\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:345: body: fixture(\"panel-initial.post.json\"),\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:352: fixture(\"panel-initial.sse\").trimEnd(),\n../../../apps/brunch-agent/test/transport-aisdk-server.test.ts:420: body: fixture(\"panel-tool-results.post.json\"),\npackages/binding-flue/src/index.ts:207: applied.snapshot,\npackages/binding-flue/src/index.ts:233: // live slot rather than the render-time persistent-state snapshot.\n../../../apps/brunch-agent/test/petrinaut-chat.integration.ts:49: const fixturePath = fileURLToPath(\n../../../apps/brunch-agent/test/petrinaut-chat.integration.ts:51: \"../../../libs/@hashintel/brunch-agent/packages/transport-aisdk/test/fixtures/panel-initial.post.json\",\n../../../apps/brunch-agent/test/petrinaut-chat.integration.ts:62: body: await readFile(fixturePath, \"utf8\"),\npackages/binding-flue/src/capabilities.ts:80: \"public materialized history snapshot over a host-injected conversation URL/transport; `role`/`purpose` discriminate provenance; no raw entry ranges\",\npackages/binding-flue/test/capture-accounting.test.ts:18: const snapshot = {\npackages/binding-flue/test/capture-accounting.test.ts:88: snapshot,\npackages/binding-flue/test/capture-accounting.test.ts:97: const snapshot = {\npackages/binding-flue/test/capture-accounting.test.ts:141: snapshot,\npackages/binding-flue/src/capture-accounting.ts:13: snapshot: CaptureStoreSnapshot,\npackages/binding-flue/src/capture-accounting.ts:17: for (const capture of snapshot.captures) {\npackages/core/src/plugin.ts:22: /** The artifact family this plugin elicits — gherkin scenarios, assurance arguments. */\npackages/binding-flue/src/history-reader.ts:90: snapshot: Pick<FlueConversationSnapshot, \"messages\">,\npackages/binding-flue/src/history-reader.ts:92: const { messages } = snapshot;\npackages/binding-flue/src/history-reader.ts:174: snapshot: FlueConversationSnapshot,\npackages/binding-flue/src/history-reader.ts:176: projectFlueHistoryForSweep(snapshot).map((entry, index) => ({\npackages/binding-flue/src/history-reader.ts:180: materialized: materializedJson(snapshot.messages[index]!),\npackages/binding-flue/src/history-reader.ts:197: const snapshot = await peek(sessionId);\npackages/binding-flue/src/history-reader.ts:200: substrateConversationId: snapshot.conversationId,\npackages/binding-flue/src/history-reader.ts:201: offset: snapshot.offset,\npackages/binding-flue/src/history-reader.ts:202: ...(snapshot.incarnation === undefined\npackages/binding-flue/src/history-reader.ts:204: : { incarnation: snapshot.incarnation }),\npackages/binding-flue/src/history-reader.ts:205: entries: classifyMessages(snapshot),\npackages/binding-flue/src/history-reader.ts:206: settlements: snapshot.settlements.map(materializedJson),\npackages/binding-flue/src/history-reader.ts:208: return snapshot;\npackages/binding-flue/test/local-capture-store.test.ts:90: const snapshot = await reopened.read();\npackages/binding-flue/test/local-capture-store.test.ts:91: expect(snapshot.captures).toHaveLength(1);\npackages/binding-flue/test/local-capture-store.test.ts:92: expect(snapshot.captures[0]!.content).toEqual({ value: \"alpha\" });\npackages/binding-flue/test/local-capture-store.test.ts:127: const snapshot = await createLocalCaptureStore(path).read();\npackages/binding-flue/test/local-capture-store.test.ts:128: expect(snapshot.captures.map((capture) => capture.content)).toEqual([\npackages/binding-flue/test/local-capture-store.test.ts:189: // And still readable through the parser, which is what makes it a snapshot\npackages/binding-flue/test/local-capture-store.test.ts:209: const captureId = created.snapshot.captures[0]!.id;\npackages/binding-flue/test/local-capture-store.test.ts:220: // wrote it. If the snapshot aliased any of it, the result the caller was\npackages/binding-flue/test/local-capture-store.test.ts:226: retracted.snapshot,\npackages/transport-aisdk/test/golden.test.ts:36:const FIXTURES = join(import.meta.dirname, \"fixtures\");\npackages/transport-aisdk/test/golden.test.ts:53: test(\"validates the load-bearing fields in the complete initial panel POST fixture\", () => {\npackages/binding-flue/src/local-capture-store.ts:105: document: { ...document, captureStore: result.snapshot },\npackages/core/src/testing/index.ts:2: * `@hashintel/brunch-agent/testing` — fixtures, arbitraries, and the replay driver.\npackages/core/src/testing/index.ts:9: * land with their own slices; this module holds the seed fixtures they grow\npackages/core/src/testing/index.ts:17:const fixtureProposalSchema = v.strictObject({\npackages/core/src/testing/index.ts:24: content: v.strictObject({ value: v.literal(\"fixture\") }),\npackages/core/src/testing/index.ts:28: * The smallest honest plugin (spec §11.3), as a fixture: a flat record list and\npackages/core/src/testing/index.ts:35: name: \"plugin-fixture\",\npackages/core/src/testing/index.ts:36: targetDomain: \"fixture\",\npackages/core/src/testing/index.ts:39: name: \"fixture-proposal\",\npackages/core/src/testing/index.ts:40: description: \"A fixture-only capture proposal.\",\npackages/core/src/testing/index.ts:41: schema: fixtureProposalSchema,\npackages/binding-flue/test/history-reader.test.ts:30:const snapshot = {\npackages/binding-flue/test/history-reader.test.ts:99: expect(projectFlueHistoryForSweep(snapshot)).toEqual([\npackages/binding-flue/test/history-reader.test.ts:127: ...snapshot,\npackages/binding-flue/test/history-reader.test.ts:190: test(\"uses only the host-resolved URL and transport, then archives the public snapshot\", async () => {\npackages/binding-flue/test/history-reader.test.ts:196: return Response.json(snapshot);\npackages/binding-flue/test/history-reader.test.ts:205: expect(await reader.peek(\"session-1\")).toEqual(snapshot);\npackages/binding-flue/test/history-reader.test.ts:208: expect(await reader.read(\"session-1\")).toEqual(snapshot);\npackages/binding-flue/test/history-reader.test.ts:226: JSON.parse(JSON.stringify(snapshot.messages[0]!)),\npackages/binding-flue/test/history-reader.test.ts:245: const capture = captured.snapshot.captures[0]!;\npackages/binding-flue/test/history-reader.test.ts:279: snapshot: { captures: [expect.any(Object), expect.any(Object)] },\npackages/binding-flue/test/history-reader.test.ts:316: const snapshots = [\npackages/binding-flue/test/history-reader.test.ts:318: ...snapshot,\npackages/binding-flue/test/history-reader.test.ts:323: messages: snapshot.messages.slice(0, 3),\npackages/binding-flue/test/history-reader.test.ts:325: { ...snapshot, offset: \"2\" },\npackages/binding-flue/test/history-reader.test.ts:330: Response.json(snapshots.shift()!)) as unknown as typeof fetch,\npackages/binding-flue/test/history-reader.test.ts:370: expect(retry.snapshot.captures).toHaveLength(1);\npackages/binding-flue/test/history-reader.test.ts:377: const snapshots = [\npackages/binding-flue/test/history-reader.test.ts:379: ...snapshot,\npackages/binding-flue/test/history-reader.test.ts:398: ...snapshot,\npackages/binding-flue/test/history-reader.test.ts:414: Response.json(snapshots.shift()!)) as unknown as typeof fetch;\npackages/core/src/capture-store.ts:277: readonly snapshot: CaptureStoreSnapshot;\npackages/core/src/capture-store.ts:296:// commands, persisted snapshots — reaches it through here, so all of them\npackages/core/src/capture-store.ts:417:const snapshotSchema = v.strictObject({\npackages/core/src/capture-store.ts:453: const snapshot = v.parse(snapshotSchema, input) as CaptureStoreSnapshot;\npackages/core/src/capture-store.ts:454: for (const records of [snapshot.captures, snapshot.issues, snapshot.events]) {\npackages/core/src/capture-store.ts:461: for (const capture of snapshot.captures) {\npackages/core/src/capture-store.ts:474: !snapshot.captures.some(\npackages/core/src/capture-store.ts:484: for (const event of snapshot.events) {\npackages/core/src/capture-store.ts:495: !snapshot.captures.some((capture) => capture.id === event.captureId)\npackages/core/src/capture-store.ts:503: const issue = snapshot.issues.find(\npackages/core/src/capture-store.ts:533: for (const issue of snapshot.issues) {\npackages/core/src/capture-store.ts:537: !snapshot.captures.some((capture) => capture.id === captureId),\npackages/core/src/capture-store.ts:552: for (const capture of snapshot.captures) {\npackages/core/src/capture-store.ts:555: for (const event of snapshot.events) {\npackages/core/src/capture-store.ts:564: for (const capture of snapshot.captures) {\npackages/core/src/capture-store.ts:569: snapshot.captures.some((candidate) => candidate.id === current)\npackages/core/src/capture-store.ts:580: const openConflicts = snapshot.issues.filter(\npackages/core/src/capture-store.ts:586: (captureId) => deriveCaptureStatus(snapshot, captureId) !== \"active\",\npackages/core/src/capture-store.ts:606: return snapshot;\npackages/core/src/capture-store.ts:710: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:717: return snapshot.captures\npackages/core/src/capture-store.ts:798: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:802: snapshot.events.some(\npackages/core/src/capture-store.ts:809: snapshot.captures.some((capture) => capture.supersedes === captureId) ||\npackages/core/src/capture-store.ts:810: snapshot.events.some(\npackages/core/src/capture-store.ts:822: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:825: snapshot.events.some(\npackages/core/src/capture-store.ts:842: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:845: snapshot.issues\npackages/core/src/capture-store.ts:850: deriveIssueStatus(snapshot, issue.id) === \"open\",\npackages/core/src/capture-store.ts:855: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:862: for (const capture of snapshot.captures) {\npackages/core/src/capture-store.ts:872: for (const event of snapshot.events) {\npackages/core/src/capture-store.ts:883: return snapshot.captures\npackages/core/src/capture-store.ts:888: deriveCaptureStatus(snapshot, capture.id) === \"active\",\npackages/core/src/capture-store.ts:912: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:925: const exactRetry = snapshot.captures.some(\npackages/core/src/capture-store.ts:935: snapshot.captures.some(\npackages/core/src/capture-store.ts:949: const target = snapshot.captures.find(\npackages/core/src/capture-store.ts:962: const blockingIssueIds = openConflictsNaming(snapshot, target.id);\npackages/core/src/capture-store.ts:972: deriveCaptureStatus(snapshot, target.id) !== \"active\" ||\npackages/core/src/capture-store.ts:979: currentHeadIds: currentHeads(snapshot, target.id),\npackages/core/src/capture-store.ts:987: const captures = [...snapshot.captures];\npackages/core/src/capture-store.ts:998: const nextSnapshot = { ...snapshot, captures };\npackages/core/src/capture-store.ts:1035: snapshot: nextSnapshot,\npackages/core/src/capture-store.ts:1041: snapshot: CaptureStoreSnapshot,\npackages/core/src/capture-store.ts:1067: snapshot,\npackages/core/src/capture-store.ts:1092: const result = applySweep(snapshot, proposals);\npackages/core/src/capture-store.ts:1122: !snapshot.captures.some((capture) => capture.id === captureId),\npackages/core/src/capture-store.ts:1130: // Activity is a fact about this snapshot, so it is checked here rather\npackages/core/src/capture-store.ts:1137: deriveCaptureStatus(snapshot, captureId) !== \"active\",\npackages/core/src/capture-store.ts:1143: message: `A new conflicting issue must reference active captures; capture ${inactiveReference} is ${deriveCaptureStatus(snapshot, inactiveReference)}.`,\npackages/core/src/capture-store.ts:1148: ? snapshot.issues.find(\npackages/core/src/capture-store.ts:1151: deriveIssueStatus(snapshot, issue.id) === \"open\" &&\npackages/core/src/capture-store.ts:1165: snapshot: { ...snapshot, issues: [...snapshot.issues, candidateIssue] },\npackages/core/src/capture-store.ts:1171: const issue = snapshot.issues.find(\npackages/core/src/capture-store.ts:1181: if (deriveIssueStatus(snapshot, issue.id) === \"closed\") {\npackages/core/src/capture-store.ts:1203: snapshot: { ...snapshot, events: [...snapshot.events, event] },\npackages/core/src/capture-store.ts:1209: const issue = snapshot.issues.find(\npackages/core/src/capture-store.ts:1245: // Cloned, not aliased: the snapshot is the store's record, and a caller\npackages/core/src/capture-store.ts:1257: deriveIssueStatus(snapshot, issue.id) === \"closed\" ||\npackages/core/src/capture-store.ts:1262: (captureId) => deriveCaptureStatus(snapshot, captureId) !== \"active\",\npackages/core/src/capture-store.ts:1274: snapshot: {\npackages/core/src/capture-store.ts:1275: ...snapshot,\npackages/core/src/capture-store.ts:1276: events: [...snapshot.events, candidateRecord],\npackages/core/src/capture-store.ts:1283: const capture = snapshot.captures.find(\npackages/core/src/capture-store.ts:1293: const blockingIssueIds = openConflictsNaming(snapshot, capture.id);\npackages/core/src/capture-store.ts:1331: deriveCaptureStatus(snapshot, capture.id) !== \"active\" ||\npackages/core/src/capture-store.ts:1344: snapshot: { ...snapshot, events: [...snapshot.events, event] },\npackages/core/test/anchoring.test.ts:63: const capture = result.snapshot.captures[0]!;\npackages/core/test/anchoring.test.ts:118: first.snapshot,\npackages/core/test/anchoring.test.ts:126: expect(replay.snapshot.captures).toHaveLength(1);\npackages/core/test/anchoring.test.ts:128: first.snapshot.captures[0]!.dedupKey,\npackages/core/test/anchoring.test.ts:173: first.snapshot,\npackages/core/test/anchoring.test.ts:181: expect(replay.snapshot.captures).toHaveLength(1);\npackages/core/test/anchoring.test.ts:183: replay.snapshot.captures.flatMap((capture) =>\npackages/core/test/anchoring.test.ts:194: replay.snapshot,\npackages/core/test/anchoring.test.ts:203: expect(bothOccurrences.snapshot.captures).toHaveLength(2);\npackages/core/test/anchoring.test.ts:205: bothOccurrences.snapshot.captures.flatMap((capture) =>\npackages/core/test/anchoring.test.ts:252: first.snapshot,\npackages/core/test/anchoring.test.ts:259: expect(retry.snapshot.captures).toHaveLength(1);\npackages/transport-aisdk/test/ask-reply.test.ts:25:const FIXTURES = join(import.meta.dirname, \"fixtures\");\npackages/core/test/ask-protocol.test.ts:18: \"What outcome should the scenario describe?\",\npackages/core/test/ask-protocol.test.ts:27: markdown: \"What outcome should the scenario describe?\",\npackages/core/test/ask-protocol.test.ts:28: payload: { question: \"What outcome should the scenario describe?\" },\npackages/core/test/ask-protocol.test.ts:60: \"What outcome should the scenario describe?\",\npackages/core/test/capture-store.test.ts:59: snapshot: CaptureStoreSnapshot,\npackages/core/test/capture-store.test.ts:62: applyCaptureStoreCommandWithArchive(snapshot, command, {\npackages/core/test/capture-store.test.ts:85: snapshot: CaptureStoreSnapshot,\npackages/core/test/capture-store.test.ts:88: const result = applyCaptureStoreCommand(snapshot, command);\npackages/core/test/capture-store.test.ts:93: // through the file it will be kept in, and come back the same snapshot. The\npackages/core/test/capture-store.test.ts:95: // value the command surface accepts and JSON cannot carry is a snapshot the\npackages/core/test/capture-store.test.ts:98: parseCaptureStoreSnapshot(JSON.parse(JSON.stringify(result.snapshot))),\npackages/core/test/capture-store.test.ts:99: ).toEqual(result.snapshot);\npackages/core/test/capture-store.test.ts:110: const retry = apply(first.snapshot, {\npackages/core/test/capture-store.test.ts:115: expect(first.snapshot.captures).toHaveLength(1);\npackages/core/test/capture-store.test.ts:116: expect(retry.snapshot.captures).toHaveLength(1);\npackages/core/test/capture-store.test.ts:119: skippedDedupKeys: [first.snapshot.captures[0]!.dedupKey],\npackages/core/test/capture-store.test.ts:123: const originalId = retry.snapshot.captures[0]!.id;\npackages/core/test/capture-store.test.ts:124: const revisedReading = apply(retry.snapshot, {\npackages/core/test/capture-store.test.ts:130: expect(revisedReading.snapshot.captures).toHaveLength(2);\npackages/core/test/capture-store.test.ts:131: expect(revisedReading.snapshot.captures[1]).toMatchObject({\npackages/core/test/capture-store.test.ts:132: dedupKey: first.snapshot.captures[0]!.dedupKey,\npackages/core/test/capture-store.test.ts:139: // JSON.stringify(-0) is \"0\", so accepting -0 mints a snapshot whose read\npackages/core/test/capture-store.test.ts:192: expect(result.snapshot.events).toEqual([]);\npackages/core/test/capture-store.test.ts:238: expect(result.snapshot.captures.map((capture) => capture.content)).toEqual(\npackages/core/test/capture-store.test.ts:242: result.snapshot.captures.every(\npackages/core/test/capture-store.test.ts:271: result.snapshot.captures.map((capture) => capture.epistemicStatus),\npackages/core/test/capture-store.test.ts:302: result.snapshot.captures.map((capture) => capture.epistemicStatus),\npackages/core/test/capture-store.test.ts:305: const userCitedDefault = applyCaptureStoreCommand(result.snapshot, {\npackages/core/test/capture-store.test.ts:325: const originalId = original.snapshot.captures[0]!.id;\npackages/core/test/capture-store.test.ts:326: const corrected = apply(original.snapshot, {\npackages/core/test/capture-store.test.ts:334: const correctionId = corrected.snapshot.captures[1]!.id;\npackages/core/test/capture-store.test.ts:336: expect(corrected.snapshot.captures).toHaveLength(2);\npackages/core/test/capture-store.test.ts:337: expect(deriveCaptureStatus(corrected.snapshot, originalId)).toBe(\npackages/core/test/capture-store.test.ts:340: expect(deriveCaptureStatus(corrected.snapshot, correctionId)).toBe(\npackages/core/test/capture-store.test.ts:344: corrected.snapshot.captures.every(\npackages/core/test/capture-store.test.ts:349: const stale = applyCaptureStoreCommand(corrected.snapshot, {\npackages/core/test/capture-store.test.ts:377: const [marchId, juneId] = captures.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:380: const issue = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:392: applyCaptureStoreCommand(issue.snapshot, {\npackages/core/test/capture-store.test.ts:398: applyCaptureStoreCommand(issue.snapshot, {\npackages/core/test/capture-store.test.ts:413: applyCaptureStoreCommand(issue.snapshot, {\npackages/core/test/capture-store.test.ts:428: const resolved = apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:437: expect(deriveIssueStatus(resolved.snapshot, issueId)).toBe(\"closed\");\npackages/core/test/capture-store.test.ts:438: expect(deriveCaptureStatus(resolved.snapshot, marchId!)).toBe(\"superseded\");\npackages/core/test/capture-store.test.ts:439: expect(deriveCaptureStatus(resolved.snapshot, juneId!)).toBe(\"active\");\npackages/core/test/capture-store.test.ts:440: expect(resolved.snapshot.issues[0]).not.toHaveProperty(\"status\");\npackages/core/test/capture-store.test.ts:452: const captureIds = captures.snapshot.captures.map((capture) => capture.id);\npackages/core/test/capture-store.test.ts:453: const issue = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:463: const partial = applyCaptureStoreCommand(issue.snapshot, {\npackages/core/test/capture-store.test.ts:487: // one reference this fixture is refused for referencing too few\npackages/core/test/capture-store.test.ts:499: test(\"persisted snapshots refuse more than one closing event for an issue\", () => {\npackages/core/test/capture-store.test.ts:503: }).snapshot;\npackages/core/test/capture-store.test.ts:511: }).snapshot;\npackages/core/test/capture-store.test.ts:515: }).snapshot;\npackages/core/test/capture-store.test.ts:533: test(\"persisted snapshots refuse stale keys and forking supersession graphs\", () => {\npackages/core/test/capture-store.test.ts:544: }).snapshot;\npackages/core/test/capture-store.test.ts:574: const captureId = created.snapshot.captures[0]!.id;\npackages/core/test/capture-store.test.ts:601: const result = applyCaptureStoreCommand(created.snapshot, {\npackages/core/test/capture-store.test.ts:614: expect(apply(created.snapshot, wellFormed).snapshot.issues).toHaveLength(1);\npackages/core/test/capture-store.test.ts:631: const [marchId, juneId, septemberId] = created.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:635: const corrected = apply(created.snapshot, {\npackages/core/test/capture-store.test.ts:643: const withRetraction = apply(corrected.snapshot, {\npackages/core/test/capture-store.test.ts:648: const aprilId = corrected.snapshot.captures.at(-1)!.id;\npackages/core/test/capture-store.test.ts:650: applyCaptureStoreCommand(withRetraction.snapshot, {\npackages/core/test/capture-store.test.ts:691: const issue = apply(withRetraction.snapshot, {\npackages/core/test/capture-store.test.ts:700: const resolved = apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:708: expect(deriveIssueStatus(resolved.snapshot, issue.value.issueId)).toBe(\npackages/core/test/capture-store.test.ts:714: const ambiguous = apply(withRetraction.snapshot, {\npackages/core/test/capture-store.test.ts:723: const closed = apply(ambiguous.snapshot, {\npackages/core/test/capture-store.test.ts:727: expect(deriveIssueStatus(closed.snapshot, ambiguous.value.issueId)).toBe(\npackages/core/test/capture-store.test.ts:743: captures.snapshot.captures.map((capture) => capture.id);\npackages/core/test/capture-store.test.ts:744: const first = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:757: const result = applyCaptureStoreCommand(first.snapshot, {\npackages/core/test/capture-store.test.ts:777: const disjoint = apply(first.snapshot, {\npackages/core/test/capture-store.test.ts:784: expect(disjoint.snapshot.issues).toHaveLength(2);\npackages/core/test/capture-store.test.ts:788: ...first.snapshot,\npackages/core/test/capture-store.test.ts:790: ...first.snapshot.issues,\npackages/core/test/capture-store.test.ts:802: // The command surface pins these captures, but a persisted snapshot could\npackages/core/test/capture-store.test.ts:808: ...first.snapshot,\npackages/core/test/capture-store.test.ts:810: ...first.snapshot.events,\npackages/core/test/capture-store.test.ts:833: const [marchId, juneId, venueId] = captures.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:836: const issue = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:887: const result = applyCaptureStoreCommand(issue.snapshot, command);\npackages/core/test/capture-store.test.ts:907: apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:911: }).snapshot.events,\npackages/core/test/capture-store.test.ts:916: const resolved = apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:924: expect(deriveCaptureStatus(resolved.snapshot, marchId!)).toBe(\"superseded\");\npackages/core/test/capture-store.test.ts:926: apply(resolved.snapshot, {\npackages/core/test/capture-store.test.ts:930: }).snapshot.events,\npackages/core/test/capture-store.test.ts:942: const [marchId, juneId] = created.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:945: const issue = apply(created.snapshot, {\npackages/core/test/capture-store.test.ts:951: }).snapshot;\npackages/core/test/capture-store.test.ts:974: const [marchId, juneId] = captures.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:977: const issue = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:986: const resolved = apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:993: }).snapshot;\npackages/core/test/capture-store.test.ts:1012: const [marchId, juneId] = created.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:1015: const issue = apply(created.snapshot, {\npackages/core/test/capture-store.test.ts:1027: const resolved = apply(issue.snapshot, {\npackages/core/test/capture-store.test.ts:1036: const retracted = apply(resolved.snapshot, {\npackages/core/test/capture-store.test.ts:1051: const resolution = retracted.snapshot.events.find(\npackages/core/test/capture-store.test.ts:1054: const retraction = retracted.snapshot.events.find(\npackages/core/test/capture-store.test.ts:1072: // And the snapshot the caller could still reach is one the parser accepts.\npackages/core/test/capture-store.test.ts:1073: expect(() => parseCaptureStoreSnapshot(retracted.snapshot)).not.toThrow();\npackages/core/test/capture-store.test.ts:1092: const [marchId, juneId, septemberId] = captures.snapshot.captures.map(\npackages/core/test/capture-store.test.ts:1095: const issue = apply(captures.snapshot, {\npackages/core/test/capture-store.test.ts:1138: const result = applyCaptureStoreCommand(issue.snapshot, command);\npackages/core/test/capture-store.test.ts:1147: test(\"persisted snapshots refuse a reversed evidence range in a capture or an event\", () => {\npackages/core/test/capture-store.test.ts:1152: const retracted = apply(created.snapshot, {\npackages/core/test/capture-store.test.ts:1154: captureId: created.snapshot.captures[0]!.id,\npackages/core/test/capture-store.test.ts:1156: }).snapshot;\npackages/core/test/capture-store.test.ts:1158: // Bent from a snapshot the store itself produced, so the reversed range is\npackages/core/test/capture-store.test.ts:1194: let snapshot = apply(createEmptyCaptureStoreSnapshot(), {\npackages/core/test/capture-store.test.ts:1210: }).snapshot;\npackages/core/test/capture-store.test.ts:1212: const [marchId, juneId, venueId] = snapshot.captures.map(\npackages/core/test/capture-store.test.ts:1217: // and a snapshot with a supersession link in it.\npackages/core/test/capture-store.test.ts:1218: snapshot = apply(snapshot, {\npackages/core/test/capture-store.test.ts:1226: }).snapshot;\npackages/core/test/capture-store.test.ts:1228: const ambiguous = apply(snapshot, {\npackages/core/test/capture-store.test.ts:1238: snapshot = apply(ambiguous.snapshot, {\npackages/core/test/capture-store.test.ts:1241: }).snapshot;\npackages/core/test/capture-store.test.ts:1244: const conflict = apply(snapshot, {\npackages/core/test/capture-store.test.ts:1253: snapshot = apply(conflict.snapshot, {\npackages/core/test/capture-store.test.ts:1260: }).snapshot;\npackages/core/test/capture-store.test.ts:1263: snapshot = apply(snapshot, {\npackages/core/test/capture-store.test.ts:1267: }).snapshot;\npackages/core/test/capture-store.test.ts:1276: parseCaptureStoreSnapshot(JSON.parse(JSON.stringify(snapshot))),\npackages/core/test/capture-store.test.ts:1277: ).toEqual(snapshot);\npackages/core/test/capture-store.test.ts:1279: captures: snapshot.captures.length,\npackages/core/test/capture-store.test.ts:1280: issues: snapshot.issues.length,\npackages/core/test/capture-store.test.ts:1281: events: snapshot.events.length,\npackages/core/test/capture-store.test.ts:1290: const captureId = created.snapshot.captures[0]!.id;\npackages/core/test/capture-store.test.ts:1292: applyCaptureStoreCommand(created.snapshot, {\npackages/core/test/capture-store.test.ts:1304: const retracted = apply(created.snapshot, {\npackages/core/test/capture-store.test.ts:1310: expect(deriveCaptureStatus(retracted.snapshot, captureId)).toBe(\npackages/core/test/capture-store.test.ts:1313: expect(retracted.snapshot.captures[0]).not.toHaveProperty(\"status\");\npackages/core/test/capture-store.test.ts:1314: expect(retracted.snapshot.events.at(-1)).toMatchObject({\n", - "exitCode": 0 -} -``` - -## Assistant - -We should stop asking one scenario to serve every proof role: - -```text -Evaluation corpus -├─ Vestera Coatings -│ └─ cold-start elicitation quality benchmark -├─ Industrial gas: PN → SPN → SCPN → DCPN → SDCPN -│ └─ semantic/projection complexity ladder -├─ Yannis’s reference nets -│ └─ reviewed projection targets and shared team vocabulary -└─ Confirmed September case - └─ live product narrative and optimisation handoff -``` - -Vestera remains useful until another case has both a net **and** an interviewable source-of-truth pack. - -The baseline reset should compare three conditions on the same model/version and situation pack: - -1. Bare Claude. -2. Best current flat elicitation prompt. -3. Brunch cold-start runbook. - -Never overwrite prior runs. Version prompts/protocols and preserve immutable run records. Brunch must beat the prompt baseline on computed coverage, provenance, uncertainty/conflict preservation, stopping, and projectable output—not merely conversational quality. - -For fixture organization, I suggest vocabulary and placement by role: - -```text -evaluations/ -├─ cases/ reusable domain corpus -│ ├─ vestera-coatings/ -│ └─ industrial-gas/ -├─ protocols/ versioned comparison procedures and prompts -└─ oracles/ reviewed semantic expectations - -packages/*/test/fixtures/ exact package-contract inputs -apps/*/test/fixtures/ application/integration inputs - -docs/planning/<effort>/ -└─ evaluations/<dated-run>/ - └─ immutable outputs, legibility rendering, strain, witness, verdict -``` - -Definitions: - -- **Case/corpus:** reusable domain truth. -- **Fixture:** exact input owned by one test boundary. -- **Run snapshot:** observed evidence, never overwritten. -- **Golden/oracle:** a reviewed claim distilled from a snapshot—not necessarily the entire output. - -This avoids brittle “whole LLM transcript equals golden” tests while allowing witnessed findings to graduate into regression oracles. - -One crucial question: **do Dora and Yannis’s materials include enough narrative/expert facts to establish expected requirements, or only the resulting nets?** If only nets, they are immediately useful as projection-complexity fixtures, but not yet as cold-start elicitation benchmarks. - -## User - -I like that topology and agree in every part except `docs/planning/<effort>/` -- I worry that 'effort' is now a competing ordering principle to our STEERING.md doc - -For use-case descriptions and PN / SDCPN models thereof, this is all still in motion and hasn't settled; some were already mentioned in @docs/reference/2026-08 SDCPNs for cyber-physical systems.md and there are some JSON net representations of those I can give you, but the rest is in flux. Claude in Slack made me the following summary--you won't be able to open the links, but I can: - ---- - -There are two competing use-case tracks, and the one you were given in the brunch:left_right_arrow:Petrinaut sync has since been overridden — that's probably the thing that matters most to you. - -Track A — the blog-post nets (these exist) -Seven nets, all as Petrinaut JSON in the blog post Drive folder, built by Dora for the SDCPN generalisation blog post: -• gases-1-spn → gases-5-sdcpn — one industrial gas supply chain modelled five times, climbing the formalism ladder SPN → CPN → DCPN → SDCPN-with-diffusion. This is the "you can translate any model of the world into an SDCPN" argument ARIA asked for.. -• truck-fleet-predictive-maintenance and semiconductor-fab-drift — the same formalism applied to two other domains.. - -Review state, from the blog thread: Yannis ran a maths-conformance pass and found the coloured nets lacked stochastic firing, net 2's DEs were fake (clocks dressed as differential equations), and the semiconductor/truck DEs were just constant assignments. Dora fixed all of that. The one open item is net 5 — Petrinaut only integrates ODEs today, so diffusion is faked by a kernel every 0.5h; Chris says real SDE support is small (expose diffusion alongside drift, x: [1, 0.5]) and that Coherence decided in Bristol they need SDEs after all, so it's worth doing rather than working around. - -Track B — biopharma (where it's heading) -Dei pushed back on all three: the demo should match the problem curriculum/ladder dataset, on the grounds that live-presenting a domain we actually know is safer. No nets exist for this yet. Candidates: -• Supply allocation — from the problem curriculum doc; optimise the allocation decision logic as a stand-in for an AI controller. Ciaran flags it as probably quite complicated.. -• Inventory purchasing — no doc yet; optimise order quantity and rate, trading overstock cost (quantity × time) against production delay.. -• Nora's vaccine supply chain from Zulip — batches in QC, expiry as waste, two products one refrigerated. Dei doesn't like "clinics order at random" but does want the surrounding artefacts (batch-release SOP, order-history CSV, planner persona) built either way, as backtest material for extraction and process mining.. - -The conflict to resolve -Point 7 of the brunch↔Petrinaut agreement fixed the use case as truck fleet or semiconductor, and your Sept MVP deliverable (elicit SDCPN in Petrinaut, generate net, view IR) is scoped against that. Dei's biopharma steer lands after it and isn't reconciled anywhere. Dora's vote deadline is tomorrow, Tue 25th, and it's on the TA1.3 sync agenda for the second half. If biopharma wins, the demo needs a net that doesn't exist yet, which is a different shape of work for you than pointing brunch at an existing one. - -Yannis -He isn't building nets. His output is the formalism and the curriculum scaffolding: -• Difficulty ladder L0–L5 + grading dimensions in the Final Exam doc — this is the main thing. He finished editing it and posted for collective review on Zulip (#58 Problem Curriculum Design → Semantic Ladder), asking for feedback by EOD Wed 26 Aug. Tracked as SOL-89, under SOL-88.. -• Instance schema — he handed this to Dora: port the blog-post supply chain nets and split them per ladder rung, each with a list of state specifications ("are there any raw material tokens whose temperature fell below 80°C?"). SOL-90, blocked by SOL-89.. -• Maths documentation — a Reference/formalism page on hash.dev/docs/petrinaut pulling from internal-docs/petri-nets, two-way linked to the glossary: FE-1323, H-6662, glossary content in FE-1458 and FE-1430.. - -His net review lives only as messages in that blog thread, not as a document, so if you want his read on model quality that thread is the source. - -## Assistant - -This clarifies the roles—and corrects my earlier attribution: Yannis supplies the **ladder, grading dimensions, and instance schema**, not the reference nets. - -```text -Frontier 0 — make the claim decidable -├─ Use-case state gate -│ ├─ Track A: existing blog-post nets -│ │ ├─ gas formalism ladder -│ │ ├─ truck fleet -│ │ └─ semiconductor fab -│ └─ Track B: biopharma -│ ├─ domain artifacts partly available -│ ├─ no reference net -│ └─ substantially more preparatory work if selected -│ -├─ Evaluation reference surfaces -│ ├─ Vestera → cold-start elicitation benchmark -│ ├─ Blog JSON → projection/conformance corpus -│ ├─ SOL-89 → complexity ladder and grading protocol -│ └─ SOL-90 → case-instance/state-specification schema -│ -├─ Demonstration proof -│ ├─ bounded change with visible consequence -│ ├─ provenance back to evidence -│ └─ witnessed legibility artifact -│ -└─ Missing mappings - ├─ baseline-v2 protocol and rerun - ├─ ingestion/versioning of Dora’s seven nets - ├─ Yannis review/conformance snapshot - ├─ biopharma case + reference net if selected - ├─ SDE support dependency if gas level 5 matters - └─ optimisation handoff contract -``` - -The tracks imply different work: - -- **Track A:** Brunch can target reviewed, existing nets. The risk is elicitation/projection quality. -- **Track B:** the team must first establish domain truth, a reference model, state specifications, and optimisation intent. That preparation joins the critical path. -- Regardless of the vote, the blog nets remain valuable as a formalism-complexity corpus. - -I would begin the baseline reset independently: - -```text -B0 bare current Claude -B1 strongest current flat prompt -T1 Brunch build-from-scratch runbook -``` - -Version the protocol, prompt, model, and runs. Re-run B0 and B1 soon; add T1 when executable. - -On documentation topology: agreed. **Effort should become metadata, not the filesystem’s organizing authority.** A better target is: - -```text -docs/ -├─ control/ STEERING, COORDINATION, active ledgers -├─ specs/ current obligation-bearing contracts -├─ adr/ settled decisions -├─ evidence/ -│ ├─ proofs/<claim>/ -│ └─ evaluations/<protocol>/<run-id>/ -├─ reference/ settled source material -└─ archive/ superseded records -``` - -Executable evaluation material remains outside docs: - -```text -evaluations/ -├─ cases/ -├─ protocols/ -└─ oracles/ -``` - -STEERING links objectives/frontiers to immutable evidence; evidence is organized by the claim or protocol it supports, not by whichever “effort” happened to produce it. - -Please send the seven JSON nets when convenient. They are useful regardless of tomorrow’s use-case decision. A Slack export of Yannis’s review would also let us preserve its conformance findings as a proper evidence snapshot. - -## User - -The JSON nets are now in @docs/inbox/SDCPNs - -## Assistant - -I’ll inventory the nets as candidate corpus material—identities, formalism level, schema shape, and obvious completeness/conformance signals—without yet declaring them settled reference fixtures. Because they are now being used, I’ll also register their inbox status under the documentation protocol. - -**Tool Use:** `shell_command` - -```json -{ - "command": "find docs/inbox/SDCPNs -maxdepth 2 -type f -print | sort; printf '%s\\n' '--- sizes'; find docs/inbox/SDCPNs -maxdepth 2 -type f -exec wc -c {} + | sort -n", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git status --short -- docs/inbox/SDCPNs docs/INDEX.md; printf '%s\\n' '--- all status'; git status --short", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "find docs/inbox/SDCPNs -maxdepth 2 -type f -name '*.json' -print0 | while IFS= read -r -d '' f; do printf '\\n=== %s ===\\n' \"$f\"; jq -r '\"type=\" + (type), (if type == \"object\" then \"keys=\" + (keys|join(\",\")) else \"length=\" + (length|tostring) end)' \"$f\" 2>&1; done", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgbqCwlvLobV0Oupi7B` - -``` -{ - "output": "docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\ndocs/inbox/SDCPNs/gases-1-pn-layout.json\ndocs/inbox/SDCPNs/gases-2-spn-layout.json\ndocs/inbox/SDCPNs/gases-3-cpn-layout.json\ndocs/inbox/SDCPNs/gases-4-dcpn-layout.json\ndocs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\ndocs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n--- sizes\n 13774 docs/inbox/SDCPNs/gases-1-pn-layout.json\n 14262 docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\n 30679 docs/inbox/SDCPNs/gases-2-spn-layout.json\n 41070 docs/inbox/SDCPNs/gases-3-cpn-layout.json\n 64493 docs/inbox/SDCPNs/gases-4-dcpn-layout.json\n 97303 docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n 104228 docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\n 365809 total\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgbqDv4jy6S1gTtHUhg` - -``` -{ - "output": "?? docs/inbox/SDCPNs/\n--- all status\n M docs/agents/git-workflow.md\n M docs/agents/issue-writing.md\n M docs/agents/legibility.md\n M docs/planning/_shared/STEERING.md\n M docs/planning/_shared/hash-monorepo-import-plan.md\n?? docs/inbox/SDCPNs/\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgbqF5bHRM4uNxTX6DR` - -``` -{ - "output": "\n=== docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/gases-1-pn-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/gases-2-spn-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/gases-4-dcpn-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/gases-3-cpn-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n\n=== docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json ===\ntype=object\nkeys=componentInstances,differentialEquations,meta,metrics,parameters,places,scenarios,subnets,title,transitions,types,version\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "for f in docs/inbox/SDCPNs/*.json; do jq -r --arg f \"$f\" '[ $f, (.title // \"\"), (.version|tostring), (.places|length|tostring), (.transitions|length|tostring), (.types|length|tostring), (.differentialEquations|length|tostring), (.parameters|length|tostring), (.scenarios|length|tostring), (.metrics|length|tostring), (.subnets|length|tostring), (.componentInstances|length|tostring) ] | @tsv' \"$f\"; done | (printf 'file\\ttitle\\tversion\\tplaces\\ttransitions\\ttypes\\tDEs\\tparameters\\tscenarios\\tmetrics\\tsubnets\\tcomponents\\n'; cat)", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "for f in docs/inbox/SDCPNs/*.json; do printf '\\n=== %s ===\\n' \"$(basename \"$f\")\"; jq '{meta, typeNames: [.types[].name], differentialEquationNames: [.differentialEquations[].name], scenarioNames: [.scenarios[].name], metricNames: [.metrics[].name]}' \"$f\"; done", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n \"parseSDCPNFile|SDCPNFile\" ../../../libs/@hashintel/petrinaut-core ../../../libs/@hashintel/petrinaut --glob '*.ts' | head -120; jq -r '.name,.scripts' ../../../libs/@hashintel/petrinaut-core/package.json", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n 'SDCPNs|blog-post nets|gases-[1-5]|truck-fleet|semiconductor' docs/INDEX.md docs --glob '*.md' | head -120", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cgc9LrXSI6LVPzHZrft` - -``` -{ - "output": "file\ttitle\tversion\tplaces\ttransitions\ttypes\tDEs\tparameters\tscenarios\tmetrics\tsubnets\tcomponents\ndocs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\tGases 1 — plain net, one customer (consumption trigger)\t1\t14\t8\t0\t0\t1\t2\t7\t0\t0\ndocs/inbox/SDCPNs/gases-1-pn-layout.json\tGases 1 — plain net, one customer\t1\t13\t8\t0\t0\t1\t2\t7\t0\t0\ndocs/inbox/SDCPNs/gases-2-spn-layout.json\tGases 2 — stochastic net, two customers on one tanker\t1\t25\t17\t0\t0\t9\t4\t12\t0\t0\ndocs/inbox/SDCPNs/gases-3-cpn-layout.json\tGases 3 — coloured net, three customers and a mixed fleet\t1\t36\t25\t1\t0\t9\t4\t11\t0\t0\ndocs/inbox/SDCPNs/gases-4-dcpn-layout.json\tGases 4 — dynamic coloured net\t1\t31\t26\t3\t3\t23\t5\t16\t0\t0\ndocs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\tSemiconductor fab — process drift & multi-chamber tools (v2)\t1\t19\t25\t2\t5\t36\t4\t12\t0\t0\ndocs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\tTruck fleet with condition-based maintenance (v2)\t1\t26\t26\t3\t7\t40\t11\t19\t0\t0\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cgc9MXtB3TqEqHe24AQ` - -``` -{ - "output": "\n=== gases-1-pn-consumption-trigger-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [],\n \"differentialEquationNames\": [],\n \"scenarioNames\": [\n \"Customer drawing normally\",\n \"Customer shut, tank still evaporating\"\n ],\n \"metricNames\": [\n \"Loads delivered\",\n \"Stockouts\",\n \"Vented through relief\",\n \"Evaporated\",\n \"Consumed\",\n \"Contents plus ullage\",\n \"Stranded customers\"\n ]\n}\n\n=== gases-1-pn-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [],\n \"differentialEquationNames\": [],\n \"scenarioNames\": [\n \"Customer drawing normally\",\n \"Customer shut, tank still evaporating\"\n ],\n \"metricNames\": [\n \"Loads delivered\",\n \"Stockouts\",\n \"Vented through relief\",\n \"Evaporated\",\n \"Consumed\",\n \"Contents plus ullage\",\n \"Stranded customers\"\n ]\n}\n\n=== gases-2-spn-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [],\n \"differentialEquationNames\": [],\n \"scenarioNames\": [\n \"Customers drawing normally\",\n \"Customers shut, tanks still evaporating\",\n \"A second tanker on the depot\",\n \"Routes half again as long\"\n ],\n \"metricNames\": [\n \"Loads delivered\",\n \"Stockouts\",\n \"Criticality-weighted stockouts\",\n \"Vented through relief\",\n \"Evaporated\",\n \"Consumed\",\n \"Share of outflow lost to boil-off\",\n \"Stockouts per 100 units consumed\",\n \"SteadyNitrogen stockouts\",\n \"SlowNitrogen stockouts\",\n \"Contents plus ullage\",\n \"Stranded customers\"\n ]\n}\n\n=== gases-3-cpn-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [\n \"Tanker\"\n ],\n \"differentialEquationNames\": [],\n \"scenarioNames\": [\n \"Three tankers, normal routes\",\n \"Three tankers, routes half again as long\",\n \"Two tankers, normal routes\",\n \"Three tankers, two of them oxygen\"\n ],\n \"metricNames\": [\n \"Loads delivered\",\n \"Stockouts\",\n \"Criticality-weighted stockouts\",\n \"Vented through relief\",\n \"Evaporated\",\n \"Consumed\",\n \"SteadyNitrogen stockouts\",\n \"SlowNitrogen stockouts\",\n \"CriticalOxygen stockouts\",\n \"Contents plus ullage\",\n \"Stranded customers\"\n ]\n}\n\n=== gases-4-dcpn-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [\n \"Tanker\",\n \"Tank\",\n \"Plant\"\n ],\n \"differentialEquationNames\": [\n \"Tank\",\n \"Journey clock (on route)\",\n \"Journey clock (returning)\"\n ],\n \"scenarioNames\": [\n \"Three tankers, normal routes\",\n \"Three tankers, routes half again as long\",\n \"Two tankers, normal routes\",\n \"SlowNitrogen throttled back\",\n \"No spot hire\"\n ],\n \"metricNames\": [\n \"Loads delivered\",\n \"Stockouts\",\n \"Criticality-weighted stockouts\",\n \"Vented through relief\",\n \"Evaporated\",\n \"Surplus lost on delivery\",\n \"Consumed\",\n \"Stockouts per 100 units delivered to customers\",\n \"SteadyNitrogen stockouts\",\n \"SlowNitrogen stockouts\",\n \"CriticalOxygen stockouts\",\n \"Level in tanks\",\n \"Relief valve openings\",\n \"Pressure at SlowNitrogen\",\n \"Tankers hired in\",\n \"Plant outages\"\n ]\n}\n\n=== semiconductor-fab-drift-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [\n \"Lot\",\n \"Chamber\"\n ],\n \"differentialEquationNames\": [\n \"Lot urgency escalation (+ age, wait clocks)\",\n \"Clock: process countdown (+ age)\",\n \"Chamber wear and contamination (coupled)\",\n \"Clock: maintenance countdown\",\n \"Lot urgency escalation in batch queue (+ age, wait, batch clocks)\"\n ],\n \"scenarioNames\": [\n \"Normal operation\",\n \"High process drift\",\n \"Frequent calibration\",\n \"Reduced maintenance crew\"\n ],\n \"metricNames\": [\n \"Throughput\",\n \"Yield\",\n \"Average cycle time (hours)\",\n \"On-time delivery rate\",\n \"Average process bias\",\n \"Maximum chamber drift\",\n \"Chamber utilisation\",\n \"Maintenance events\",\n \"Unplanned breakdowns\",\n \"Calibrations\",\n \"WIP level\",\n \"Batch queue size\"\n ]\n}\n\n=== truck-fleet-predictive-maintenance-layout.json ===\n{\n \"meta\": {\n \"generator\": \"Petrinaut\"\n },\n \"typeNames\": [\n \"Truck\",\n \"Load\",\n \"Conditions\"\n ],\n \"differentialEquationNames\": [\n \"Wear, fuel and road conditions (loaded)\",\n \"Wear, fuel and road conditions (running back empty)\",\n \"Clock: standing at the depot (age only)\",\n \"Clock: service countdown\",\n \"Clock: driver rest countdown\",\n \"Clock: load ageing on the board\",\n \"Regional weather drift (+ resample clock)\"\n ],\n \"scenarioNames\": [\n \"Run to failure\",\n \"Condition-based servicing\",\n \"Servicing too early\",\n \"Condition-based, worn trucks off mountain work\",\n \"Condition-based, one bay\",\n \"Condition-based, one part on the shelf\",\n \"Winter conditions\",\n \"Summer baseline\",\n \"Demand surge\",\n \"Winter + demand surge\",\n \"Route-aware dispatch + winter\"\n ],\n \"metricNames\": [\n \"Loads delivered on time\",\n \"Loads delivered late\",\n \"Loads dropped\",\n \"Service level\",\n \"Revenue\",\n \"Total fuel burned\",\n \"Operating cost\",\n \"Profit\",\n \"Roadside failures\",\n \"Planned services\",\n \"Unplanned repairs\",\n \"Services deferred\",\n \"Driver rest events\",\n \"Trucks earning\",\n \"Trucks off the road\",\n \"Fleet utilisation\",\n \"Average brake wear\",\n \"Average engine wear\",\n \"Average tyre wear\"\n ]\n}\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cgc9NKHsMYcpDroBIpm` - -``` -{ - "output": "../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.ts:123:export const parseSDCPNFile = (data: unknown): ImportResult => {\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:3:import { parseSDCPNFile } from \"./parse-sdcpn-file\";\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:33:describe(\"parseSDCPNFile\", () => {\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:36: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:51: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:71: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:94: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:112: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:160: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:226: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:245: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:269: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:284: const result = parseSDCPNFile(minimalSDCPN);\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:293: const result = parseSDCPNFile(minimalSDCPN);\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:305: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:316: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:327: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:360: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:372: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:389: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:407: const result = parseSDCPNFile(minimalSDCPN);\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:419: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:431: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:445: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:457: const result = parseSDCPNFile(null);\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:462: const result = parseSDCPNFile(\"not a json object\");\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:467: const result = parseSDCPNFile({});\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:472: const result = parseSDCPNFile({\n../../../libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts:484: const result = parseSDCPNFile({ title: \"Test\" });\n../../../libs/@hashintel/petrinaut-core/src/index.ts:440: parseSDCPNFile,\n../../../libs/@hashintel/petrinaut-core/src/optimization.ts:3:import { parseSDCPNFile } from \"./file-format/parse-sdcpn-file\";\n../../../libs/@hashintel/petrinaut-core/src/optimization.ts:160: const parsed = parseSDCPNFile({ ...model.definition, title: model.title });\n../../../libs/@hashintel/petrinaut/src/ui/file-io/import-sdcpn.ts:1:import { type ImportResult, parseSDCPNFile } from \"@hashintel/petrinaut-core\";\n../../../libs/@hashintel/petrinaut/src/ui/file-io/import-sdcpn.ts:5: * {@link parseSDCPNFile} (which lives in `/core/file-format/`). Returns null\n../../../libs/@hashintel/petrinaut/src/ui/file-io/import-sdcpn.ts:29: resolve(parseSDCPNFile(loadedData));\n../../../libs/@hashintel/petrinaut-core/src/lsp/lib/file-paths.ts:6:export type SDCPNFileType =\n../../../libs/@hashintel/petrinaut-core/src/lsp/lib/file-paths.ts:48:export const getItemFilePath = <T extends SDCPNFileType>(\n@hashintel/petrinaut-core\n{\n \"build\": \"vite build\",\n \"fix:eslint\": \"oxlint --fix --type-aware --report-unused-disable-directives-severity=error .\",\n \"lint:eslint\": \"oxlint --type-aware --report-unused-disable-directives-severity=error .\",\n \"lint:tsc\": \"tsgo --noEmit\",\n \"prepack\": \"node ../../../scripts/check-package-payload.mjs\",\n \"prepublishOnly\": \"turbo run build\",\n \"test:unit\": \"vitest\"\n}\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034Cgc9PlRwgOvjfoEcmFY` - -``` -{ - "output": "docs/INDEX.md:24:| [2026-08 SDCPNs for cyber-physical systems](reference/2026-08%20SDCPNs%20for%20cyber-physical%20systems.md) | settled | 2026-08 (settled 2026-08-18) | Unattributed draft blog post (image placeholders, typos): five-level SDCPN explainer applied to gas supply, truck fleet, semiconductor fab; arrived during the FE-1405 arc. Read skeptically: good pedagogy, promotional register — concedes its formal guarantees don't apply once continuous/stochastic features are used (open research problem), models carry heavy kernel/guard logic that strains the \"formal and inspectable\" claim, and Petrinaut's integrator limitation is admitted | Register-3 background (projection-target expressivity) only; not elicitation design input; no consumer yet |\ndocs/INDEX.md:24:| [2026-08 SDCPNs for cyber-physical systems](reference/2026-08%20SDCPNs%20for%20cyber-physical%20systems.md) | settled | 2026-08 (settled 2026-08-18) | Unattributed draft blog post (image placeholders, typos): five-level SDCPN explainer applied to gas supply, truck fleet, semiconductor fab; arrived during the FE-1405 arc. Read skeptically: good pedagogy, promotional register — concedes its formal guarantees don't apply once continuous/stochastic features are used (open research problem), models carry heavy kernel/guard logic that strains the \"formal and inspectable\" claim, and Petrinaut's integrator limitation is admitted | Register-3 background (projection-target expressivity) only; not elicitation design input; no consumer yet |\ndocs/reference/SDCPN Library - Ideas.md:86:This example is particularly effective because it combines physics (component wear and degradation), real-time sensing, predictive analytics, and operational decision-making. The cyber system must continuously estimate each vehicle's health, predict future failures, and coordinate maintenance without unnecessarily disrupting fleet operations. It showcases SDCPNs' ability to model systems where continuous degradation, stochastic failures, resource allocation, and logistics interact over long time horizons.\ndocs/reference/SDCPN Library - Ideas.md:158:This is a particularly compelling SDCPN example because it couples human behaviour, communication infrastructure, continuous traffic dynamics, and discrete control decisions. Unlike many CPS examples where the physical dynamics arise from machines or vehicles, here the driving force is the stochastic movement and behaviour of large populations, making it an excellent demonstration that SDCPNs can model systems where cyber decisions adapt continuously to changing human activity. It also illustrates an important optimisation objective that is easy for a non-technical audience to grasp: _providing enough network capacity where and when people need it, while minimizing the energy consumed by thousands of cell towers._\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:1:# SDCPNs for cyber-physical systems\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:3:_One formalism for continuous dynamics, stochastic events and typed state, demonstrated across industrial gas supply, truck fleet maintenance, and semiconductor fabrication._\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:27:The four features together allow SDCPNs to represent all aspects of a cyber-physical system in the same state and clock: its physical process (a tank emptying, a machine wearing), control logic (when to dispatch, when to service), and the randomness that affects both (a breakdown, a demand spike).\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:29:## Where SDCPNs come from\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:35:# Modelling real world with SDCPNs\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:37:The following sections model an industrial supply chain process progressively, adding one feature of the formalism at each level until the model is a full SDCPN. To illustrate the expressivity of SDCPNs, we model two further domains as full SDCPNs: truck fleet maintenance and semiconductor fabrication.\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:111:Introducing noise and randomness to the dynamics in SDCPNs enables us to answer questions on the system’s varying environment. One limitation of this model is specific to the current Petrinaut engine: its integrator handles only deterministic ODEs, so diffusion is injected discretely via a kernel every 0.5 simulated hours rather than continuously. The approximation works, but its quality depends on the step size the model builder chooses rather than improving automatically as the engine's time step shrinks.\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:115:SDCPNs can be used for truck fleet operators to solve their maintenance problem, in deciding when and where they should service each vehicle. The service must occur early enough to prevent a breakdown on the road, and late enough not to waste maintenance capacity. The maintenance schedule must ensure deliveries are still completed within the agreed window.\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:135:A semiconductor foundry processes batches of wafers (wafer lots) through 28 steps using shared machines. The same machine group handles multiple steps in the sequence, for example the same lithography group is visited at layers 0, 4, 9, 12, 16, 20, and 24, so wafer lots at different stages compete for the same machines.\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:163:## Why SDCPNs?\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:185:- **Rare-event probabilities can be quantified.** Some failures are too rare to observe in ordinary Monte Carlo, estimating a probability of 10⁻⁹ would need billions of runs. SDCPNs support acceleration methods ([importance sampling](https://doi.org/10.1109/acc.2011.5991305), [interacting particle systems](https://doi.org/10.1201/9781420008548.ch10)) that exploit the net's structure (strong Markov property) to estimate these probabilities efficiently.\ndocs/reference/2026-08 SDCPNs for cyber-physical systems.md:189:This post applied SDCPNs to model three domains: industrial gas supply, truck fleet maintenance and semiconductor fabrication. We explored what each feature from the formalism adds, where by:\ndocs/planning/legibility-sweep/issue-pr-migration-2026-08-20/review/linear-editorial-review.md:33:- **FE-1363 — STATUS_DRIFT:** Resolved. The outer identifies truck-fleet predictive maintenance as the engineering recommendation pending team agreement, cold chain as the runner-up, and production scheduling as the baseline.\ndocs/planning/process-model-elicitation/ir-design-plain.md:31:Layer B is a working design, validated only against the truck-fleet reference case. The worked-examples exercise left it unchanged, except that it exported source-regime up to Layer A. The harness still gets its turn.\ndocs/planning/process-model-elicitation/notes/grilling-inputs-2026-08-12.md:85: technical criterion; candidates truck-fleet / cold-chain / scheduling.\ndocs/planning/process-model-elicitation/research/petrinaut-survey.md:191:> (SDCPNs) in Petrinaut.\ndocs/planning/process-model-elicitation/ir-design.md:86:A working design, validated only against the truck-fleet reference case (FE-1363). The\ndocs/planning/legibility-sweep/issue-pr-migration-2026-08-20/review/linear-FE-1357.md:13:This issue is the planning map for that demo: each decision that must be made before serious building starts is a sub-issue below, and the sections after the divider index what's decided and what's still open. Status: the groundwork research is done, and three big decisions have landed — the demo will be a purpose-built demo app that uses a new interviewing library and Petrinaut's existing libraries side by side, passing a model file between them, rather than building the interviewer into Petrinaut itself (2026-08-12); the demo's reference use case is truck-fleet predictive maintenance (recommended to the team, ratification expected ~18 August), with the interviewee played from a prepared briefing rather than requiring a live domain expert (2026-08-12); and the form interview knowledge is stored in is settled (2026-08-13) — the store is the set of recorded, source-traceable statements the expert made, and the runnable diagram is one view generated from it, so everything an expert says that has no place in a diagram (reasons, policies, unwritten rules) is kept rather than lost. Next: pressure-test that storage definition against more plugin kinds, then write the plugin spec once the team ratifies the use case. Planning documents live in the brunch-lite repo under `docs/planning/process-model-elicitation/`.\ndocs/planning/legibility-sweep/issue-pr-migration-2026-08-20/review/linear-FE-1357.md:145:**Title:** Source dossier: published fleet-maintenance models + operational data for the truck-fleet case → **Compile the truck-fleet source dossier**\ndocs/planning/legibility-sweep/issue-pr-migration-2026-08-20/review/linear-FE-1357.md:153:Find published fleet-maintenance models, public operational data, and practitioner evidence sufficient to author the truck-fleet situation pack, reference net, and answer key without a live expert. Record licenses and granularity, and pay particular attention to models of per-vehicle degradation. The dossier resolves the sourcing part of FE-1363; situation-pack authoring waits for team feedback on PRO-99.\ndocs/planning/process-model-elicitation/notes/research-patterns-audit.md:45:**The VUT earning test, and colour as a folding.** Two results that turn \"which formalism features does this system need?\" from taste into tests. The Kingman/VUT relation from queueing theory says queue time scales with variability times utilization times service time — so stochastic modelling is _earned_ when utilization is high or variability is large, and genuinely unnecessary when both are low and the question concerns a mean. Conversely, coloured tokens add no expressive power at all: a coloured net with finite colour sets unfolds mechanically into a plain net, so colour is compactness and maintainability — an engineering convenience, not a capability a system can \"need.\" Together these retired the effort's earlier \"formalism ladder\" framing (which ranked features by prestige) in favor of per-capability evidence tests, and they ground the showcase criterion that chose the truck-fleet case: the one thing that genuinely earns dynamic per-entity state is a continuous quantity gating a threshold-crossing question. Also derived from VUT: the \"too-good agreement\" checklist — when the simulation matches the spreadsheet, check utilization first; agreement is suspicious only when utilization is high.\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "jq '{name,exports,type}' libs/@hashintel/petrinaut-core/package.json; test -d libs/@hashintel/petrinaut-core/dist && find libs/@hashintel/petrinaut-core/dist -maxdepth 1 -type f | head", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '110,175p' libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.ts; sed -n '430,448p' libs/@hashintel/petrinaut-core/src/index.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "rg -n '\"tsx\"|\"ts-node\"|\"vite-node\"' package.json yarn.lock | head -40", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgcPj1P0Nf5yAwTvEvq` - -``` -{ - "output": "{\n \"name\": \"@hashintel/petrinaut-core\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./ai\": {\n \"types\": \"./dist/ai.d.d.ts\",\n \"import\": \"./dist/ai.js\"\n },\n \"./compiled-model\": {\n \"types\": \"./dist/compiled-model.d.d.ts\",\n \"import\": \"./dist/compiled-model.js\"\n },\n \"./examples\": {\n \"types\": \"./dist/examples/index.d.d.ts\",\n \"import\": \"./dist/examples/index.js\"\n },\n \"./hir\": {\n \"types\": \"./dist/hir.d.d.ts\",\n \"import\": \"./dist/hir.js\"\n },\n \"./hir-runtime\": {\n \"types\": \"./dist/hir-runtime.d.d.ts\",\n \"import\": \"./dist/hir-runtime.js\"\n },\n \"./optimization\": {\n \"types\": \"./dist/optimization.d.d.ts\",\n \"import\": \"./dist/optimization.js\"\n },\n \"./workers/lsp\": {\n \"types\": \"./dist/workers/lsp.d.d.ts\",\n \"import\": \"./dist/workers/lsp.js\"\n },\n \"./workers/monte-carlo\": {\n \"types\": \"./dist/workers/monte-carlo.d.d.ts\",\n \"import\": \"./dist/workers/monte-carlo.js\"\n },\n \"./workers/simulation\": {\n \"types\": \"./dist/workers/simulation.d.d.ts\",\n \"import\": \"./dist/workers/simulation.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"type\": \"module\"\n}\nlibs/@hashintel/petrinaut-core/dist/language-server.worker-CdgJLTC7.js.map\nlibs/@hashintel/petrinaut-core/dist/hir-C8Dn6nC5.js\nlibs/@hashintel/petrinaut-core/dist/examples-CAEchurz.js\nlibs/@hashintel/petrinaut-core/dist/ai.js\nlibs/@hashintel/petrinaut-core/dist/extensions-DPGqwj9E.js.map\nlibs/@hashintel/petrinaut-core/dist/instantiate-C0T6Bd80.js.map\nlibs/@hashintel/petrinaut-core/dist/protocol-Cu-9ylyx.d.ts\nlibs/@hashintel/petrinaut-core/dist/environment-Cf4KfzoD.d.ts\nlibs/@hashintel/petrinaut-core/dist/hir-metric-Bt36EA1z.js\nlibs/@hashintel/petrinaut-core/dist/language-server.worker-CdgJLTC7.js\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgcPjnmPc7HtfdIbzKv` - -``` -{ - "output": " componentInstances: (subnet.componentInstances ?? []).map((instance) => ({\n ...instance,\n x: instance.x ?? 0,\n y: instance.y ?? 0,\n })),\n })),\n }) as SDCPNWithTitle;\n\n/**\n * Parses raw JSON data into an SDCPN, handling versioned, legacy, and old\n * pre-2025-11-28 formats. Pure — no DOM, no I/O. Callers (e.g. the `/ui`\n * file-picker wrapper) are responsible for sourcing the data.\n */\nexport const parseSDCPNFile = (data: unknown): ImportResult => {\n // Try the versioned format first\n const versioned = sdcpnFileSchema.safeParse(data);\n if (versioned.success) {\n const { version: _version, meta: _meta, ...sdcpnData } = versioned.data;\n return {\n ok: true,\n sdcpn: fillMissingVisualInfo(sdcpnData),\n hadMissingPositions: hasMissingPositions(sdcpnData),\n };\n }\n\n // If the data has a `version` field but failed the versioned schema, reject it\n // rather than falling through to the legacy path (which would silently accept\n // future-versioned files by stripping the unknown `version` key).\n if (typeof data === \"object\" && data !== null && \"version\" in data) {\n const version = (data as { version: unknown }).version;\n if (\n typeof version === \"number\" &&\n version >= 1 &&\n version <= SDCPN_FILE_FORMAT_VERSION\n ) {\n // Supported version but invalid structure — show actual Zod errors\n return {\n ok: false,\n error: `Invalid SDCPN file: ${versioned.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\", \")}`,\n };\n }\n return {\n ok: false,\n error: \"Unsupported SDCPN file format version\",\n };\n }\n\n // Fall back to legacy format (current schema without version/meta)\n const legacy = legacySdcpnFileSchema.safeParse(data);\n if (legacy.success) {\n return {\n ok: true,\n sdcpn: fillMissingVisualInfo(legacy.data),\n hadMissingPositions: hasMissingPositions(legacy.data),\n };\n }\n\n return {\n ok: false,\n error: `Invalid SDCPN file: ${legacy.error.issues.map((i) => i.message).join(\", \")}`,\n };\n};\n} from \"./simulation/engine/uuid\";\nexport {\n displayNameSchema,\n validateDisplayName,\n} from \"./validation/display-name\";\nexport { entityNameSchema, validateEntityName } from \"./validation/entity-name\";\nexport { validateVariableName } from \"./validation/variable-name\";\n\n// --- File, clipboard, and editor protocol helpers ---\nexport {\n parseSDCPNFile,\n type ImportResult,\n} from \"./file-format/parse-sdcpn-file\";\nexport { serializeSDCPN } from \"./file-format/serialize-sdcpn\";\nexport { sdcpnToTikZ } from \"./file-format/sdcpn-to-tikz\";\nexport { pastePayloadIntoSDCPN } from \"./clipboard/paste\";\nexport {\n parseClipboardPayload,\n serializeSelection,\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgcPkHtPRy0FIzS5vN9` - -``` -{ - "output": "", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "node --input-type=module <<'NODE'\nimport { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseSDCPNFile } from './libs/@hashintel/petrinaut-core/dist/index.js';\n\nconst dir = './libs/@hashintel/brunch-agent/docs/inbox/SDCPNs';\nfor (const name of (await readdir(dir)).filter((name) => name.endsWith('.json')).sort()) {\n const parsed = parseSDCPNFile(JSON.parse(await readFile(join(dir, name), 'utf8')));\n console.log(`${name}\\t${parsed.ok ? `ok missingPositions=${parsed.hadMissingPositions}` : parsed.error}`);\n}\nNODE", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgccodshdIhsnzs7aAc` - -``` -{ - "output": "gases-1-pn-consumption-trigger-layout.json\tok missingPositions=false\ngases-1-pn-layout.json\tok missingPositions=false\ngases-2-spn-layout.json\tok missingPositions=false\ngases-3-cpn-layout.json\tok missingPositions=false\ngases-4-dcpn-layout.json\tok missingPositions=false\nsemiconductor-fab-drift-layout.json\tok missingPositions=false\ntruck-fleet-predictive-maintenance-layout.json\tok missingPositions=false\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,32p' docs/INDEX.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgcogQ9fxZVVNNpsgUC` - -``` -{ - "output": "# Document index\n\nOne line per document: what it is, where it lives, where it's used. Protocol:\n[`docs/agents/documentation.md`](agents/documentation.md). Statuses: `inbox` (awaiting\nsettlement) · `active` (artifact of a live effort) · `settled` (permanent home) · `superseded`\n(retained history replaced by newer canon) · `accepted` (ratified ADR) · `external` (canonical\ncopy lives outside the repo).\n\n## Inbox (awaiting settlement)\n\n_(empty — items settle out via the arc-close inbox sweep)_\n\n## Reference (settled sources)\n\n| Document | Status | Date | Digest | Used by |\n| ----------------------------------------------------------------------------------------------------------- | ------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| [agentic-elicitation-challenges](reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md) | settled | 2026-08-06 | Turn 1 of the founding analysis: four contracts, packs, IR; source of the \"capture meaning before representation\" principle | elicitation-kernel spec §1 |\n| [agentic-elicitation-criteria](reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md) | settled | 2026-08-06 | Turn 2: hourglass, five proof obligations, ten invariants, smells, test matrix | elicitation-kernel spec §14 |\n| [SDCPN Library - Ideas](reference/SDCPN%20Library%20-%20Ideas.md) | settled | 2026-08-11 | Eight CPS use-case sketches (physical/cyber/events/continuous-state/emergence anatomy), ChatGPT-drafted | FE-1357 map; FE-1363 candidates |\n| [hash-sails-public-report.pdf](reference/hash-sails-public-report.pdf) | settled | 2026-08-11 (pub. 2026-01) | SAILS/ARIA public report: Safeguarded AI gatekeeper (world model + safety spec + verifier), biopharma supply-chain research, tacit knowledge as adoption barrier | FE-1357 map (the \"why\"); FE-1363 cold-chain anchor |\n| [voice-implementation-recommendation-pplx](reference/voice-implementation-recommendation-pplx.md) | settled | 2026-08-11 | Perplexity research: voice-adapter options (ElevenLabs/OpenAI/Gemini/xAI) | FE-1359 (superseded in part by its findings) |\n| [yannis-dora-lu-transcript](reference/yannis-dora-lu-transcript-2026-08-11.md) | settled | 2026-08-11 | Meeting transcript: no in-house interviewing practice; SDCPN-as-hypothesis aired; baseline-control and priming ideas | expert-meeting-findings note; FE-1360, FE-1361 |\n| [amp-analysis-flue-vs-tilde](reference/amp-analysis-flue-vs-tilde.md) | settled | 2026-08-14 | Amp thread export: comparative assessment of the Flue and tilde agent frameworks (development and deployment stories) and its import for this project; verdict: keep Flue, Tilde is a control plane not a runtime | reconciled into flue-architecture-cheatsheet (2026-08-17); source of the pre-remote-exposure gates |\n| [2026-08 SDCPNs for cyber-physical systems](reference/2026-08%20SDCPNs%20for%20cyber-physical%20systems.md) | settled | 2026-08 (settled 2026-08-18) | Unattributed draft blog post (image placeholders, typos): five-level SDCPN explainer applied to gas supply, truck fleet, semiconductor fab; arrived during the FE-1405 arc. Read skeptically: good pedagogy, promotional register — concedes its formal guarantees don't apply once continuous/stochastic features are used (open research problem), models carry heavy kernel/guard logic that strains the \"formal and inspectable\" claim, and Petrinaut's integrator limitation is admitted | Register-3 background (projection-target expressivity) only; not elicitation design input; no consumer yet |\n\n## history/planning/elicitation-kernel (effort complete 2026-08-10; settled 2026-08-12)\n\n| Document | Status | Linear | Digest |\n| -------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [spec.md](spec.md) | settled | linked from FE-1366 (context-canonical) | The elicitation-kernel spec: 14 sections + adjudications; FE-1437 import amendment records the native HASH package family, context root, and remote-server application charter |\n| [product-description.md](history/planning/elicitation-kernel/product-description.md) | settled | none | STE-style product description |\n| [product-description-plain.md](history/planning/elicitation-kernel/product-description-plain.md) | settled | none | Plain-prose rendering of the product description |\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `apply_patch` - -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n@@\n ## Inbox (awaiting settlement)\n \n-_(empty — items settle out via the arc-close inbox sweep)_\n+| Document | Status | Date | Digest | Used by |\n+| ---------------------------- | ------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |\n+| [SDCPN JSON nets](inbox/SDCPNs/) | inbox | 2026-08-24 | Seven valid Petrinaut v1 JSON files: two plain-net gas variants, gas SPN/CPN/DCPN models, truck-fleet maintenance, and semiconductor-fab drift; the expected fifth gas SDCPN is not present in this arrival | Frontier 0 corpus inventory; settlement/conformance pending |\n \n ## Reference (settled sources)\n*** End Patch" -} -``` - -## User - -**Tool Result:** `TU-034Cgcx2uvAOhQiG3FWDO2` - -``` -{ - "files": [ - { - "uri": "file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/docs/INDEX.md", - "diff": "Index: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n===================================================================\n--- /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n+++ /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md\n@@ -7,9 +7,11 @@\n copy lives outside the repo).\n \n ## Inbox (awaiting settlement)\n \n-_(empty — items settle out via the arc-close inbox sweep)_\n+| Document | Status | Date | Digest | Used by |\n+| ---------------------------- | ------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |\n+| [SDCPN JSON nets](inbox/SDCPNs/) | inbox | 2026-08-24 | Seven valid Petrinaut v1 JSON files: two plain-net gas variants, gas SPN/CPN/DCPN models, truck-fleet maintenance, and semiconductor-fab drift; the expected fifth gas SDCPN is not present in this arrival | Frontier 0 corpus inventory; settlement/conformance pending |\n \n ## Reference (settled sources)\n \n | Document | Status | Date | Digest | Used by |\n", - "type": "update", - "additions": 3, - "deletions": 1 - } - ], - "summary": "update: /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/INDEX.md (+3/-1)" -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "sed -n '1,19p' docs/INDEX.md", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cgd6qnurG4IQRfLcIgu` - -``` -{ - "output": "# Document index\n\nOne line per document: what it is, where it lives, where it's used. Protocol:\n[`docs/agents/documentation.md`](agents/documentation.md). Statuses: `inbox` (awaiting\nsettlement) · `active` (artifact of a live effort) · `settled` (permanent home) · `superseded`\n(retained history replaced by newer canon) · `accepted` (ratified ADR) · `external` (canonical\ncopy lives outside the repo).\n\n## Inbox (awaiting settlement)\n\n| Document | Status | Date | Digest | Used by |\n| ---------------------------- | ------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |\n| [SDCPN JSON nets](inbox/SDCPNs/) | inbox | 2026-08-24 | Seven valid Petrinaut v1 JSON files: two plain-net gas variants, gas SPN/CPN/DCPN models, truck-fleet maintenance, and semiconductor-fab drift; the expected fifth gas SDCPN is not present in this arrival | Frontier 0 corpus inventory; settlement/conformance pending |\n\n## Reference (settled sources)\n\n| Document | Status | Date | Digest | Used by |\n| ----------------------------------------------------------------------------------------------------------- | ------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| [agentic-elicitation-challenges](reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md) | settled | 2026-08-06 | Turn 1 of the founding analysis: four contracts, packs, IR; source of the \"capture meaning before representation\" principle | elicitation-kernel spec §1 |\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "for f in docs/inbox/SDCPNs/gases-4-dcpn-layout.json docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json; do printf '\\n=== %s DE shape ===\\n' \"$(basename \"$f\")\"; jq '[.differentialEquations[] | {name, keys:(keys), code:(.code // .expression // .equation // .derivatives // .values // null)}]' \"$f\"; done", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "for f in docs/inbox/SDCPNs/*.json; do printf '\\n=== %s transition timing ===\\n' \"$(basename \"$f\")\"; jq -r '[.transitions[] | (.kind // .type // .timing.type // .firing.type // \"unspecified\")] | group_by(.) | map({kind:.[0], count:length})' \"$f\"; done", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "jq '.differentialEquations[0]' docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json; jq '.transitions[0]' docs/inbox/SDCPNs/gases-2-spn-layout.json", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgdTFhTetiMBJyiSwGI` - -``` -{ - "output": "\n=== gases-4-dcpn-layout.json DE shape ===\n[\n {\n \"name\": \"Tank\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"// The rung the whole domain is built for. Level falls from the customer's draw\\n// and from boil-off together, and stops at empty so Euler cannot drive it\\n// negative. Pressure rises as boil-off gas fills whatever ullage is left, so a\\n// nearly full tank pressurises fastest, and falls as liquid is drawn off. Empty\\n// stops the customer's line and full opens the relief valve, so the safe region\\n// is an interval and \\\"hold more stock\\\" is not a safe default.\\nexport default Dynamics((tokens, parameters) => {\\n return tokens.map((unit) => ({\\n level: unit.level > 0 ? -(unit.draw + parameters.boiloff_rate) : 0, pressure: Math.max(parameters.pressure_gain * parameters.boiloff_rate / Math.max(unit.capacity - unit.level, 1) - parameters.pressure_vented_by_draw * unit.draw, unit.pressure > 1 ? -1 : 0), capacity: 0, draw: 0, drawn: unit.level > 0 ? unit.draw : 0, boiled: unit.level > 0 ? parameters.boiloff_rate : 0, spilled: 0\\n }));\\n});\"\n },\n {\n \"name\": \"Journey clock (on route)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ remaining: -1, payload: 0 }));\\n});\"\n },\n {\n \"name\": \"Journey clock (returning)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ remaining: -1, payload: 0 }));\\n});\"\n }\n]\n\n=== truck-fleet-predictive-maintenance-layout.json DE shape ===\n[\n {\n \"name\": \"Wear, fuel and road conditions (loaded)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((truck) => {\\n const speed = parameters.average_speed * truck.speed_factor;\\n const severity = truck.road_severity;\\n const brakeRoute = truck.route_class === 2 ? 2.5 : truck.route_class === 1 ? 1.4 : 1.0;\\n const tyreRoute = truck.route_class === 2 ? 1.6 : 1.0;\\n return {\\n brake_wear: parameters.brake_wear_per_km * speed * severity * brakeRoute\\n * (1 + parameters.wear_feedback * truck.brake_wear),\\n engine_wear: parameters.engine_wear_per_km * speed * severity * 1.2\\n * (1 + parameters.wear_feedback * truck.engine_wear),\\n tyre_wear: parameters.tyre_wear_per_km * speed * severity * tyreRoute\\n * (1 + parameters.wear_feedback * truck.tyre_wear),\\n km_remaining: -speed,\\n route_distance: 0, service_remaining: 0, load_due: 0, load_revenue: 0, age: 1,\\n road_severity: parameters.severity_reversion\\n * (parameters.base_severity_mean - truck.road_severity),\\n speed_factor: parameters.speed_reversion\\n * (parameters.base_speed_mean - truck.speed_factor),\\n conditions_clock: -1,\\n fuel_burned: parameters.fuel_per_km * speed * severity\\n * (truck.route_class === 2 ? 1.4 : 1.0),\\n hours_driven: 1, rest_remaining: 0, fuel_rate: 0\\n };\\n });\\n});\"\n },\n {\n \"name\": \"Wear, fuel and road conditions (running back empty)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((truck) => {\\n const speed = parameters.average_speed * truck.speed_factor;\\n const severity = truck.road_severity;\\n const brakeRoute = truck.route_class === 2 ? 2.5 : truck.route_class === 1 ? 1.4 : 1.0;\\n const tyreRoute = truck.route_class === 2 ? 1.6 : 1.0;\\n return {\\n brake_wear: parameters.brake_wear_per_km * speed * severity * brakeRoute * 0.7\\n * (1 + parameters.wear_feedback * truck.brake_wear),\\n engine_wear: parameters.engine_wear_per_km * speed * severity * 0.7\\n * (1 + parameters.wear_feedback * truck.engine_wear),\\n tyre_wear: parameters.tyre_wear_per_km * speed * severity * tyreRoute * 0.7\\n * (1 + parameters.wear_feedback * truck.tyre_wear),\\n km_remaining: -speed,\\n route_distance: 0, service_remaining: 0, load_due: 0, load_revenue: 0, age: 1,\\n road_severity: parameters.severity_reversion\\n * (parameters.base_severity_mean - truck.road_severity),\\n speed_factor: parameters.speed_reversion\\n * (parameters.base_speed_mean - truck.speed_factor),\\n conditions_clock: -1,\\n fuel_burned: parameters.fuel_per_km * speed * severity\\n * (truck.route_class === 2 ? 1.4 : 1.0) * 0.8,\\n hours_driven: 1, rest_remaining: 0, fuel_rate: 0\\n };\\n });\\n});\"\n },\n {\n \"name\": \"Clock: standing at the depot (age only)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ brake_wear: 0, engine_wear: 0, tyre_wear: 0, km_remaining: 0, route_distance: 0, service_remaining: 0, load_due: 0, load_revenue: 0, age: 1, road_severity: 0, speed_factor: 0, conditions_clock: 0, fuel_burned: 0, hours_driven: 0, rest_remaining: 0, fuel_rate: 0 }));\\n});\"\n },\n {\n \"name\": \"Clock: service countdown\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ brake_wear: 0, engine_wear: 0, tyre_wear: 0, km_remaining: 0, route_distance: 0, service_remaining: -1, load_due: 0, load_revenue: 0, age: 1, road_severity: 0, speed_factor: 0, conditions_clock: 0, fuel_burned: 0, hours_driven: 0, rest_remaining: 0, fuel_rate: 0 }));\\n});\"\n },\n {\n \"name\": \"Clock: driver rest countdown\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ brake_wear: 0, engine_wear: 0, tyre_wear: 0, km_remaining: 0, route_distance: 0, service_remaining: 0, load_due: 0, load_revenue: 0, age: 1, road_severity: 0, speed_factor: 0, conditions_clock: 0, fuel_burned: 0, hours_driven: 0, rest_remaining: -1, fuel_rate: 0 }));\\n});\"\n },\n {\n \"name\": \"Clock: load ageing on the board\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({ distance: 0, due: 0, revenue: 0, age: 1 }));\\n});\"\n },\n {\n \"name\": \"Regional weather drift (+ resample clock)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((cond) => ({\\n severity_mean: parameters.env_reversion\\n * (parameters.base_severity_mean - cond.severity_mean),\\n speed_mean: parameters.env_reversion\\n * (parameters.base_speed_mean - cond.speed_mean),\\n clock: -1\\n }));\\n});\"\n }\n]\n\n=== semiconductor-fab-drift-layout.json DE shape ===\n[\n {\n \"name\": \"Lot urgency escalation (+ age, wait clocks)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((lot) => ({\\n priority: lot.priority < parameters.max_priority\\n ? (lot.priority * lot.priority) / parameters.target_cycle_time\\n : 0,\\n age: 1, defect_count: 0, process_remaining: 0, wait_time: 1, due_date: 0, process_bias: 0, batch_wait: 0\\n }));\\n});\"\n },\n {\n \"name\": \"Clock: process countdown (+ age)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({\\n priority: 0, age: 1, defect_count: 0, process_remaining: -1, wait_time: 0, due_date: 0, process_bias: 0, batch_wait: 0\\n }));\\n});\"\n },\n {\n \"name\": \"Chamber wear and contamination (coupled)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((chamber) => {\\n const particleTarget = parameters.particle_baseline\\n + parameters.particle_drift * chamber.hours_since_maintenance\\n + parameters.particle_condition_factor * chamber.condition;\\n return {\\n condition: parameters.degradation_rate\\n * (1 + chamber.particle_count / parameters.particle_threshold),\\n particle_count: parameters.particle_reversion\\n * (particleTarget - chamber.particle_count),\\n hours_since_maintenance: 1,\\n maintenance_remaining: 0,\\n diffusion_clock: -1,\\n process_drift: -parameters.drift_reversion * chamber.process_drift\\n };\\n });\\n});\"\n },\n {\n \"name\": \"Clock: maintenance countdown\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens) => {\\n return tokens.map(() => ({\\n condition: 0, particle_count: 0, hours_since_maintenance: 0, maintenance_remaining: -1, diffusion_clock: 0, process_drift: 0\\n }));\\n});\"\n },\n {\n \"name\": \"Lot urgency escalation in batch queue (+ age, wait, batch clocks)\",\n \"keys\": [\n \"code\",\n \"colorId\",\n \"id\",\n \"name\"\n ],\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((lot) => ({\\n priority: lot.priority < parameters.max_priority\\n ? (lot.priority * lot.priority) / parameters.target_cycle_time\\n : 0,\\n age: 1, defect_count: 0, process_remaining: 0, wait_time: 1, due_date: 0, process_bias: 0, batch_wait: 1\\n }));\\n});\"\n }\n]\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgdTG5ZvD1F2PAOx8HG` - -``` -{ - "output": "\n=== gases-1-pn-consumption-trigger-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 8\n }\n]\n\n=== gases-1-pn-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 8\n }\n]\n\n=== gases-2-spn-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 17\n }\n]\n\n=== gases-3-cpn-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 25\n }\n]\n\n=== gases-4-dcpn-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 26\n }\n]\n\n=== semiconductor-fab-drift-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 25\n }\n]\n\n=== truck-fleet-predictive-maintenance-layout.json transition timing ===\n[\n {\n \"kind\": \"unspecified\",\n \"count\": 26\n }\n]\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgdTGZwPMRDgyDcEVnf` - -``` -{ - "output": "{\n \"id\": \"de__driving\",\n \"name\": \"Wear, fuel and road conditions (loaded)\",\n \"colorId\": \"type__truck\",\n \"code\": \"export default Dynamics((tokens, parameters) => {\\n return tokens.map((truck) => {\\n const speed = parameters.average_speed * truck.speed_factor;\\n const severity = truck.road_severity;\\n const brakeRoute = truck.route_class === 2 ? 2.5 : truck.route_class === 1 ? 1.4 : 1.0;\\n const tyreRoute = truck.route_class === 2 ? 1.6 : 1.0;\\n return {\\n brake_wear: parameters.brake_wear_per_km * speed * severity * brakeRoute\\n * (1 + parameters.wear_feedback * truck.brake_wear),\\n engine_wear: parameters.engine_wear_per_km * speed * severity * 1.2\\n * (1 + parameters.wear_feedback * truck.engine_wear),\\n tyre_wear: parameters.tyre_wear_per_km * speed * severity * tyreRoute\\n * (1 + parameters.wear_feedback * truck.tyre_wear),\\n km_remaining: -speed,\\n route_distance: 0, service_remaining: 0, load_due: 0, load_revenue: 0, age: 1,\\n road_severity: parameters.severity_reversion\\n * (parameters.base_severity_mean - truck.road_severity),\\n speed_factor: parameters.speed_reversion\\n * (parameters.base_speed_mean - truck.speed_factor),\\n conditions_clock: -1,\\n fuel_burned: parameters.fuel_per_km * speed * severity\\n * (truck.route_class === 2 ? 1.4 : 1.0),\\n hours_driven: 1, rest_remaining: 0, fuel_rate: 0\\n };\\n });\\n});\"\n}\n{\n \"id\": \"transition__s1_draw\",\n \"name\": \"Draw a unit (SteadyNitrogen)\",\n \"inputArcs\": [\n {\n \"placeId\": \"place__s1_contents\",\n \"weight\": 1,\n \"type\": \"standard\"\n }\n ],\n \"outputArcs\": [\n {\n \"placeId\": \"place__s1_consumed\",\n \"weight\": 1\n },\n {\n \"placeId\": \"place__s1_ullage\",\n \"weight\": 1\n }\n ],\n \"lambdaType\": \"stochastic\",\n \"lambdaCode\": \"export default Lambda((input, parameters) => {\\n return Math.max(parameters.draw_1 * parameters.draw_enabled, 1e-9);\\n});\",\n \"transitionKernelCode\": \"\",\n \"x\": 990,\n \"y\": 1410\n}\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "for f in docs/inbox/SDCPNs/*.json; do printf '%s\\t' \"$(basename \"$f\")\"; jq -c '[.transitions[].lambdaType // \"missing\"] | group_by(.) | map({type:.[0], count:length})' \"$f\"; done", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgdcSXkqLeZqZWKW3Cq` - -``` -{ - "output": "gases-1-pn-consumption-trigger-layout.json\t[{\"type\":\"predicate\",\"count\":8}]\ngases-1-pn-layout.json\t[{\"type\":\"predicate\",\"count\":8}]\ngases-2-spn-layout.json\t[{\"type\":\"stochastic\",\"count\":17}]\ngases-3-cpn-layout.json\t[{\"type\":\"stochastic\",\"count\":25}]\ngases-4-dcpn-layout.json\t[{\"type\":\"predicate\",\"count\":16},{\"type\":\"stochastic\",\"count\":10}]\nsemiconductor-fab-drift-layout.json\t[{\"type\":\"predicate\",\"count\":20},{\"type\":\"stochastic\",\"count\":5}]\ntruck-fleet-predictive-maintenance-layout.json\t[{\"type\":\"predicate\",\"count\":19},{\"type\":\"stochastic\",\"count\":7}]\n", - "exitCode": 0 -} -``` - -## Assistant - -The arrival contains seven valid Petrinaut v1 files, but not the seven described in the Slack summary: it has two plain-net gas variants plus gas SPN/CPN/DCPN, truck, and semiconductor. The expected fifth gas SDCPN/diffusion model is absent. I’m preserving that discrepancy in the inbox record rather than normalizing it away. - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --check -- docs/INDEX.md && for file in docs/inbox/SDCPNs/*.json; do jq empty \"$file\"; done && node --input-type=module <<'NODE'\nimport { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseSDCPNFile } from '../../../petrinaut-core/dist/index.js';\n\nconst directory = 'docs/inbox/SDCPNs';\nfor (const name of (await readdir(directory)).filter((entry) => entry.endsWith('.json')).sort()) {\n const result = parseSDCPNFile(JSON.parse(await readFile(join(directory, name), 'utf8')));\n if (!result.ok) throw new Error(`${name}: ${result.error}`);\n if (result.hadMissingPositions) throw new Error(`${name}: missing positions`);\n}\nconsole.log('7 SDCPN files parse with complete positions');\nNODE", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgdtN8TtCkx3VGokkSN` - -``` -{ - "output": "node:internal/modules/esm/resolve:271\n throw new ERR_MODULE_NOT_FOUND(\n ^\n\nError [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/lunelson/Code/hashintel/hash/petrinaut-core/dist/index.js' imported from /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/[eval1]\n at finalizeResolution (node:internal/modules/esm/resolve:271:11)\n at moduleResolve (node:internal/modules/esm/resolve:865:10)\n at defaultResolve (node:internal/modules/esm/resolve:992:11)\n at #cachedDefaultResolve (node:internal/modules/esm/loader:701:20)\n at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:721:38)\n at ModuleLoader.resolveSync (node:internal/modules/esm/loader:759:56)\n at #resolve (node:internal/modules/esm/loader:683:17)\n at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:603:35)\n at ModuleJob.syncLink (node:internal/modules/esm/module_job:163:33)\n at ModuleJob.link (node:internal/modules/esm/module_job:253:17) {\n code: 'ERR_MODULE_NOT_FOUND',\n url: 'file:///Users/lunelson/Code/hashintel/hash/petrinaut-core/dist/index.js'\n}\n\nNode.js v24.19.0\n", - "exitCode": 1 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --check -- docs/INDEX.md && for file in docs/inbox/SDCPNs/*.json; do jq empty \"$file\"; done && node --input-type=module <<'NODE'\nimport { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseSDCPNFile } from '../petrinaut-core/dist/index.js';\n\nconst directory = 'docs/inbox/SDCPNs';\nfor (const name of (await readdir(directory)).filter((entry) => entry.endsWith('.json')).sort()) {\n const result = parseSDCPNFile(JSON.parse(await readFile(join(directory, name), 'utf8')));\n if (!result.ok) throw new Error(`${name}: ${result.error}`);\n if (result.hadMissingPositions) throw new Error(`${name}: missing positions`);\n}\nconsole.log('7 SDCPN files parse with complete positions');\nNODE", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034Cge5FVEeKVZdpfeM1RL` - -``` -{ - "output": "7 SDCPN files parse with complete positions\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run lint:tsc --filter '@hashintel/brunch-agent' && turbo run test:unit --filter '@hashintel/brunch-agent' && turbo run build --filter '@hashintel/brunch-agent'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CgeGF5En6w5h4BCeCf6` - -``` -{ - "output": "turbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running lint:tsc in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:lint:tsc: cache hit, replaying logs 50584d5be80cc7ec\n\n Tasks: 1 successful, 1 total\nCached: 1 cached, 1 total\n Time: 3.983s >>> FULL TURBO\n\nturbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running test:unit in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:test:unit: cache hit, replaying logs d4c5393b6af87011\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: RUN v4.1.10 /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ✓ test/architecture/boundaries.test.ts (29 tests) 33ms\n@hashintel/brunch-agent:test:unit: ✓ test/naming.test.ts (11 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/capture-store.test.ts (24 tests) 21ms\n@hashintel/brunch-agent:test:unit: ✓ test/anchoring.test.ts (6 tests) 4ms\n@hashintel/brunch-agent:test:unit: ✓ test/session-log.test.ts (4 tests) 8ms\n@hashintel/brunch-agent:test:unit: ✓ test/sweep-protocol.test.ts (9 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/workspace.test.ts (7 tests) 3ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/linear-project-graph.test.ts (2 tests) 14ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/docs-index.test.ts (7 tests) 12ms\n@hashintel/brunch-agent:test:unit: ✓ test/ask-protocol.test.ts (9 tests) 2ms\n@hashintel/brunch-agent:test:unit: stderr | test/architecture/open-gaps.test.ts\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ⚠ 3 verification gaps are open (spec §14.5 and friends):\n@hashintel/brunch-agent:test:unit: · restart-durability — FE-1396 (spec §14.5)\n@hashintel/brunch-agent:test:unit: · compaction-vs-durable-history — FE-1386 (spec §9.7, §14.5)\n@hashintel/brunch-agent:test:unit: · interpretation-render-plugin-seam — FE-1394 (spec §7.6, §14.5)\n@hashintel/brunch-agent:test:unit: Closing one means deleting its entry in the commit that lands its proof.\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: ✓ test/architecture/open-gaps.test.ts (2 tests) 1ms\n@hashintel/brunch-agent:test:unit: ✓ test/architecture/baseline-runner.test.ts (4 tests) 1867ms\n@hashintel/brunch-agent:test:unit: ✓ checkpoints a truncated expert reply and stops before another interviewer call 756ms\n@hashintel/brunch-agent:test:unit: ✓ resume regenerates a trailing truncated expert reply before continuing 636ms\n@hashintel/brunch-agent:test:unit: ✓ continues a truncated final delivery without sending checkpoint metadata 311ms\n@hashintel/brunch-agent:test:unit: \n@hashintel/brunch-agent:test:unit: Test Files 12 passed (12)\n@hashintel/brunch-agent:test:unit: Tests 114 passed (114)\n@hashintel/brunch-agent:test:unit: Start at 12:31:31\n@hashintel/brunch-agent:test:unit: Duration 2.08s (transform 76ms, setup 0ms, import 155ms, tests 1.97s, environment 0ms)\n@hashintel/brunch-agent:test:unit: \n\n Tasks: 1 successful, 1 total\nCached: 1 cached, 1 total\n Time: 1.527s >>> FULL TURBO\n\nturbo 2.6.3\n\n• Packages in scope: @hashintel/brunch-agent\n• Running build in 1 packages\n• Remote caching disabled\n@hashintel/brunch-agent:build: cache bypass, force executing d9f7ac2bc077ccba\n@hashintel/brunch-agent:build: vite v8.1.0 building client environment for production...\n@hashintel/brunch-agent:build: \u001b[2K\r@hashintel/brunch-agent:build: transforming...✓ 13 modules transformed.\n@hashintel/brunch-agent:build: rendering chunks...\n@hashintel/brunch-agent:build: computing gzip size...\n@hashintel/brunch-agent:build: dist/client-tools.js 0.12 kB │ gzip: 0.12 kB\n@hashintel/brunch-agent:build: dist/storage.js 0.20 kB │ gzip: 0.15 kB\n@hashintel/brunch-agent:build: dist/plugin-BlyisbcK.js 0.68 kB │ gzip: 0.41 kB │ map: 3.09 kB\n@hashintel/brunch-agent:build: dist/testing/index.js 0.69 kB │ gzip: 0.41 kB │ map: 2.13 kB\n@hashintel/brunch-agent:build: dist/naming-DhqH8ada.js 0.78 kB │ gzip: 0.48 kB │ map: 4.29 kB\n@hashintel/brunch-agent:build: dist/session-log-1mbM2HlS.js 6.44 kB │ gzip: 2.22 kB │ map: 20.44 kB\n@hashintel/brunch-agent:build: dist/index.js 25.31 kB │ gzip: 7.67 kB │ map: 80.29 kB\n@hashintel/brunch-agent:build: \n@hashintel/brunch-agent:build: ✓ built in 16ms\n\n Tasks: 1 successful, 1 total\nCached: 0 cached, 1 total\n Time: 2.731s \n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "yarn workspace @hashintel/brunch-agent vitest run test/architecture/docs-index.test.ts", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CgeiPXHFUIKXmG3aJMw` - -``` -{ - "output": "\n RUN v4.1.10 /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core\n\n ✓ test/architecture/docs-index.test.ts (7 tests) 12ms\n\n Test Files 1 passed (1)\n Tests 7 passed (7)\n Start at 13:48:18\n Duration 218ms (transform 14ms, setup 0ms, import 22ms, tests 12ms, environment 0ms)\n\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "turbo run linear:graph --filter '@hashintel/brunch-agent' 2>/dev/null | sed -n '/project brunch-agent/p;/cycles:/p'", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CgetbOt6dYI1rVHQg6w` - -``` -{ - "output": "@hashintel/brunch-agent:linear:graph: project brunch-agent open=35 hard=16\n@hashintel/brunch-agent:linear:graph: cycles: none\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git add docs/INDEX.md docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json docs/inbox/SDCPNs/gases-1-pn-layout.json docs/inbox/SDCPNs/gases-2-spn-layout.json docs/inbox/SDCPNs/gases-3-cpn-layout.json docs/inbox/SDCPNs/gases-4-dcpn-layout.json docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json && git status --short && printf '%s\\n' '--- staged paths' && git diff --cached --name-only", - "workdir": "/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgfA2MIuwW27fT6G0Du` - -``` -{ - "output": "M docs/INDEX.md\n M docs/agents/git-workflow.md\n M docs/agents/issue-writing.md\n M docs/agents/legibility.md\nA docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\nA docs/inbox/SDCPNs/gases-1-pn-layout.json\nA docs/inbox/SDCPNs/gases-2-spn-layout.json\nA docs/inbox/SDCPNs/gases-3-cpn-layout.json\nA docs/inbox/SDCPNs/gases-4-dcpn-layout.json\nA docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\nA docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n M docs/planning/_shared/STEERING.md\n M docs/planning/_shared/hash-monorepo-import-plan.md\n--- staged paths\nlibs/@hashintel/brunch-agent/docs/INDEX.md\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-2-spn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-3-cpn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-4-dcpn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git diff --cached --check && git commit -m \"Register incoming SDCPN nets\"", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 60000 -} -``` - -## User - -**Tool Result:** `TU-034CgfJ4NhE2hAPxrpnbXK` - -``` -{ - "output": "╭──────────────────────────────────────╮\n│ 🥊 lefthook v2.0.0 hook: pre-commit │\n╰──────────────────────────────────────╯\n│ toml (skip) no files for inspection\n│ sqlfluff (skip) no files for inspection\n│ rust (skip) no files for inspection\n│ yarn (skip) no matching staged files\n┃ oxfmt ❯ \n\n(node:29806) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/lunelson/Code/hashintel/hash/oxfmt.config.ts?cache=1787572121528 is not specified and it doesn't parse as CommonJS.\r\nReparsing as ES module because module syntax was detected. This incurs a performance overhead.\r\nTo eliminate this warning, add \"type\": \"module\" to /Users/lunelson/Code/hashintel/hash/package.json.\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\nExpected at least one target file. All matched files may have been excluded by ignore rules.\r\n\n┃ markdownlint ❯ \n\nmarkdownlint-cli2 v0.22.1 (markdownlint v0.40.0)\r\nFinding: libs/@hashintel/brunch-agent/docs/INDEX.md !**/LICENSE*.md !**/AGENTS*.md !node_modules/** !target/** !**/_temp/** !**/_ai/** !**/.cursor/plans/** !libs/@hashintel/ds-components/** !libs/@hashintel/ds-helpers/** !libs/@hashintel/brunch-agent/docs/**\r\nLinting: 0 file(s)\r\nSummary: 0 error(s)\r\n\n \n ────────────────────────────────────\nsummary: (done in 0.97 seconds) \n✔️ oxfmt (0.30 seconds)\n✔️ markdownlint (0.90 seconds)\n[ln/fe-xxxx-whats-next c3d29164ab] Register incoming SDCPN nets\n 8 files changed, 8602 insertions(+), 1 deletion(-)\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-2-spn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-3-cpn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-4-dcpn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n", - "exitCode": 0 -} -``` - -## Assistant - -**Tool Use:** `shell_command` - -```json -{ - "command": "git status --short; git show --stat --oneline --summary HEAD", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -**Tool Use:** `shell_command` - -```json -{ - "command": "git show --format= --check HEAD; git show --format= --name-only HEAD", - "workdir": "/Users/lunelson/Code/hashintel/hash", - "timeout_ms": 10000 -} -``` - -## User - -**Tool Result:** `TU-034CgfTxQq1LE53iOSKNFb` - -``` -{ - "output": " M libs/@hashintel/brunch-agent/docs/agents/git-workflow.md\n M libs/@hashintel/brunch-agent/docs/agents/issue-writing.md\n M libs/@hashintel/brunch-agent/docs/agents/legibility.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/STEERING.md\n M libs/@hashintel/brunch-agent/docs/planning/_shared/hash-monorepo-import-plan.md\nc3d29164ab Register incoming SDCPN nets\n libs/@hashintel/brunch-agent/docs/INDEX.md | 4 +-\n .../gases-1-pn-consumption-trigger-layout.json | 486 +++++\n .../docs/inbox/SDCPNs/gases-1-pn-layout.json | 472 +++++\n .../docs/inbox/SDCPNs/gases-2-spn-layout.json | 978 +++++++++\n .../docs/inbox/SDCPNs/gases-3-cpn-layout.json | 1300 ++++++++++++\n .../docs/inbox/SDCPNs/gases-4-dcpn-layout.json | 1619 ++++++++++++++\n .../SDCPNs/semiconductor-fab-drift-layout.json | 1509 +++++++++++++\n .../truck-fleet-predictive-maintenance-layout.json | 2235 ++++++++++++++++++++\n 8 files changed, 8602 insertions(+), 1 deletion(-)\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-2-spn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-3-cpn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-4-dcpn-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\n create mode 100644 libs/@hashintel/brunch-agent/docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n", - "exitCode": 0 -} -``` - -**Tool Result:** `TU-034CgfTy1DdxTa9G2ZCEpv` - -``` -{ - "output": "libs/@hashintel/brunch-agent/docs/INDEX.md\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-consumption-trigger-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-1-pn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-2-spn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-3-cpn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/gases-4-dcpn-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/semiconductor-fab-drift-layout.json\nlibs/@hashintel/brunch-agent/docs/inbox/SDCPNs/truck-fleet-predictive-maintenance-layout.json\n", - "exitCode": 0 -} -``` - -## Assistant - -Registered and committed the seven nets as inbox material (`c3d29164ab`). - -Findings: - -- All parse successfully with current Petrinaut schemas and complete positions. -- Inventory: two gas PN variants, gas SPN/CPN/DCPN, truck fleet, semiconductor fab. -- The expected gas SDCPN/diffusion net is absent. -- Structural inspection reflects stochastic transitions and nontrivial DEs, but is not a mathematical conformance review. - -They remain in `docs/inbox/SDCPNs/` pending the corpus-location decision and provenance/conformance settlement. If `gases-5-sdcpn` exists, please add it. - -## User - -No, gases-5 doesn't seem to exist, that might be a bad reference. What else can I provide now? Where are we with locking these decisions? - -BTW I suggest you use subagents from now on in this thread, for all forms of file processing including parsing/evaluation/reporting, but especially for executing file moves, cleanups etc. so we don't waste our discussion context on tool calling - -## Assistant - -Agreed. I’ll keep the strategic synthesis in this thread and route file inspection, parsing, migrations, cleanup, and artifact generation through subagents. I’m having one inspect the current control documents now—read-only—so I can distinguish what is actually deposited from what remains only agreed in conversation, without touching your in-flight edits. - -**Tool Use:** `Task` - -```json -{ - "prompt": "Perform a read-only audit in the Brunch context root `/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent`. Do not modify, stage, or commit anything; the worktree is shared and has unrelated/in-flight edits. Inspect current `docs/planning/_shared/STEERING.md`, `docs/planning/_shared/COORDINATION.md`, `docs/INDEX.md`, relevant ADR references they point to, and `git diff` for those files. Determine which of these session decisions are durably deposited versus absent/stale/contradicted: (1) Vestera remains the cold-start benchmark for now; (2) Dora's seven delivered JSON files are a projection/formalism-complexity corpus, with no evidence that a `gases-5` file exists and the prior five-stage filename reference likely bad; (3) live-demo completeness is bounded, but a reasonably capable full-model cold-start path and an improved-flat-prompt baseline comparison remain important; (4) proof means executable evidence + a legible persistent artifact + ideally a human witness; artifacts may graduate into regression oracles; (5) long-lived STEERING should be compact, using a frontier/issue tree and links to immutable evidence; (6) `docs/planning/<effort>` is no longer accepted as the target ordering principle—effort should be metadata, with control/spec/decision/evidence/reference/archive roles proposed but not finalized; (7) evaluation-material vocabulary/placement (case corpus, local test fixture, immutable run snapshot, reviewed oracle) is proposed but not locked; (8) demo use case Track A vs biopharma Track B remains an external decision gate; (9) FE-1480 inference authority remains unresolved. Return a compact table: decision, deposited status, current authoritative location if any, exact stale/contradictory text with file:line, and smallest future reconciliation action. Also report whether the current uncommitted STEERING diff belongs to another actor and must remain untouched. No broad review or copyediting.", - "description": "Audit deposited decisions" -} -``` - -## User - -**Tool Result:** `TU-034Cgjgp2amD8smaEq2yqO` - -``` -Read-only audit completed. No files modified, staged, or committed. - -| # | Decision | Status | Current authority | Stale/contradictory text | Smallest reconciliation | -|---|---|---|---|---|---| -| 1 | Vestera remains the cold-start benchmark | **Absent as a current decision**; corpus exists | `baseline/situation-pack.md:1,11` identifies Vestera as FE-1361’s baseline | `STEERING.md:126,157,229` defers/ excludes complete cold-start work without preserving Vestera’s benchmark role | Add one compact steering/frontier note linking the existing baseline corpus and naming Vestera as retained benchmark. | -| 2 | Dora’s seven JSON files are a projection/formalism-complexity corpus; no `gases-5`; prior five-stage filename likely bad | **Partially deposited, awaiting settlement** | `docs/INDEX.md:13` records seven files and absence of the expected fifth gas file | `docs/INDEX.md:26`: “five-level SDCPN explainer applied to gas supply…” may perpetuate the level/ladders framing, though it is not itself a filename claim. No `gases-5` reference exists in searched Markdown. | Settle the inbox corpus with the projection/formalism-complexity role; explicitly retire the presumed five-stage filename rather than infer a missing file. | -| 3 | Live-demo completeness bounded, while capable full-model cold-start and improved-flat-prompt comparison remain important | **Bounded demo deposited; retained importance absent/stale** | Demo bound: `STEERING.md:39-42,143-158,222-233` | `STEERING.md:126`: “defer a complete cold-start runbook”; `:157`: “do not expand into cold-start elicitation”; `:229`: “Do not build a complete cold-start CPS interview…” No improved-flat-prompt comparison is recorded. | Preserve the demo cut while adding both items as post-frontier evaluation obligations, not current demo scope. | -| 4 | Proof = executable evidence + legible persistent artifact + ideally human witness; artifacts may graduate to regression oracles | **Partially deposited** | Executable/product-path proofs: `STEERING.md:178-180,188-193,206-210,218-220`; persistent inspectable artifacts appear at `:190-191,218-220`; witness-like confirmation at `:154` | No explicit three-part proof definition, human-witness preference, or artifact-to-regression-oracle lifecycle exists. | Add a compact proof convention and link each frontier to immutable evidence; record oracle graduation as a proposed lifecycle. | -| 5 | Long-lived STEERING should be compact: frontier/issue tree plus immutable-evidence links | **Contradicted by current shape; frontier concept deposited** | Frontier ordering: `STEERING.md:160-220`; issue projection: `:235-264` | `STEERING.md:17`: “this file carries only the current model”; `:18`: “shape is intentionally specific to the present effort.” The file is 292 lines and embeds extensive narrative/tables rather than compact links. | On a future reconciliation pass, retain current choice, frontier/issue tree, gates and links; move durable evidence/details to immutable records. | -| 6 | `docs/planning/<effort>` no longer accepted as ordering principle; effort becomes metadata; role taxonomy proposed, not final | **Contradicted / proposal absent** | Current index remains effort-oriented | `docs/INDEX.md:28`: `history/planning/elicitation-kernel`; `:39`: `planning/process-model-elicitation (effort active…)`; `:69`: `planning/_shared`; `:114-118`: “Both efforts moved wholesale to `docs/planning/<effort>/`…” | Record the proposed role taxonomy as unresolved design input; only after ratification revise INDEX/path policy. | -| 7 | Evaluation vocabulary/placement: case corpus, local fixture, immutable run snapshot, reviewed oracle | **Absent / not locked** | No authoritative vocabulary found | Existing terms are inconsistent: `docs/INDEX.md:13` “corpus inventory”; `STEERING.md:168-176` “fixture”; `:190-193` test/proposal evidence. No lifecycle distinction is defined. | Add an explicitly provisional vocabulary note with placement examples; do not migrate files until ratified. | -| 8 | Demo Track A vs biopharma Track B is an external decision gate | **Substance deposited; track labels absent** | `STEERING.md:23-25,256-259,271-274,289`; `COORDINATION.md:133-135` | Current documents frame the gate as create-new/cold-start versus review-existing, not “Track A/Track B.” Biopharma appears only as background at `docs/INDEX.md:22`. | Name both candidate tracks and their external owner/gate in one steering line once terminology is confirmed. | -| 9 | FE-1480 inference authority unresolved | **Durably deposited and consistent** | `STEERING.md:107-112,168-180,248,275,289`; `COORDINATION.md:20-21,45,61,98-101`; governing constraint `ADR-0003:24-46` | No contradiction found. ADR-0003 requires write-time semantics and pure projection; planning correctly leaves the worked CPS adjudication open. | No reconciliation beyond linking the eventual worked transformation/decision and amending ADR-0003 if required. | - -### Uncommitted STEERING diff - -`docs/planning/_shared/STEERING.md` is already modified. Its diff consists only of Markdown table/list reformatting and line wrapping. It was not produced by this audit; in the shared-worktree protocol it therefore belongs to another actor and **must remain untouched**. Git cannot identify the uncommitted actor, only that the last committed author was Lu Nelson. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "Make one narrowly scoped factual correction in the Brunch context root `/Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent`. The shared worktree has unrelated/in-flight edits, especially `docs/planning/_shared/STEERING.md` and agent protocol files: do not touch, stage, format, or commit those. Read `docs/INDEX.md`, then change only its inbox row for `docs/inbox/SDCPNs/` so it no longer calls a fifth gas SDCPN 'expected' or implies a missing artifact. State compactly that the arrival contains seven valid Petrinaut v1 files—two plain-net gas variants, gas SPN/CPN/DCPN, truck, semiconductor—and that a prior `gases-5` reference is unconfirmed/likely erroneous rather than part of the corpus. Re-read the changed file, run `git diff --check -- docs/INDEX.md` and `yarn workspace @hashintel/brunch-agent vitest run test/architecture/docs-index.test.ts` from the HASH root. Stage only the literal path `libs/@hashintel/brunch-agent/docs/INDEX.md`, verify the staged set contains only that path, and commit with message `Correct SDCPN corpus inventory`. Return the commit SHA, exact wording, validation, and confirm unrelated changes remain untouched.", - "description": "Correct corpus inbox wording" -} -``` - -## User - -**Tool Result:** `TU-034CglO4YHRHYXZ8Nsxp83` - -``` -Committed as `d38c17120e13d1bc7dd57a9949ec308882108ad6`. - -Exact wording: - -> Seven valid Petrinaut v1 files: two plain-net gas variants, gas SPN/CPN/DCPN, truck, and semiconductor; a prior `gases-5` reference is unconfirmed and likely erroneous, not part of the corpus - -Validation passed: - -- `git diff --check -- docs/INDEX.md` -- Docs index test: 7/7 passed -- Pre-commit hooks passed - -Only `docs/INDEX.md` was staged and committed. All unrelated in-flight changes remain untouched. -``` - -## Assistant - -Agreed on subagents. I’ll keep this thread for synthesis and decisions; file inspection, parsing, reports, migrations, and verification will run in delegated workers. - -I also corrected the corpus inventory so `gases-5` is treated as an unconfirmed, likely erroneous reference—not a missing artifact. Commit: `d38c17120e`. - -### Lock status - -**Durably deposited** - -- Three-register architecture and write-time semantic constraint. -- FE-1480 remains an explicit unresolved authority decision. -- Bounded review-and-revise demo spine. -- Seven-net corpus inventory. - -**Agreed here, but not yet deposited** - -- Vestera remains the cold-start benchmark. -- Cold-start capability remains important outside the bounded live demonstration. -- Baseline comparison becomes bare Claude vs improved flat prompt vs Brunch. -- Proof bundles require executable evidence, persistent legibility, and human witness. -- STEERING should become a compact frontier/issue tree linking immutable evidence. -- Evaluation vocabulary: case, fixture, run snapshot, reviewed oracle. -- Effort should become metadata rather than documentation’s directory authority. - -**Still pending** - -- Track A versus biopharma demo decision. -- Exact optimisation handoff contract. -- Minimum coherent CPS semantic coverage. -- Evaluation/document placement convention. -- Issue coverage for controller, runbooks, proof instruments, and baseline reruns. - -The current STEERING file therefore does **not** yet represent all settled discussion. It also has an in-flight modification from another actor, which remains untouched. - -### Most useful material now - -In priority order: - -1. **Outcome of tomorrow’s use-case decision**, including rationale and expected demo claim. -2. **Yannis’s L0–L5 ladder and grading dimensions**—an export rather than inaccessible links. -3. **SOL-90 instance/state-specification schema** when available. -4. **Optimisation handoff example:** one representative input artifact and what Chris/Yannis expect to consume. -5. **Export of Yannis’s mathematical review thread**, including Dora’s fixes. -6. **Best current flat prompt** for the new baseline—or confirmation that we should derive it from the existing research and v0 prompt. -7. For provenance, a small manifest for the seven nets: author, source location, revision date, and review status. - -Once the use-case decision lands, we can conduct the actual Frontier 0 lock pass: ratify only the necessary decisions, route each to its correct authority, compact STEERING, and expose remaining issue gaps. - -## User - -In the meantime, we can do the topology cleanups and stale-to-archive passes that we've already described and agreed upon, and reconciliation and clarification of all attendant agent guidance. I then also want to re-examine the list of routine agent actions you had proposed when first outlining the "STEERING" protocol, before you had actually named it. Scan back over that stuff, collect it, look for any gap we haven't mapped or agreed, let's lock those and do them - -## Assistant - -I’ll treat this as one control-surface migration with three outputs: a role-based documentation topology, reconciled agent guidance, and a locked steering operating loop. I’ll keep synthesis here and delegate the inventory, file classification, moves, link repair, and verification. First I’m loading the governing guidance for architecture-sensitive and agent-facing changes. - -**Tool Use:** `skill` - -```json -{ - "name": "posture", - "arguments": "Apply the Brunch prototype/high-stakes/current-milestone posture to a documentation/control-surface topology migration." -} -``` - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-writing-for-agents", - "arguments": "Reconcile Brunch AGENTS.md and docs/agents guidance around STEERING, documentation topology, proof artifacts, and recurring steering actions." -} -``` - -**Tool Use:** `skill` - -```json -{ - "name": "building-skills", - "arguments": "Assess and, only if justified by the locked protocol, create or edit a local Brunch steering skill. Load before touching skill files." -} -``` - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-domain-modeling", - "arguments": "Use precise authority and lifecycle vocabulary for control, spec, decision, evidence, reference, and archive documents." -} -``` - -## User - -**Tool Result:** `TU-034Cgr60mtLZY97kxALo2K` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"posture\">\n# posture Skill\n\n\n# Posture\n\nA **posture** declares a repo's epistemic and strategic stance so architecture-sensitive decisions default correctly. The instance lives in `<cwd>/memory/POSTURE.md`; this skill is the schema and its behavioral consequences.\n\n## Instance format\n\n`memory/POSTURE.md` declares one **preset**, then only the axes that diverge:\n\n```\nposture: prototype\n\nstakes: high # override + one-line reason\nhorizon: current-milestone\n```\n\nA bare preset name with no overrides is valid. The overrides carry the signal — they are the interesting, non-default facts about the project. Presets are authoring shorthand; they expand to the six stored dimensions (the form persisted in the workspace state stub).\n\n## Presets\n\nPick the closest preset, override the rest.\n\n| axis | `prototype` | `product` |\n| ------------ | -------------- | ---------------------- |\n| certainty | proving | earned |\n| migration | free-rewrite | deprecation-discipline |\n| dependencies | resist | resist |\n| audience | internal | external |\n| stakes | low | high |\n| horizon | current-slice | current-milestone |\n\n- **`prototype`** — early, uncertain, internal. Optimize for proof and rewrite speed.\n- **`product`** — shipped, externally consumed, stable. Optimize for continuity and public-surface care.\n- **`library`** — `product` with `horizon: next-major` (API stability across majors).\n\n`dependencies` defaults to `resist` in both presets; it is the axis most often overridden to `accept` when a project deliberately buys into a framework.\n\n## Axes and behavior\n\nEach axis names the behavior its non-default pole imposes; the relaxed pole is the baseline.\n\n**certainty: proving | earned**\n- `proving` → no abstraction until two real callers (rule of three); no generics; prefer inlining. Under uncertainty, complexity is a tax on future understanding.\n- `earned` → abstraction permitted where a second real caller exists; materialize topology into structure; canonicalize names; delete obsolete shims and concept bridges. Closure and conceptual integrity over first-cut speed.\n\n**migration: free-rewrite | deprecation-discipline**\n- `free-rewrite` → change or remove the old shape directly; let compile/type/test breakage enumerate the rewrite. No aliases, adapters, shims, or expand/contract schemes unless (a) the transition crosses a boundary you cannot update atomically — separate deploys/processes, persisted data, external consumers — or (b) the bridge is named, tiny, and removed in the same slice.\n- `deprecation-discipline` → preserve existing call sites and contracts across slices; deprecate before removing.\n\n**dependencies: accept | resist**\n- `accept` → take well-fit dependencies on their happy path; don't reinvent.\n- `resist` → treat new dependency surface as cost until proven otherwise; strip or fork before adding; lose weight opportunistically.\n\n**audience: internal | external**\n- `internal` → optimize for the team; minimal public-surface ceremony.\n- `external` → care with public surface: naming, docs, error messages, stability.\n\n**stakes: low | high**\n- `low` → trust internal callers; minimal defensive code.\n- `high` → validate at boundaries; fail loud; defensive error paths.\n\n**horizon: current-slice | current-milestone | next-major**\n- Stubs and layouts extend only as far as work named in project planning artifacts (e.g. `memory/PLAN.md`, `memory/SPEC.md`) within the declared horizon — no further.\n\n## Scope — repo default, narrower override\n\n`memory/POSTURE.md` is the **repo default**, but posture is not uniform across a codebase: a mostly-`earned` repo can carry a fresh `proving` seam, and a settled seam can regress to `proving` on a new unknown. A narrower scope — a frontier, milestone, or work item — may therefore **override** one or more axes for its duration; the override wins inside that scope and falls back to the repo default outside it.\n\n`certainty` is the axis that most often varies this way, because it flips the planning decision kernel (`proving` → optimize for information gain; `earned` → optimize for closure gain); the other axes are usually stable repo-wide. An override is a **state, not a permanent mode** — `earned → proving` on a new unknown is a transition, not a third value.\n\nHow overrides are recorded is the project's call. In repos using the `ln-*` planning system, each frontier in `memory/PLAN.md` carries an explicit `Certainty:` field — consult that planning convention rather than restating it here.\n\n## The common early-stage posture\n\n`prototype` (proving · free-rewrite · current-slice) is the default for new work: prefer breakage-driven rewrite over compatibility scaffolding — remove or change the thing, then follow the fix list. Well-typed systems make the fix list legible; reach for a bridge only when the type/test surface cannot reach the change (wire formats, persisted state, external consumers).\n\n## Conflicts\n\nThe **effective** posture for a piece of work is the repo default plus any active per-frontier override. If a request implies a posture different from that (external API but `audience: internal`; a generic framework but `certainty: proving`), state the mismatch, name the behavioral consequence, and ask before taking the more committing path. Do not silently average conflicting postures.\n\n---\n\n*Note — posture can also appear as a **product** concept: a project-grounding tool may capture a user's project posture as first-class data (Brunch mirrors this exact vocabulary). Keep any such product model independently owned — this skill is the methodology vocabulary, not a shared module.*\n\n\nARGUMENTS: Apply the Brunch prototype/high-stakes/current-milestone posture to a documentation/control-surface topology migration.\n\nBase directory for this skill: file:///Users/lunelson/.agents/skills/posture\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -**Tool Result:** `TU-034Cgr61xLTkCJl9lJfHtQ` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-writing-for-agents\">\n# ds-writing-for-agents Skill\n\n\n<!-- Vendored from mattpocock/skills@84fdeff: skills/productivity/writing-for-agents -->\n\nReference for writing any document an agent consumes — a skill, an `AGENTS.md` / `CLAUDE.md`, a doc reached by a pointer. The packaging differs; the writing does not: the same levers make each one predictable — the agent taking the same _process_ every run, not producing the same output.\n\nWhen the document you're writing is a skill, read [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md) for frontmatter, invocation choice, and router skills.\n\n## Context pointers\n\nA **context pointer** is a reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. A skill's description is one; a line in `AGENTS.md` naming a doc is the same object. The pointer's _wording_, not its target, decides when the agent reaches the material — and how reliably. A must-have target behind a weakly worded pointer is a variance bug: sharpen the wording first, and inline the material only if sharpening fails.\n\nA pointer does two jobs — state what the material is, and list the **branches** that should trigger reaching it (a branch is a distinct case the document handles, so different runs take different paths through it). Every word of an always-loaded pointer costs on every turn, so it earns even harder pruning than the body:\n\n- **Front-load the leading word** — the pointer is where it does its triggering work.\n- **One trigger per branch.** Synonyms that rename a single branch are one branch written twice; collapse them and keep only genuinely distinct branches.\n- **Cut identity the body already carries.**\n\n## The two loads\n\nEvery document and pointer you add spends one of two budgets:\n\n- **Context load** — the cost of always-loaded material on the agent's window: an `AGENTS.md` line, a skill description, anything sitting in context every turn, spending tokens and attention whether or not it fires.\n- **Cognitive load** — the cost on the human: which documents exist and when to reach for each. The human is the index. Not a cost to minimise — it is the price of human agency; spend it where human judgement matters, remove it where it does not.\n\nMaterial reached only through a pointer escapes context load at the price of the pointer's own line; material with no pointer at all rides entirely on cognitive load.\n\n## Information hierarchy\n\nA document is built from two content types — **steps** (the ordered actions the agent performs) and **reference** (definitions, rules, facts consulted on demand) — that mix freely: all steps (a recipe), all reference (a review's rules, this skill), or both. The core decision is where each piece sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:\n\n1. **In-file step** — the primary tier: what the agent does, in order.\n2. **In-file reference** — consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell.\n3. **Disclosed reference** — pushed out into a separate file, reached by a context pointer, loaded only when the pointer fires. Spans a sibling file in the same folder through fully external reference that lives anywhere and any document can point at.\n\nPush too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.\n\n**Progressive disclosure** is the move down the ladder — out of the main file and behind a pointer — so the top stays legible. Not primarily a token optimisation: it is how the hierarchy is protected. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. When a document has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one.\n\n**Co-location** is the within-file companion: where the ladder decides _how far down_ a piece sits, co-location decides _what sits beside it_ once there. Keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it. The test: the document should read like documentation written for the agent — grouped material reads that way; scattered material does not. (Distinct from duplication: that repeats one meaning in two places; scattering fragments one meaning across many.)\n\n**Sprawl** is the failure mode here: a document simply too long, even when every line is live and unique. Attention thins across the excess, and every extra line is one more to keep relevant. The cure is the ladder: disclose reference behind pointers, and split by branch or sequence so each path carries only what it needs.\n\n## Steps and completion criteria\n\nEvery step ends on a **completion criterion** — the condition that tells the agent the work is done. Two properties make it a lever:\n\n- **Clarity** — can the agent tell done from not-done? A vague bound (\"understanding reached\") invites **premature completion**: ending the step before it is genuinely done, attention slipping to _being done_. The visible steps still ahead — the **post-completion steps** — supply the pull; the criterion's clarity is the resistance. Defend in order: **sharpen the bound first** (local and cheap); only if it is irreducibly fuzzy _and_ you observe the rush, hide the later steps by splitting the sequence — and hiding only works across a real context boundary (a hand-off or a subagent dispatch; an inline call leaves the later steps in context and clears nothing).\n- **Demand** — how much it requires. \"Every modified model accounted for\" forces thorough work where \"produce a change list\" does not. Demand drives **legwork** — the digging the agent does within the work, latent in the wording rather than written as its own step — and it is not step-bound: \"every rule applied\" binds a body of flat reference just as \"every step done\" binds a sequence, which is how an all-reference document still carries an exhaustiveness bar.\n\nThe strongest criteria are both checkable and exhaustive.\n\n## When to split\n\nSplitting one document into two spends one of the two loads, so split only when the cut earns it:\n\n- **By sequence** — split a run of steps where the post-completion steps tempt the agent to rush the one in front of it. Keeping them out of view drives more legwork on the current task. Beware the reverse: merging sequences exposes each step's later steps to what follows, inviting premature completion.\n- **By invocation** — skill-specific: see [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md).\n\n## Leading words\n\nA **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the document (_lesson_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free; reach for an existing word first.\n\nIt anchors twice. In the body, _execution_: the agent reaches for the same behaviour every time the word appears, and inside flat reference it focuses attention on a class of thing to look for. In a pointer, _invocation_: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the material and reaches it more reliably.\n\nHunt for opportunities to refactor with leading words. A triad spelled out at three sites, a pointer spending a sentence to gesture at one idea — each is a passage begging to collapse into a single token:\n\n- \"fast, deterministic, low-overhead\" → _tight_ (a _tight_ loop).\n- \"a loop you believe in\" → _red_ — a fuzzy gate becomes a binary observable state (the loop goes _red_ on the bug, or it doesn't).\n\nYou win twice: fewer tokens, and a sharper hook for the agent to hang its thinking on. Assume every document is carrying restatements that leading words retire — go find them.\n\n**Negation** is the failure mode beside this lever: steering by prohibition drags the forbidden behaviour into context and makes it _more_ available, not less. _Don't think of an elephant_, and the elephant is all there is; the negation is a weak modifier the strongly-activated concept overruns, so the ban half-reads as an instruction to do the thing. Prompt the **positive** — state the target behaviour (\"write one-line comments\") so the banned one is never spoken. A prohibition earns its place only as a hard guardrail you cannot phrase positively; even then, pair it with the positive target so attention lands on what to do.\n\n## Pruning\n\n- Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit. **Duplication** — the same meaning in more than one place — costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank. (The accidental inverse of a leading word, which repeats a token on purpose, never the meaning.)\n- The **environment** is a source of truth too — `package.json` scripts, config files, the directory layout, `--help` output — and a document that restates it is a **cache**: a copy of a lookup, earning its load only when the lookup is expensive. Cache what the agent cannot find by looking: the unwritten convention, the reason behind a choice, the gotcha no config confesses. Leave the one-file, one-command lookups to the environment, where they cannot go stale.\n- Check every line for **relevance**: does it still bear on what the document does? A line loses relevance by never bearing on the task (mere exposition, or a branch that should be disclosed) or by going stale as the behaviour or world it describes changes. Shorter documents are easier to keep relevant. Without a pruning discipline the default fate is **sediment**: stale layers that settle because adding feels safe and removing feels risky, until you must core down through them to find what is still live.\n- Hunt **no-ops** sentence by sentence: an instruction the model already obeys by default pays load to say nothing. The test — does it change behaviour versus the default? — is model-relative, not reader-relative: two people disagreeing about a no-op disagree about the default, and settle it by running the document, not by debate. When a sentence fails, delete the whole sentence rather than trim words from it. The test also grades leading words: a word too weak to beat the default (_be thorough_ when the agent is already thorough-ish) is a no-op, and the fix is a stronger word (_relentless_), not a different technique.\n\n\nARGUMENTS: Reconcile Brunch AGENTS.md and docs/agents guidance around STEERING, documentation topology, proof artifacts, and recurring steering actions.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-writing-for-agents\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-writing-for-agents/SKILL-MECHANICS.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-writing-for-agents/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -**Tool Result:** `TU-034Cgr62RW6NM1JMBBmjYK` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"building-skills\">\n# building-skills Skill\n\n# Building Skills\n\nCreates well-structured Agent Skills following best practices.\n\n## Skill Structure\n\nEvery skill needs a `SKILL.md` file with YAML frontmatter:\n\n```markdown\n---\nname: my-skill-name\ndescription: Does X when Y happens. Use for Z tasks.\n---\n\n# Skill Title\n\nInstructions go here.\n```\n\n## Frontmatter Requirements\n\n### name (required)\n- Maximum 64 characters\n- Lowercase letters (a-z), numbers (0-9), and hyphens only\n- Must not start or end with a hyphen\n- No consecutive hyphens (`my--skill` is invalid)\n- Must match parent directory name exactly\n- Use gerund form (verb + -ing): `processing-pdfs`, `analyzing-data`, `managing-deployments`\n- Avoid vague names: `helper`, `utils`, `tools`\n\n### description (required)\n- Maximum 1024 characters (should be much shorter than 1024 characters)\n- Write in third person (\"Processes files\" not \"I process files\")\n- Include BOTH what the skill does AND when to use it\n- Be specific with key terms for discovery\n- **Quote the value** if it contains colons, special YAML characters, or \"Triggers on:\" patterns:\n ```yaml\n description: \"Fetches tasks from Notion. Triggers on: my tasks, show work.\"\n ```\n\n**Good descriptions:**\n- \"Extracts text and tables from PDF files, fills forms, merges documents. Use when working with PDF files or asked to read/edit PDFs.\"\n- \"Queries BigQuery datasets using the bq CLI. Use for data analytics, SQL queries, or Google Cloud data warehouse tasks.\"\n- \"Reviews pull requests for code quality, security, and test coverage. Use when asked to review a PR or diff.\"\n\n**Bad descriptions:**\n- \"Helps with files\" (too vague)\n- \"I can help you with data\" (wrong POV)\n- \"PDF tool\" (no trigger context)\n\n### Optional fields\n- `license`: License identifier (e.g., \"MIT\", \"Apache-2.0\")\n- `compatibility`: Max 500 characters describing compatibility requirements\n- `metadata`: Arbitrary metadata object\n- `allowed-tools`: List of tools the skill can use\n- `argument-hint`: Hint for skill arguments\n- `model`: Preferred model for the skill\n- `mode`: Agent mode override\n- `isolatedContext`: Run skill in isolated context\n- `mcpServers`: Inline MCP server config for skills, especially single-file skills (see Bundling MCP Servers)\n\n## Directory Structure\n\n### Simple Skill (instructions only)\n```\n.agents/skills/my-skill/\n└── SKILL.md\n```\n\n### Skill with Scripts\n```\n.agents/skills/my-skill/\n├── SKILL.md\n└── scripts/\n └── my-script.sh\n```\n\n### Complex Skill (progressive disclosure)\n```\n.agents/skills/my-skill/\n├── SKILL.md # Overview, under 500 lines\n├── reference/\n│ ├── api.md # Detailed API docs\n│ └── examples.md # Code examples\n└── scripts/\n └── validate.py # Executable scripts\n```\n\n## Progressive Disclosure\n\nSkills load in stages to save context:\n\n1. **Level 1 - Metadata**: Name + description loaded at startup (~100 tokens)\n2. **Level 2 - Instructions**: SKILL.md body loaded when triggered (<5k tokens)\n3. **Level 3 - Resources**: Additional files loaded only when needed\n\nKeep SKILL.md under 500 lines. Split large content into separate files.\n\n## Writing Effective Instructions\n\n### Do\n- Start with a clear one-line summary\n- List specific capabilities\n- Provide step-by-step workflows\n- Include concrete examples\n- Reference scripts with execution intent: \"Run `scripts/validate.py` to check...\"\n\n### Don't\n- Explain concepts the model already knows\n- Add lengthy introductions or summaries\n- Include time-sensitive information in main sections\n- Use abstract examples\n\n## Executable Scripts\n\nPlace scripts in a `scripts/` subdirectory and reference them in SKILL.md:\n\n```\n.agents/skills/my-skill/\n├── SKILL.md\n└── scripts/\n └── run-task.sh\n```\n\nReference with execution intent: \"Run `scripts/run-task.sh` to execute the task\"\n\n## Bundling MCP Servers\n\nSkills can bundle MCP servers. The MCP starts at Amp startup but its tools stay hidden until the skill loads. There are two ways to declare servers.\n\n### Inline in frontmatter (default)\n\nPrefer this. Declare servers under an `mcpServers` key in SKILL.md frontmatter, keyed by server name. It keeps the skill a single self-contained file — the simplest to move, import, and review.\n\n```markdown\n---\nname: web-browser\ndescription: Automates a Chrome browser. Use for navigating pages and taking screenshots.\nmcpServers:\n chrome-devtools:\n command: npx\n args: [\"-y\", \"chrome-devtools-mcp@latest\"]\n includeTools: [\"navigate_page\", \"take_screenshot\"]\n---\n```\n\n### Sibling mcp.json (multi-file skills)\n\nUse a separate `mcp.json` only when the skill already needs a directory of resources (`scripts/`, `reference/`). If an `mcpServers` key is present it takes precedence over a sibling `mcp.json` — even when empty or containing no valid specs — and the two are never merged, so do not leave a stale `mcp.json` behind after moving config inline.\n\n```\n.agents/skills/web-browser/\n├── SKILL.md\n└── mcp.json\n```\n\n**Example mcp.json:**\n```json\n{\n \"chrome-devtools\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"chrome-devtools-mcp@latest\"],\n \"includeTools\": [\"navigate_page\", \"take_screenshot\"]\n }\n}\n```\n\nBoth forms use the same server-spec fields, so folding an `mcp.json` into inline frontmatter is a direct copy of the server map.\n\n### ALWAYS Filter MCP Tools\n\n**This is critical.** MCP servers often expose many tools (chrome-devtools has 26 tools = 17,700 tokens). Always use `includeTools` to expose only what the skill needs.\n\nAsk the user: \"Which tools from this MCP do you actually need?\"\n\n```json\n{\n \"includeTools\": [\"navigate_page\", \"take_screenshot\", \"click\"]\n}\n```\n\nThis reduces token cost by 90%+ and keeps the skill focused.\n\nThe same gating exists for plugin tools: a plugin's bundled skill can list the\nplugin's own tools in `builtin-tools` frontmatter to hide them until the skill\nloads. See the building-plugins skill.\n\n### MCP server fields\n\n- `command`: Command to run for stdio servers (required unless `url` is set)\n- `args`: Array of arguments for stdio servers\n- `env`: Environment variables for stdio servers\n- `url`: HTTP MCP server URL (required unless `command` is set)\n- `headers`: HTTP request headers\n- `transport`: HTTP transport (`http`, `sse`, or `http-first`)\n- `includeTools`: **Always set this.** Glob patterns for which tools to expose. Do not guess tool names; use web search to find the tool names if in doubt.\n\n## Importing or Moving Skills\n\nWhen importing a skill (from another repo, a teammate, or the internet) or moving one between locations:\n\n- Prefer collapsing it to a single SKILL.md. Fold any sibling `mcp.json` into inline `mcpServers` frontmatter and delete the `mcp.json`.\n- A skill that ships `scripts/` or `reference/` must keep its directory layout; move the whole directory intact.\n- Preserve `includeTools` filters; if missing, add them before use.\n- Rename the frontmatter `name` and its parent directory to match your conventions.\n\n### Untrusted skills from the internet\n\nTreat internet-sourced skills as untrusted until reviewed:\n\n- MCP `command` servers run arbitrary local processes when Amp discovers the skill at startup, before the skill is triggered. Read every server spec before adding the skill.\n- Inspect `command`, `args`, `env`, and any bundled scripts for anything unexpected (network calls, credential access, install steps).\n- Always set `includeTools` to the minimum the skill needs.\n\n## Skill Locations\n\nSkills are discovered from:\n- `.agents/skills/` in the workspace (project-specific)\n- `~/.config/agents/skills/` globally (user-wide)\n- `~/.agents/skills/` globally (legacy user-wide)\n- `~/.config/amp/skills/` globally for backwards compatibility\n- Global User/Workspace Skills repositories (managed, loaded from the server)\n\nRepository- or app-specific skills belong in `.agents/skills/`, checked in with\nthe project's code. Global skills may bundle text files (`scripts/`,\n`reference/`) alongside `SKILL.md`; binary files are not served, so skills\nwith binary assets stay in a project repo.\n\nIn an Amp sandbox/orb, local skill directories do not outlive the sandbox, so a\npersistent skill belongs in a global repository (or `.agents/skills/` when it is\nrepo-specific).\n\nTo install a skill into the local global directory from an existing source, use\n`amp skill add --global <source>` where the source is `@user/skill`,\n`owner/repo`, a git URL, or a local path (`--name`, `--overwrite`). Global\nUser/Workspace skills are managed through their repository instead.\n\nInspect an Amp personal skill URL with `read_web_page`. If it is inaccessible, say\nit may be private or unavailable. If the current user message explicitly asks to\nimport it, clone the requested User or Workspace Skills repository. Then run\n`amp skills import <url> --repository <clone-directory>`.\nThis downloads and verifies the revision-pinned files but does not commit or push.\nFor an explicit update request, run `amp skills update <name>` in the clone, never\nto check or compare.\nUse `--force` only when explicitly asked to replace committed changes.\nReview the imported files, then use the signed global repository commit and push\nworkflow below. Only a workspace admin can push a Workspace import, and the\nWorkspace scope must be writable. A personal skill with the same name continues\nto take precedence for that user until it is deleted.\n\nFor a named member, use `list_workspace_members`, then\n`find_shared_plugins_and_skills` with their `userID`, the kind, and the name.\n\n## Global User/Workspace Skills\n\nDiscover global repositories with `amp skills repositories`. It lists each scope\n(User/Workspace) with its clone URL and `amp clone` command, whether it has any\nskills yet, and whether the user can write it. If the command is unavailable or\nlists no repositories, only the local directories are available. A scope with no\nskills yet is simply empty — do not present it as a missing or uncreated\nrepository; the repository is created automatically on the first push.\n\nWriting to a global skills repository:\n- Each skill is a top-level `<skill-name>/` directory with `SKILL.md` directly\n inside it; the directory name must match the frontmatter `name`. Text files\n bundled under the skill directory (`scripts/`, `reference/`) are served too.\n Binary files are not: a skill containing one is not loaded. A skill that\n needs MCP tools can use inline `mcpServers` frontmatter or a sibling\n `mcp.json`.\n- Default an unqualified request to the User Skills scope; use a Workspace scope\n only when the user asks for it and its repository entry is writable. Workspace\n pushes affect all teammates and require workspace admin permission.\n- Work in the canonical clone directory, never in the repo root:\n `~/.cache/amp/repositories/<host>-<scope>-skills` (host from the clone URL,\n e.g. `~/.cache/amp/repositories/ampcode.com-user-skills`). If the directory\n already holds a clone, reuse it: fetch and reset to the remote's main branch\n instead of re-cloning. Otherwise clone the repository there with the\n `amp clone` command that `amp skills repositories` prints, passing the cache\n directory as the target (it handles authentication), add the skill, and\n commit. If the scope has no skills yet (no repository to clone), `git init`\n the directory, run\n `git config credential.helper '!amp git-credential-helper'`, commit, then add\n the clone URL as the `origin` remote (create-on-first-write creates it on\n the first push).\n- Do not push unless the user explicitly asks or confirms; a push makes the\n skill visible to other threads, users, or workspaces. A new skill or a change\n to an existing one only takes effect after a push (Amp loads global skills\n from the server, not the local clone), so after committing, offer to push,\n naming the destination repository (for example \"Ready to push this to your\n global user skills repository so it takes effect?\") — never a bare \"Want me\n to push it?\", a destination-less \"committed\" or \"not pushed\", or git\n mechanics. After a confirmed push, tell the user where the skill was pushed\n and when it takes effect (new threads pick it up automatically; the current\n session after `reload_skills`).\n- Updating or deleting a global skill works the same way: change or remove its\n `<skill-name>/` directory in the repository clone, commit, and (with\n confirmation) push. Never edit or delete `~/.cache/amp/global-skills/`; it is\n a read-only materialized cache that re-syncs from the server, so changes there\n are silently undone.\n\n## Reloading Skills\n\nAfter creating, editing, deleting, or (for global skills) pushing a skill, use\nthe `reload_skills` tool so the running session picks up the change — no Amp\nrestart needed. It rescans local skill directories and re-syncs global skills\nfrom the server. Shell commands like `amp skills list` do not reload the\nrunning session.\n\n\nARGUMENTS: Assess and, only if justified by the locked protocol, create or edit a local Brunch steering skill. Load before touching skill files.\n\nBase directory for this skill: builtin:///skills\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -**Tool Result:** `TU-034Cgr62prWoQAmOIZRF6K` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-domain-modeling\">\n# ds-domain-modeling Skill\n\n\n<!-- Vendored from mattpocock/skills@8b78b53: skills/engineering/domain-modeling -->\n\n# Domain Modeling\n\nActively build and sharpen the project's domain model as you design. This is the *active* discipline — challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `CONTEXT.md` for vocabulary is not this skill — that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.)\n\n## File structure\n\nMost repos have a single context:\n\n```\n/\n├── CONTEXT.md\n├── docs/\n│ └── adr/\n│ ├── 0001-event-sourced-orders.md\n│ └── 0002-postgres-for-write-model.md\n└── src/\n```\n\nIf a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:\n\n```\n/\n├── CONTEXT-MAP.md\n├── docs/\n│ └── adr/ ← system-wide decisions\n├── src/\n│ ├── ordering/\n│ │ ├── CONTEXT.md\n│ │ └── docs/adr/ ← context-specific decisions\n│ └── billing/\n│ ├── CONTEXT.md\n│ └── docs/adr/\n```\n\nCreate files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.\n\n## During the session\n\n### Challenge against the glossary\n\nWhen the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. \"Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?\"\n\n### Sharpen fuzzy language\n\nWhen the user uses vague or overloaded terms, propose a precise canonical term. \"You're saying 'account' — do you mean the Customer or the User? Those are different things.\"\n\n### Discuss concrete scenarios\n\nWhen domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.\n\n### Cross-reference with code\n\nWhen the user states how something works, check whether the code agrees. If you find a contradiction, surface it: \"Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?\"\n\n### Update CONTEXT.md inline\n\nWhen a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).\n\n`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.\n\n### Offer ADRs sparingly\n\nOnly offer to create an ADR when all three are true:\n\n1. **Hard to reverse** — the cost of changing your mind later is meaningful\n2. **Surprising without context** — a future reader will wonder \"why did they do it this way?\"\n3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons\n\nIf any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).\n\n\nARGUMENTS: Use precise authority and lifecycle vocabulary for control, spec, decision, evidence, reference, and archive documents.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-domain-modeling\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-domain-modeling/ADR-FORMAT.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-domain-modeling/CONTEXT-FORMAT.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-domain-modeling/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -The authority split I’m aiming to validate is: - -- `STEERING` — current strategic state under constraint -- `COORDINATION` — issue-graph roots, dependencies, and mechanical sequencing -- specs — required behavior -- ADRs — durable decisions and rationale -- evidence — observed proof and promoted regression oracles -- reference — stable explanatory material -- archive — historical context with no current authority - -I’m commissioning three independent audits now: document lifecycle/topology, agent guidance, and the steering routine/gaps. No files will move until those inventories agree on authority and inbound links. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY AUDIT. Work in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify, stage, or commit anything. The user wants a role-based documentation topology replacing docs/planning/<effort>, plus a stale-to-archive/delete pass. Read CONTEXT.md, docs/INDEX.md, docs/agents/documentation.md, all current docs file paths and enough content to classify them, plus inbound Markdown links/references. Also inspect git status and recent history so you do not confuse concurrent edits with committed baseline. Deliver: (1) current document inventory grouped by current authority/role, (2) per-file keep/move/archive/delete recommendation with concise rationale, (3) exact proposed destination for every move, (4) inbound links/index/guidance that must be repaired, (5) documents whose currentness or external references make deletion unsafe, (6) a compact final role-based tree. Treat 'effort' as metadata, not a filesystem ordering principle. Preserve docs/inbox as transient intake unless evidence shows a better role. Flag any ambiguity that materially blocks safe execution. Report exact paths.", - "description": "Classify Brunch documents" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY AUDIT. Work in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify, stage, or commit anything. Audit AGENTS.md, docs/agents/*.md, .agents/skills/** if present, docs/INDEX.md, and relevant package scripts/tests for a migration to role-based docs and a recurring strategic STEERING protocol. Important: docs/agents/git-workflow.md, issue-writing.md, legibility.md, docs/planning/_shared/STEERING.md, and hash-monorepo-import-plan.md have concurrent uncommitted edits; inspect but do not propose overwriting unrelated changes. Deliver: (1) current guidance authority map and contradictions/duplication, (2) exact files/sections that must change for new topology and steering loop, (3) recommendation whether a local skill is justified versus a protocol only, applying single-source-of-truth/progressive-disclosure principles, (4) minimal trigger wording for AGENTS.md or skill metadata, (5) verification commands/tests relevant to docs/guidance/skills, (6) any files better archived or deleted. Use the desired authority split: STEERING strategic state; COORDINATION issue graph; specs behavior; ADR decisions; evidence observed proof/oracles; reference stable explanation; archive historical context.", - "description": "Audit agent guidance" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY AUDIT. Work in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify, stage, or commit anything. Read current docs/planning/_shared/STEERING.md (including worktree diff), COORDINATION.md, docs/agents/arc-close.md, documentation.md, legibility.md, posture.md, issue-tracker.md, and related active planning docs. Evaluate the proposed recurring loop: orient -> choose -> execute -> reconcile -> replan. Responsibilities under consideration: classify facts/beliefs/unknowns/external gates; select frontiers by objective/risk/information gain rather than merely unblocked work; define proof bundle and cut line; map supporting issues/gaps; implement through production wiring; create runnable proof plus immutable legibility snapshot and preferably human witness; promote validated claims into regression oracles; deposit decisions/spec/strategy/sequence/evidence in their authoritative homes; archive stale docs; update confidence only from evidence; replan on changed pressure, failed proof, frontier completion, external decision, or arc close; require approval before external issue writes. Deliver: (1) recovered routine as the smallest complete operating protocol, (2) gaps/overlap/contradictions versus current docs, (3) exact invocation triggers and completion criteria, (4) what belongs in STEERING template versus agent protocol/skill versus arc-close, (5) concrete edits/moves needed to lock it without duplicating meaning. Account for cold-start benchmark/contingency lane, corpus->fixture->run snapshot->oracle lifecycle, human witness/legibility, and external-gate watching.", - "description": "Audit steering protocol" -} -``` - -## User - -**Tool Result:** `TU-034CgsbG8eG51ZUuU2Nisb` - -``` -## Audit basis - -Read `CONTEXT.md`, `docs/INDEX.md`, `docs/agents/documentation.md`, every documentation path/title/status, internal Markdown link graph, `git status`, diffs, and recent documentation history. - -Working tree already has unrelated edits in: - -- `docs/agents/git-workflow.md` -- `docs/agents/issue-writing.md` -- `docs/agents/legibility.md` -- `docs/planning/_shared/STEERING.md` -- `docs/planning/_shared/hash-monorepo-import-plan.md` - -The first three add guidance; the latter changes are formatting/Graphite wording. I made no changes. - -## 1. Current inventory by authority/role - -- **Domain authority:** `CONTEXT.md` -- **Index:** `docs/INDEX.md` -- **Accepted decisions:** `docs/adr/*.md` -- **Operating guidance:** `docs/agents/*.md` -- **Primary specification:** `docs/spec.md` -- **Living strategy/control:** `docs/planning/_shared/{STEERING,COORDINATION,SPEC-LEDGER}.md` -- **Architecture/reference aids:** `docs/planning/_shared/{topology,flue-architecture-cheatsheet}.md` -- **Completed migration plan:** `docs/planning/_shared/hash-monorepo-import-plan.md` -- **Active/provisional specifications:** process-model `ir-*`, `plugin-contract-spec.md`, `petrinaut-integration-spec.md` -- **Research:** process-model `research/*`, research-pattern audit, and `docs/reference/*` -- **Experimental evidence:** process-model `baseline/**`, spikes, implementation records -- **Engineering audits/records:** process-model deep reads and `planning/legibility-sweep/*` -- **Historical planning:** `docs/history/planning/elicitation-kernel/**` -- **Superseded records:** demo-vehicle recommendation and open-questions local copy -- **Transient intake:** `docs/inbox/SDCPNs/**` - -## 2–3. Recommendations and exact destinations - -Paths ending in `/**` preserve their relative contents exactly. - -### Keep in place - -- `CONTEXT.md` -- `docs/INDEX.md` -- `docs/agents/*.md` -- `docs/adr/*.md` -- `docs/reference/**` -- `docs/inbox/SDCPNs/**` - -These already express role rather than effort. Keep the inbox transient pending settlement/conformance. - -### Specifications and architecture - -| Current path | Recommendation / destination | Rationale | -|---|---|---| -| `docs/spec.md` | `docs/specifications/elicitation-kernel.md` | Primary product contract | -| `docs/planning/process-model-elicitation/ir-design.md` | `docs/specifications/intermediate-representation.md` | Ratified design | -| `docs/planning/process-model-elicitation/ir-design-plain.md` | `docs/specifications/intermediate-representation-plain.md` | Companion rendering | -| `docs/planning/process-model-elicitation/plugin-contract-spec.md` | `docs/specifications/plugin-contract.md` | Provisional specification | -| `docs/planning/process-model-elicitation/petrinaut-integration-spec.md` | `docs/specifications/petrinaut-integration.md` | Active integration contract | -| `docs/planning/process-model-elicitation/capture-store-plain.md` | `docs/architecture/capture-store.md` | Current architectural explanation | -| `docs/planning/_shared/topology.md` | `docs/architecture/topology.md` | Ratified architecture support | -| `docs/planning/_shared/flue-architecture-cheatsheet.md` | `docs/reference/flue-architecture-cheatsheet.md` | Dependency reference, consumed by guidance | - -### Strategy and ledgers - -| Current path | Destination | -|---|---| -| `docs/planning/_shared/STEERING.md` | `docs/strategy/STEERING.md` | -| `docs/planning/_shared/COORDINATION.md` | `docs/strategy/COORDINATION.md` | -| `docs/planning/_shared/SPEC-LEDGER.md` | `docs/ledgers/SPEC-LEDGER.md` | - -All remain living documents; effort is metadata in their headers/index rows. - -### Research and meeting records - -| Current path | Destination | -|---|---| -| `docs/planning/process-model-elicitation/research/elicitation-strategy-literature.md` | `docs/research/elicitation/elicitation-strategy-literature.md` | -| `docs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md` | `docs/research/elicitation/interviewing-literature-source-catalog.md` | -| `docs/planning/process-model-elicitation/research/petrinaut-survey.md` | `docs/research/petrinaut-survey.md` | -| `docs/planning/process-model-elicitation/research/voice-feasibility.md` | `docs/research/voice-feasibility.md` | -| `docs/planning/process-model-elicitation/notes/research-patterns-audit.md` | `docs/research/elicitation/research-patterns-audit.md` | -| `docs/planning/process-model-elicitation/notes/expert-meeting-findings-2026-08-11.md` | `docs/records/meetings/expert-meeting-findings-2026-08-11.md` | - -### Evidence - -| Current path | Destination | -|---|---| -| `docs/planning/process-model-elicitation/baseline/**` | `docs/evidence/experiments/baseline-control/**` | -| `docs/planning/process-model-elicitation/ir-worked-examples.md` | `docs/evidence/design-validation/ir-worked-examples.md` | -| `docs/planning/process-model-elicitation/spikes/fe-1434-suspension-verdict-2026-08-19.md` | `docs/evidence/spikes/fe-1434-suspension-verdict-2026-08-19.md` | -| `docs/planning/process-model-elicitation/spikes/fe-1434-suspension-evidence-2026-08-19.json` | `docs/evidence/spikes/fe-1434-suspension-evidence-2026-08-19.json` | -| `docs/planning/process-model-elicitation/adapter-panel-spike-2026-08-19.md` | `docs/evidence/spikes/fe-1435-adapter-panel-2026-08-19.md` | -| `docs/planning/process-model-elicitation/transport-aisdk-implementation-2026-08-19.md` | `docs/evidence/implementations/fe-1436-transport-aisdk-2026-08-19.md` | -| `docs/planning/process-model-elicitation/ask-return-implementation-2026-08-19.md` | `docs/evidence/implementations/fe-1449-ask-return-2026-08-19.md` | -| `docs/planning/process-model-elicitation/notes/deep-read-fe-1389.md` | `docs/evidence/audits/deep-read-fe-1389.md` | -| `docs/planning/process-model-elicitation/notes/deep-read-fe-1390.md` | `docs/evidence/audits/deep-read-fe-1390.md` | -| `docs/planning/legibility-sweep/flue-patterns-audit-2026-08-17.md` | `docs/evidence/audits/flue-patterns-audit-2026-08-17.md` | -| `docs/planning/legibility-sweep/flue-entry-projection-source-read-2026-08-18.md` | `docs/evidence/audits/flue-entry-projection-source-read-2026-08-18.md` | - -### Archive - -| Current path | Destination | Rationale | -|---|---|---| -| `docs/history/planning/elicitation-kernel/**` | `docs/archive/elicitation-kernel/**` | Closed map, resolved tickets and assembly records | -| `docs/planning/_shared/hash-monorepo-import-plan.md` | `docs/archive/migrations/hash-monorepo-import-plan.md` | FE-1437 executed; document itself says to settle afterward | -| `docs/planning/legibility-sweep/issue-pr-migration-2026-08-20/**` | `docs/archive/migrations/issue-pr-legibility-2026-08-20/**` | Completed migration, but rollback/hash evidence must remain intact | -| `docs/planning/legibility-sweep/refactor-queue-2026-08-14.md` | `docs/archive/engineering/legibility/refactor-queue-2026-08-14.md` | Executed queue | -| `docs/planning/legibility-sweep/remediation-plan-2026-08-17.md` | `docs/archive/engineering/legibility/remediation-plan-2026-08-17.md` | Historical ledger | -| `docs/planning/legibility-sweep/review-remediation-2026-08-18.md` | `docs/archive/engineering/legibility/review-remediation-2026-08-18.md` | Settled execution record | -| `docs/planning/process-model-elicitation/recommendation-demo-vehicle.md` | `docs/archive/decisions/superseded/recommendation-demo-vehicle.md` | Explicitly superseded by ADR-0004 | -| `docs/planning/process-model-elicitation/notes/open-questions-elicitation-design-2026-08-11.md` | `docs/archive/external-snapshots/open-questions-elicitation-design-2026-08-11.md` | Local copy superseded by Notion | -| `docs/planning/process-model-elicitation/notes/expert-meeting-prep-2026-08-11.md` | `docs/archive/meetings/expert-meeting-prep-2026-08-11.md` | One-off completed preparation | -| `docs/planning/process-model-elicitation/notes/grilling-inputs-2026-08-12.md` | `docs/archive/planning-inputs/grilling-inputs-2026-08-12.md` | Dated carryover record | -| `docs/planning/process-model-elicitation/notes/penciled-directions-2026-08-14.md` | `docs/archive/planning-inputs/penciled-directions-2026-08-14.md` | Dated pre-ticket list | - -### Delete - -- `docs/inbox/.gitkeep` only. The inbox is no longer empty. -- No substantive document is presently safe to delete. - -## 4. Links and guidance requiring repair - -Mandatory direct repairs: - -- `README.md` links to `docs/spec.md` and `docs/INDEX.md`. -- `docs/INDEX.md`: every moved path, role heading, status, digest, and the obsolete “Path migration note”. -- `docs/agents/documentation.md`: entire zone model, ingest settlement destinations, control-vs-record language, effort-close policy. -- `docs/agents/arc-close.md`: references to `docs/planning/<effort>`, `_shared`, `SPEC-LEDGER`, and `COORDINATION`. -- `docs/agents/issue-tracker.md`: repository artifact locations, `_shared/COORDINATION`, and historical planning paths. -- `docs/agents/issue-writing.md`: long-form artifact location. -- `docs/agents/domain.md`: proposed role tree. -- `docs/agents/flue-routing.md`: links to cheatsheet and Flue audits. -- `docs/adr/0001`–`0004`: links to specification, topology, IR, integration spec, and superseded recommendation. -- `docs/strategy/COORDINATION.md` and `STEERING.md`: mutual links and import-plan reference. -- All relative links inside the moved elicitation-kernel archive, baseline experiment, research set, migration package, and implementation/spike records. -- `docs/architecture/topology.md`: hard-coded `docs/planning/**/baseline/run.ts`. -- Migration package `README.md`: validator command paths. -- Tests enforcing topology/index coverage, notably `packages/core/test/architecture/docs-index.test.ts`. - -Historical prose containing obsolete but evidentiary paths should generally remain verbatim and receive a migration note rather than silent rewriting: - -- `docs/reference/amp-analysis-flue-vs-tilde.md` -- archived Linear/GitHub migration snapshots -- completed elicitation-kernel tickets -- old `brunch-lite` repository references - -External Linear descriptions also contain many literal `docs/planning/...` paths. The documentation protocol says those references must be updated before deletion of the old paths. - -## 5. Unsafe deletions - -Do not delete: - -- Any `docs/reference/**` source: several are direct spec/research evidence; the PDF and transcript have irreplaceable provenance. -- `docs/inbox/SDCPNs/**`: added on 2026-08-24 and explicitly awaiting corpus settlement. -- Elicitation-kernel history: mirrored from FE-1366–1379 and linked by the specification/research. -- Migration package: canonical hashes, source snapshots, proposals, validator, apply log, and rollback evidence. -- Superseded recommendation: ADR-0004 explicitly cites it. -- Open-questions snapshot: Notion is canonical, but local research links to it and external availability is not guaranteed. -- Flue audits/cheatsheet: current agent guidance consumes them and they pin dependency-version behavior. -- `SPEC-LEDGER.md`: still contains many partial/pending obligations. -- Any active process-model specification or evidence record without first checking current Linear state. - -## 6. Compact target tree - -```text -CONTEXT.md -docs/ -├── INDEX.md -├── agents/ -├── adr/ -├── inbox/ -├── strategy/ -├── ledgers/ -├── specifications/ -├── architecture/ -├── research/ -├── reference/ -├── evidence/ -│ ├── audits/ -│ ├── design-validation/ -│ ├── experiments/ -│ ├── implementations/ -│ └── spikes/ -├── records/ -│ └── meetings/ -└── archive/ - ├── decisions/superseded/ - ├── elicitation-kernel/ - ├── engineering/legibility/ - ├── external-snapshots/ - ├── meetings/ - ├── migrations/ - └── planning-inputs/ -``` - -## Material ambiguity - -Safe execution is blocked on one point: `docs/INDEX.md` still labels several August legibility and process-model records “active,” while their text or owning work appears completed. Linear is canonical for issue state, so archive decisions for FE-1419, FE-1383, FE-1401, FE-1434–1436, and FE-1449 should be confirmed against Linear immediately before moving. The role destinations themselves are unaffected. -``` - -**Tool Result:** `TU-034CgsbGWpOm9v5mhT8Q8F` - -``` -## Audit outcome - -Read-only audit completed. No files were modified, staged, or committed. Existing uncommitted edits were preserved. - -## 1. Current authority map and conflicts - -| Authority | Current source | Finding | -|---|---|---| -| Strategic state | `docs/planning/_shared/STEERING.md` | Correctly declares objectives, bets, proof frontiers, cuts, beliefs, and replan conditions. No recurring protocol currently ensures it is revisited. | -| Issue graph / sequencing projection | Linear + `COORDINATION.md` | Linear correctly owns state, hierarchy, and blockers. `COORDINATION.md` appropriately owns soft edges and graph projection, but its prose still duplicates strategic sequencing from `STEERING.md`. | -| Behavior | `docs/spec.md`, effort specs, `SPEC-LEDGER.md` | Specs correctly own obligations. The ledger owns obligation status/evidence, not behavior itself. | -| Decisions | `docs/adr/*.md` | Clear and appropriately protected from silent steering overrides. | -| Evidence / oracles | Scattered through `docs/planning/**`, tests, spike records | No role-based home. “Research,” “notes,” implementation records, spike evidence, and verification artifacts are mixed with planning. | -| Stable explanation | `docs/reference/**`, `CONTEXT.md`, some `_shared` documents | `CONTEXT.md` correctly owns glossary. Some stable explanatory documents remain in `_shared` because placement is lifecycle-based rather than role-based. | -| Historical context | `docs/history/**`, completed records still under `docs/planning/**` | Split authority: documentation protocol says completed effort artifacts remain permanently in `planning`, while a historical tree also exists. | - -### Principal duplication and contradictions - -1. **Strategic recommendation is stated twice.** - `STEERING.md` §“Current choice” and `COORDINATION.md` §“Current sequencing recommendation” both describe what should happen next. Coordination should only translate the selected strategy into issue edges/frontier availability. - -2. **Arc close omits strategic reconciliation.** - `docs/agents/arc-close.md` reassesses `COORDINATION.md` when sequencing changes, but never reassesses `STEERING.md` when evidence changes the objective, proof spine, authority boundary, cut line, belief, or replan condition. - -3. **Documentation placement is lifecycle-based, not authority-based.** - `documentation.md` puts nearly all working artifacts under `docs/planning/<effort>/`. This conflicts with the requested split between specifications, evidence, reference, and archive. - -4. **Completed-material policy is inconsistent.** - `documentation.md` says completed effort artifacts stay under `docs/planning/<effort>/`; `docs/history/planning/**` already exists. `INDEX.md` consequently mixes active state and historical records. - -5. **The import plan is no longer a live control surface.** - `hash-monorepo-import-plan.md` says it remains active until FE-1437 lands, while `COORDINATION.md` says the authority threshold was crossed on 2026-08-21. It should become archived evidence/history after its concurrent correction lands. - -6. **AGENTS.md exposes protocols as an undifferentiated list.** - “Read the corresponding protocol” plus a complete filename inventory provides reachability but weak routing. It does not say when to run strategic steering. - -7. **Skill/protocol indirection is circular but workable.** - AGENTS says run `arc-close`; the skill says read the protocol; the protocol says load the skill. The procedure still has one source, but the protocol should not instruct loading its own wrapper. - -8. **Skill copies are duplicated.** - `.agents/skills/arc-close/SKILL.md` and `.claude/skills/arc-close/SKILL.md` are parallel copies. Unless both discovery locations are mandatory, one should be generated/symlinked or explicitly treated as a compatibility mirror. - -9. **Stale external skill names appear in guidance.** - `issue-tracker.md` refers to `tool-linear-cli` and `ds-wayfind`, neither present locally. Either point to available canonical tooling/skills or make the protocol self-contained. - -## 2. Files and sections that must change - -### Required control/protocol changes - -- **`AGENTS.md`** - - Replace the flat protocol list with role/trigger routing. - - Add the steering trigger. - - Point to the new role-based documentation topology. - - Retain explicit arc-close and Flue design triggers. - -- **`docs/agents/documentation.md`** - - Replace **“Zones”**, **“Control surface vs record”**, **“Ingest protocol”**, and **“Effort completion”**. - - Define the desired authorities: - - `STEERING`: current strategic state - - `COORDINATION`: issue graph projection - - specs: behavior - - ADRs: decisions - - evidence: observed proof and oracles - - reference: stable explanation - - archive: historical context - - State promotion rules and link-not-copy rules. - -- **`docs/agents/arc-close.md`** - - Add a conditional steering reconciliation step. - - Narrow coordination reconciliation to issue projection. - - Clarify that evidence changes update evidence records first, then affected spec/ADR/steering authorities. - - Remove “load the skill” from the canonical protocol to eliminate the circular instruction. - -- **`docs/agents/issue-tracker.md`** - - Remove strategic sequencing from map/coordination semantics. - - Define `COORDINATION.md` solely as graph projection and exceptional-root authority. - - Repair stale skill/tool references. - -- **`docs/agents/legibility.md`** - - Update **“Consolidation”** so each finding deposits into exactly one authority: specification, ADR, evidence, reference, steering, coordination, or archive. - - Preserve the concurrent “point finding” addition. - -- **`docs/INDEX.md`** - - Reorganize sections by role rather than by `planning/<effort>` location. - - Update every moved path/status. - - Keep archive summaries compact rather than indexing historical files as live planning. - -### Required control-document changes - -- **`docs/planning/_shared/STEERING.md`** - - Preserve its substantive current model. - - Add only the recurring steering-pass contract: inputs, triggers, outputs, and reconciliation order. - - Remove issue-graph detail that belongs solely in coordination. - - Do not disturb the current formatting-only uncommitted edits. - -- **`docs/planning/_shared/COORDINATION.md`** - - Remove duplicated strategic rationale and cuts. - - Retain issue nodes, hard/soft edges, exceptional roots, unresolved graph seams, and links back to the selected steering frontier. - -- **`docs/planning/_shared/SPEC-LEDGER.md`** - - Move under the specifications role or clearly identify it as a temporary evidence/status companion to the canonical spec. - - Archive when its declared milestone closes. - -- **`docs/planning/_shared/topology.md`** - - Classify either as: - - stable architecture explanation → reference; or - - enforceable architecture decision → ADR/spec. - - Its current “verification and specification” combination spans two authorities and should be split or assigned decisively. - -- **`docs/planning/_shared/flue-architecture-cheatsheet.md`** - - Move to reference; it is stable explanatory consolidation, not planning state. - -- **`docs/planning/_shared/hash-monorepo-import-plan.md`** - - After concurrent edits settle, archive it as completed migration history/evidence. - - Update `INDEX.md` and links from `COORDINATION.md`. - - Do not rewrite its unrelated Graphite correction. - -### Mechanical test changes - -- **`packages/core/test/architecture/docs-index.test.ts`** - - Replace the current `planning/`-only placement assertion with role-directory assertions. - - Continue enforcing: - - every governed document is indexed, - - every index target resolves, - - relative links resolve, - - every protocol is reachable from `AGENTS.md`. - - Add checks for exactly one live `STEERING` and `COORDINATION` authority. - - Optionally enforce prohibited role mixing only where mechanically decidable. - -## 3. Local skill recommendation - -**Use a protocol first; do not add a steering skill yet.** - -`STEERING.md` already explicitly says not to extract a generic skill until a second real planning cycle demonstrates recurrence. That is sound single-source-of-truth discipline. - -Recommended arrangement: - -- Canonical procedure: `docs/agents/steering.md` -- Current strategic state: `STEERING.md` -- Minimal AGENTS trigger routes agents to the protocol. -- Add a tiny wrapper skill only after repeated failures show that automatic discovery is needed. - -A future skill should contain only metadata and “read and execute `docs/agents/steering.md`,” matching `arc-close`; it must not duplicate the loop. - -## 4. Minimal trigger wording - -For `AGENTS.md`: - -> When new evidence may change the current objective, proof frontier, strategic bet, cut line, or replan condition, follow `docs/agents/steering.md` and reconcile `STEERING.md` before changing issue sequencing. - -If a wrapper skill later proves necessary: - -```yaml -description: Reassess Brunch strategy when evidence may change its objective, proof frontier, strategic bet, cuts, or replan conditions. -``` - -## 5. Relevant verification - -Focused documentation/guidance checks: - -```bash -yarn workspace @hashintel/brunch-agent test:unit -- \ - test/architecture/docs-index.test.ts -``` - -Or through Turbo: - -```bash -turbo run test:unit --filter '@hashintel/brunch-agent' -``` - -Issue graph projection: - -```bash -turbo run linear:graph --filter '@hashintel/brunch-agent' -``` - -Formatting check scoped to changed documentation: - -```bash -yarn oxfmt --check \ - libs/@hashintel/brunch-agent/AGENTS.md \ - libs/@hashintel/brunch-agent/docs \ - libs/@hashintel/brunch-agent/.agents/skills -``` - -Relevant package stack if the architecture test changes: - -```bash -turbo run lint:eslint lint:tsc test:unit build \ - --filter '@hashintel/brunch-agent' -``` - -Also run `git diff --check` on the affected paths. There is currently no dedicated skill-schema test; adding one is only justified if more local skills appear. - -## 6. Archive/delete candidates - -### Archive - -- `docs/planning/_shared/hash-monorepo-import-plan.md` — completed migration record. -- `docs/planning/_shared/SPEC-LEDGER.md` — when milestone one closes. -- Completed implementation/spike/remediation records currently marked `active`. -- `recommendation-demo-vehicle.md` and other superseded effort records, preserving links and provenance. -- Completed legibility-sweep material as one archived effort set. - -### Move, not archive - -- `flue-architecture-cheatsheet.md` → reference. -- Stable research syntheses still used as explanation → reference. -- Machine-readable proof, spike transcripts, and oracle reports → evidence. -- Behavior-defining `*-spec.md` documents → specs. - -### Delete only after reference repair - -- Redundant `.agents`/`.claude` skill mirror if both discovery paths are not required. -- Superseded notes that add no evidence beyond their canonical successor. -- Obsolete external-skill references in prose rather than creating compatibility stubs. - -No immediate deletion should occur before `INDEX.md`, Linear links, and document consumers are checked. -``` - -**Tool Result:** `TU-034CgsbI5PKchEicDYQFFD` - -``` -# Read-only audit - -No files were modified, staged, or committed. The `STEERING.md` worktree diff is formatting-only; its substantive steering model is already present. - -## 1. Smallest complete recurring protocol - -### Orient - -1. Read the current objective and constraints from `STEERING.md`. -2. Refresh mechanical facts from Linear, `COORDINATION.md`, relevant specs/ADRs, and production code. -3. Classify each material statement: - - **fact** — directly evidenced; - - **belief** — confidence plus supporting evidence; - - **unknown** — named question and cheapest resolving probe; - - **external gate** — owner, expected decision, watch method, and consequence. -4. Detect changed pressure: objective, deadline, authority boundary, failed proof, completed frontier, or external decision. - -### Choose - -5. Rank candidate frontiers by: - - contribution to the current objective; - - risk retired; - - information gained; - - dependency/authority pressure; - - cost and reversibility. - - “Open and unblocked” supplies candidates; it does not choose the next move. -6. Select one frontier or a deliberately joined pair. -7. State: - - the claim being tested; - - the **proof bundle**; - - the **cut line**; - - supporting issues/gaps; - - stop and replan conditions. - -### Execute - -8. Build through the production entrypoint and real wiring. Fixtures may seed state but must not supply wiring or truth that production is supposed to produce. -9. Follow the evidence lifecycle: - -```text -source corpus - -> reviewed representative fixture - -> runnable production-path proof - -> immutable dated run snapshot - -> validated invariant - -> executable regression oracle -``` - -10. Produce: - - a rerunnable proof; - - an immutable legibility snapshot in another register; - - preferably a recorded human witness for UX, interpretation, or live-runtime claims. - -### Reconcile - -11. Compare the result with the original claim. Update confidence only from evidence. -12. Deposit each result in its authoritative home: - - architecture/invariants → ADR or specification; - - current strategic judgment → `STEERING.md`; - - sequencing/soft edges/external gates → `COORDINATION.md`; - - issue state/hierarchy/hard blockers → Linear, **only after approval**; - - milestone obligations → `SPEC-LEDGER.md`; - - dated evidence/run snapshots → owning effort directory; - - reusable categorical guarantees → executable tests/gates. -13. Settle capture channels and stale documents under `documentation.md`: repair, mark superseded/settled, or delete only with indexed disposition and repaired references. - -### Replan - -14. Re-run orientation when: - - objective, deadline, or pressure changes; - - proof fails or exposes an authority conflict; - - a frontier completes; - - an external decision lands; - - an arc closes. -15. Otherwise continue the selected frontier; do not rewrite steering for ordinary ticket movement. - ---- - -## 2. Gaps, overlap, and contradictions - -### Already strong - -- `STEERING.md` already owns objective, evidence, strategic bet, frontiers, cut line, beliefs, confidence, and replan conditions. -- It explicitly rejects “unblocked means next,” test-injected wiring, fixture-shaped false proof, and hidden projection inference. -- `COORDINATION.md` correctly separates strategic judgment from Linear’s hard facts. -- `legibility.md` already requires alternate-register rendering, strain findings, consolidation, and promotion of recurrent findings into executable gates. -- `arc-close.md` already reconciles inbox/index, Linear registry, spec ledger, coordination, and stale planning tense. -- `documentation.md` provides authoritative document placement and disposition rules. -- Existing integration docs demonstrate the intended fixture/snapshot/oracle progression: panel transcript → golden fixture → contract test. - -### Missing or incomplete - -1. **No canonical recurring steering protocol.** - The loop is described in `STEERING.md`, but its operating rules are scattered across steering, legibility, documentation, issue tracking, and arc close. - -2. **No standard proof-bundle contract.** - Frontiers have prose proofs, but there is no required bundle covering production entrypoint, rerun command, expected observation, immutable snapshot, witness status, and oracle-promotion decision. - -3. **No explicit corpus lifecycle.** - `docs/INDEX.md` now identifies an inbox SDCPN corpus, while `STEERING.md` asks to freeze a fixture. Nothing defines selection, conformance review, fixture provenance, snapshot retention, or oracle promotion. - -4. **Cold-start contingency is cut, not preserved as a lane.** - `STEERING.md` properly excludes full cold-start implementation, but Dora’s external decision can reverse the scenario. A bounded contingency lane is needed: preserve a benchmark and representative cold-start fixture without making it a delivery prerequisite. - -5. **Human witness is precedent, not policy.** - Historical HITL evidence was highly productive, but current legibility guidance does not say which claims require witness, how acceptance is recorded, or that automated proof and human witness are complementary. - -6. **External-gate watching is under-specified.** - Dora’s decision and branch-on-main gates are visible, but gates lack watcher, cadence/event source, stale threshold, and consequence. Agents could either ignore them or repeatedly poll without purpose. - -7. **Approval-before-external-write is not general.** - Current guidance allows issue repair and creation in several places. The proposed rule requires an explicit general gate before issue creation, parent changes, comments, state changes, or project updates. - -8. **Confidence vocabulary lacks transition rules.** - `STEERING.md` records confidence well, but there is no rule requiring a cited evidence delta for every confidence change. - -### Direct contradiction - -`issue-tracker.md` defines the wayfinder frontier as: - -> open, unblocked, unassigned sub-issues — lowest issue number first - -`STEERING.md` defines strategic frontier selection by objective, learning dependency, and risk rather than ticket availability. These are different meanings of **frontier**. - -**Resolution:** rename the tracker concept to **claimable queue** or **mechanical frontier**. Reserve **proof frontier** for steering. Mechanical availability filters candidates; steering selects work. - -### Lifecycle tension - -`documentation.md` says effort artifacts are permanent and generally stay in place; “archive stale docs” could imply moving or deleting them. - -**Resolution:** define archive operationally as disposition, not blanket relocation: - -- stale active control surface → repair or supersede; -- completed effort artifact → mark settled; -- replaced canon → mark superseded and link successor; -- deletion only after indexed disposition and reference repair; -- move to `docs/history/` only for a whole intentionally frozen historical effort. - -### Posture inconsistency - -The operative posture exists at `docs/agents/posture.md`, while the broader posture protocol expects `memory/POSTURE.md`. Within Brunch, `AGENTS.md` explicitly points to the former, so behavior is recoverable, but tooling may not discover it consistently. - ---- - -## 3. Invocation triggers and completion criteria - -### Steering pass triggers - -Invoke the recurring protocol when: - -- starting or resuming an arc without a current proof target; -- the objective, deadline, business use case, or strategic pressure changes; -- a frontier proof succeeds or fails; -- implementation exposes a spec/ADR authority conflict; -- an external gate changes; -- the selected frontier becomes impossible or materially less valuable; -- arc close finds coordination or confidence drift. - -Do **not** invoke it merely because an issue changed state. - -### Execution-pass completion - -A frontier is complete only when: - -1. its named claim has a pass/fail result; -2. the proof runs through production wiring; -3. fixture provenance is recorded; -4. unrelated fixture regions remain stable where required; -5. rerun instructions and expected observations exist; -6. an immutable run snapshot exists; -7. human witness is recorded or explicitly classified unnecessary; -8. reusable guarantees have been promoted to regression oracles; -9. failures and residual unknowns have owners; -10. confidence changes cite the new evidence. - -### Reconciliation completion - -Reconciliation is complete when every changed truth has exactly one authoritative home, affected mirrors point to it, stale active prose is dispositioned, and no chat-only decision remains. - -### External-gate completion - -A gate record is complete when it names: - -- question; -- external owner; -- authoritative source; -- watcher; -- watch trigger or cadence; -- current state and last checked date; -- consequences of each likely outcome. - -No external tracker mutation occurs without explicit approval. - -### Arc-close completion - -Keep the existing six criteria, adding only confirmation that: - -- the steering loop was reconciled if a trigger fired; -- immutable proof and witness records are indexed; -- validated categorical claims were considered for oracle promotion; -- no watched external gate is stale or ownerless. - ---- - -## 4. Correct ownership split - -### `STEERING.md` template - -Own current, project-specific judgment only: - -- objective and pressure; -- known facts; -- beliefs with confidence/evidence; -- unknowns; -- external gates and consequences; -- proof spine; -- current proof frontiers; -- selected frontier; -- proof bundle; -- cut line; -- strategic issue/gap projection; -- replan conditions; -- cold-start contingency lane. - -It should not contain generic operating instructions, archival mechanics, Linear write procedures, or arc-close checklists. - -### Agent protocol / skill - -Own the reusable `orient → choose → execute → reconcile → replan` procedure: - -- classification rules; -- frontier-selection criteria; -- proof-bundle schema; -- production-wiring rule; -- corpus/fixture/snapshot/oracle lifecycle; -- witness requirement; -- confidence-update discipline; -- authoritative-home routing; -- approval gate for external writes; -- external-gate watching mechanics. - -The skill should execute the protocol; the documentation page should remain canonical. - -### `arc-close.md` - -Own the final control pass only: - -- verify the recurring loop’s outputs were deposited; -- reconcile inbox/index, issue registry, ledger, coordination, and planning tense; -- ensure proof/witness snapshots are indexed; -- confirm oracle promotion was handled; -- report conditional passes. - -Arc close should not choose the next frontier or reproduce the steering procedure. - ---- - -## 5. Concrete edits and moves recommended - -1. **Add `docs/agents/steering.md`.** - Make it the canonical minimal protocol above. Link it from `AGENTS.md` and `docs/INDEX.md`. - -2. **Add a compact reusable template section to `STEERING.md`, not a second document.** - Suggested headings: - - Objective and pressure - - Facts / beliefs / unknowns / external gates - - Proof spine - - Current frontiers - - Current choice - - Proof bundle and cut line - - Contingency lane - - Replan conditions - - Preserve the current substantive September content beneath that shape. - -3. **Move generic loop prose out of `STEERING.md`.** - Replace its introductory procedural paragraph with a link to `docs/agents/steering.md`. Keep only “this document carries current judgment.” - -4. **Amend `issue-tracker.md`.** - - Rename its “Frontier” to “Mechanical frontier” or “Claimable queue.” - - State that steering chooses among mechanically available work. - - Add explicit approval before all external writes. - -5. **Amend `legibility.md`.** - - Define immutable legibility snapshot. - - Require a human witness for UX, interpretation, live-runtime, and demo-comprehension claims unless explicitly inapplicable. - - Require witness identity/date/scenario/verdict, not a transcript dump. - - Link recurrent-class findings to oracle promotion in the steering protocol. - -6. **Amend `documentation.md`.** - - Define the corpus → fixture → dated run snapshot → oracle lifecycle and homes: - - corpus: inbox/reference; - - representative fixture: owning effort or test fixtures, with provenance; - - run snapshot: dated immutable effort record; - - oracle: executable test/gate; - - superseded evidence: retained and dispositioned. - - Clarify that “archive” means explicit disposition, not automatic movement. - -7. **Amend `arc-close.md` minimally.** - Add one conditional steering reconciliation check and proof/witness/oracle deposit checks. Do not copy the full loop. - -8. **Add a cold-start contingency block to current `STEERING.md`.** - - one benchmark scenario; - - one representative corpus-derived fixture; - - expected minimum outcome; - - activation condition: Dora confirms creation-from-blank; - - explicit statement that it does not gate review-and-revise until activated. - -9. **Turn the SDCPN inbox corpus into a governed fixture source.** - - inspect and settle the seven files; - - record selection criteria; - - choose one representative fixture; - - preserve source hashes/provenance; - - generate run snapshots rather than editing the baseline; - - promote stable semantic expectations into fold/projection/provenance tests. - -10. **Resolve posture discoverability.** - Either move the declaration to `memory/POSTURE.md` and point Brunch guidance there, or explicitly document `docs/agents/posture.md` as the context-local exception. Avoid two posture authorities. - -The main design is sound. The missing piece is not more planning content; it is one canonical operating protocol joining the already-good steering, proof, legibility, documentation, tracker, and close-out practices without duplicating their meaning. -``` - -## Assistant - -The audits converge on the operating loop and authority split. Two corrections are important before editing: - -1. I’m keeping the previously agreed names (`control`, `specs`, `evidence`, `reference`, `archive`) rather than introducing new top-level synonyms like `strategy`, `specifications`, or `records`. -2. Executable evaluation material should leave `docs/`; `docs/evidence` will hold human-readable snapshots, while `evaluations/{cases,protocols,oracles}` holds runnable assets. - -I’m checking two execution hazards before the move pass: which planned paths are entangled with concurrent edits, and how the existing baseline divides between executable assets and evidence. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY. In /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent, determine which role-topology moves can be executed without modifying or breaking files that currently have foreign uncommitted edits: docs/agents/git-workflow.md, issue-writing.md, legibility.md, docs/planning/_shared/STEERING.md, docs/planning/_shared/hash-monorepo-import-plan.md. Target roles are docs/control, docs/specs, docs/evidence/{proofs,evaluations}, docs/reference, docs/archive, with docs/inbox retained. Inspect the actual worktree diffs and every inbound link from those five files to planned move targets. Return: (1) files that must remain at current paths this pass, (2) move targets whose move must be deferred because one of those foreign-edited files would acquire a broken link, (3) safe moves plus exact link-repair files, (4) whether git mv of a foreign-edited file would alter/claim another actor's work (answer conservatively), (5) external Linear/plain-path references that materially block moves under the current documentation protocol. Do not modify anything.", - "description": "Partition safe document moves" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY. In /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent, inspect docs/planning/process-model-elicitation/baseline/** and all internal consumers/config. The agreed topology is executable materials outside docs under evaluations/{cases,protocols,oracles}; docs/evidence/evaluations holds immutable human-readable run snapshots/reports. Classify every baseline file as reusable case/corpus truth, protocol/runner, oracle/expected claim, or immutable evidence snapshot. Propose exact destination paths with minimal renames and all required import/script/link/config changes. Also state whether adding top-level evaluations/ creates a new architectural unit requiring local guidance or declaration. Do not modify anything.", - "description": "Classify evaluation assets" -} -``` - -## User - -**Tool Result:** `TU-034Cgx5BP31MDifaYdjU7a` - -``` -Read-only inspection completed; no files changed. - -## 1. Must remain at current paths this pass - -These contain foreign uncommitted edits and must not be moved or edited: - -- `docs/agents/git-workflow.md` -- `docs/agents/issue-writing.md` -- `docs/agents/legibility.md` -- `docs/planning/_shared/STEERING.md` -- `docs/planning/_shared/hash-monorepo-import-plan.md` - -Conservatively, `git mv` would claim another actor’s edited content by staging its deletion/addition under new paths. Do not move them until that work is committed or explicitly handed over. - -## 2. Moves deferred by inbound links from those files - -| Planned target | Why deferred | -|---|---| -| `docs/agents/issue-tracker.md` → `docs/control/...` | Referenced twice by foreign-edited `issue-writing.md`. | -| `docs/agents/documentation.md` → `docs/control/...` | Referenced by foreign-edited `legibility.md`. | -| `docs/planning/_shared/COORDINATION.md` → `docs/control/...` | Markdown-linked by both foreign-edited `STEERING.md` and `hash-monorepo-import-plan.md`. | -| `docs/planning/_shared/SPEC-LEDGER.md` → `docs/control/...` | Plain-path reference in foreign-edited import plan. | -| `docs/adr/0004-in-petrinaut-staging-and-the-monorepo-import.md` → `docs/specs/...` | Markdown-linked by foreign-edited import plan. | -| `docs/spec.md` → `docs/specs/...` | Explicit disposition-path reference in foreign-edited import plan. | - -Also defer moving `issue-writing.md` or `legibility.md` indirectly through a wholesale `docs/agents/` move: `git-workflow.md` links both by relative filename. - -## 3. Moves safe relative to the five foreign-edited files - -Subject to repairing ordinary repository links and external tracker paths: - -- Other agent protocols can move to `docs/control/`: - - `arc-close.md`, `domain.md`, `flue-routing.md`, `posture.md`, `triage-labels.md` - - `git-workflow.md`, `issue-writing.md`, and `legibility.md` excluded this pass. -- Other shared controls can move: - - `flue-architecture-cheatsheet.md` - - `topology.md` - - `STEERING.md` and import plan excluded; `COORDINATION.md` and `SPEC-LEDGER.md` deferred as above. -- ADRs 0001–0003 can move to `docs/specs/`; ADR-0004 cannot. -- `docs/history/**` can move to `docs/archive/**`. -- Evidence/research records may move into `docs/evidence/proofs` or `docs/evidence/evaluations`; none is directly linked from the five edited files. -- `docs/reference/**` already occupies its target role. -- `docs/inbox/**` can remain retained as requested. - -Exact link-repair surfaces for any safe move are discoverable repository-wide with `rg`, but always include: - -- `docs/INDEX.md` -- Markdown files returned by searching the old exact path -- `AGENTS.md` for moved agent protocols -- `.agents/skills/arc-close/SKILL.md` and its Claude mirror if `arc-close.md` moves -- `packages/core/test/architecture/docs-index.test.ts` if its zone assumptions change - -## 4. Foreign-edited `git mv` - -**Yes, conservatively it alters/claims another actor’s work.** Although Git can preserve bytes, the move stages the old-path deletion and new-path addition containing their uncommitted edits. That changes ownership and complicates their eventual commit/rebase. - -## 5. External references that materially block moves - -The documentation protocol says a repo path referenced from Linear must not be removed without updating Linear. The preserved Linear snapshots show current or historically load-bearing plain paths to: - -- Agent protocols: `issue-tracker.md`, `issue-writing.md`, `arc-close.md`, `documentation.md`, `flue-routing.md`, `triage-labels.md` -- Shared controls: `topology.md` and the former `CONVERGENCE.md` -- ADRs 0003 and 0004 -- Numerous process-model specs, research, baseline, deep-read, remediation, and evaluation records -- Reference documents - -Therefore these moves are not fully executable merely by repairing repository Markdown. The corresponding live Linear descriptions must be fetched and updated atomically with each move. Historical comments may remain unchanged under precedent, but live issue descriptions and canonical plain-path pointers materially block deletion of the old paths. The checked-in migration snapshots are evidence, not proof of current Linear state; live tracker verification is required before execution. -``` - -**Tool Result:** `TU-034Cgx5BtKahaaHixL8wgl` - -``` -## File classification and destinations - -### Reusable case/corpus truth - -- `opening-message.md` → `evaluations/cases/process-model-elicitation/baseline/opening-message.md` -- `situation-pack.md` → `evaluations/cases/process-model-elicitation/baseline/situation-pack.md` - -### Protocol/runner - -- `protocol.md` → `evaluations/protocols/process-model-elicitation/baseline/protocol.md` -- `v0-prompt.md` → `evaluations/protocols/process-model-elicitation/baseline/v0-prompt.md` -- `run.ts` → `evaluations/protocols/process-model-elicitation/baseline/run.ts` - -### Oracle/expected claim - -None. The situation pack contains hidden case facts, but it is interviewee-side corpus truth—not the separate modeller-side answer key defined by `CONTEXT.md`. The readout is post-run evidence, not a reusable oracle. Leave `evaluations/oracles/` empty until an answer key or machine-readable expectations are authored. - -### Immutable evidence snapshots - -Move unchanged beneath `docs/evidence/evaluations/process-model-elicitation/baseline/`: - -- `readout.md` -- `transcripts/condition-1.md` -- `transcripts/condition-1.raw.json` -- `transcripts/condition-1-model.txt` -- `transcripts/condition-2.md` -- `transcripts/condition-2.raw.json` -- `transcripts/condition-2-model.txt` - -## Required consumer/config changes - -- `packages/core/package.json` - - Change `baseline:run` to: - `node --experimental-strip-types ../../evaluations/protocols/process-model-elicitation/baseline/run.ts` -- `packages/core/test/architecture/baseline-runner.test.ts` - - Replace the old `BASELINE_DIR`. - - Copy protocol and case inputs separately into the isolated fixture. - - Read generated checkpoints from the fixture evidence/output directory. -- `run.ts` - - Resolve `v0-prompt.md` beside the runner. - - Resolve case inputs from `../../../cases/process-model-elicitation/baseline/`. - - Resolve production outputs under `../../../../docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/`. - - Prefer an explicit test-only output-directory environment variable so tests never mutate committed evidence. - - Update usage/output comments and generated metadata paths. -- `protocol.md` - - Relink `opening-message.md` and `situation-pack.md` to `../../../cases/process-model-elicitation/baseline/`. - - Relink `readout.md` and transcript location to `../../../../docs/evidence/evaluations/process-model-elicitation/baseline/`. - - Its research link becomes `../../../../docs/planning/process-model-elicitation/research/re-interviewing-literature-worker-report.md`. -- `readout.md` - - Local `transcripts/` links remain valid after moving the whole evidence bundle together. - - Relink `protocol.md` to the new `evaluations/protocols/...` path. -- `docs/INDEX.md` - - Replace the old `baseline/` planning row with an evidence-bundle row targeting `evidence/evaluations/process-model-elicitation/baseline/`. - - Executable `evaluations/**` materials should not masquerade as indexed documents. -- `docs/planning/_shared/topology.md` - - Replace `docs/planning/**/baseline/run.ts` with `evaluations/protocols/**/run.ts`. -- `packages/core/test/architecture/docs-index.test.ts` - - No semantic change appears necessary; verify that `docs/evidence/**` is accepted and covered by the new index row. -- Type-check/pruned-workspace configuration - - Ensure any existing inclusion/copy rule that formerly covered `docs/planning/**` scripts also includes `evaluations/**`; `workspace.ts` notes that context-root non-workspace paths require explicit prune copying. -- External pointers - - Update the FE-1361 Linear/GitHub destination links if still live. Do **not** rewrite the committed migration JSON snapshots; they are historical evidence. - -## Architectural-unit verdict - -Yes. Top-level `evaluations/` introduces a distinct responsibility and trust topology: reusable cases, executable protocols, and private oracles, separated from immutable evidence. - -No Petrinaut-style `@layerRoot` declaration applies here, and it is not a workspace package. It should receive local guidance—preferably `evaluations/AGENTS.md`—covering: - -- case/protocol/oracle ownership; -- situation-pack versus answer-key information-wall rules; -- prohibition on generated run evidence under `evaluations/`; -- canonical evidence destination under `docs/evidence/evaluations/`; -- runner and verification conventions. - -Read-only inspection only; no files changed or tests run. -``` - -## Assistant - -There are two real blockers to the physical move pass: - -- five source files have another actor’s uncommitted edits, so renaming or editing them would claim that work; -- many old paths are still canonical pointers in Linear, and moving them requires external issue edits that you have not explicitly authorized. - -I won’t route around either with duplicate files or compatibility stubs. I can still lock the protocol now: declare the role topology, prohibit new effort-centric placement, add the steering loop, separate strategic selection from the issue “claimable queue,” and extend arc-close. That leaves the physical migration as a controlled next action once we settle the two gates. - -**Tool Use:** `Task` - -```json -{ - "prompt": "IMPLEMENT a bounded agent-guidance/control-protocol slice in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. You are editing files. Respect shared-worktree discipline: do not modify, move, stage, or commit these foreign-edited files: docs/agents/git-workflow.md, docs/agents/issue-writing.md, docs/agents/legibility.md, docs/planning/_shared/STEERING.md, docs/planning/_shared/hash-monorepo-import-plan.md. Do not move any existing docs yet; external Linear pointers and foreign edits block that. Do not write to Linear. Read current contents before patching.\n\nOutcome: lock the recurring strategic steering protocol and the agreed role-based topology, without duplicating meaning.\n\nRequired edits:\n1. Add docs/agents/steering.md as the canonical compact protocol for `orient -> choose -> execute -> reconcile -> replan`.\n - Triggers: starting/resuming without a current proof target; changed objective/deadline/use case/pressure; proof succeeds/fails; authority conflict; external gate change/staleness; selected frontier loses value; arc close detects drift. Ordinary ticket movement is not a trigger.\n - Orient classifies fact, belief+confidence+evidence, unknown+cheapest probe, external gate+owner/source/watch trigger/last checked/consequences.\n - Choose treats mechanical issue availability as a filter, then ranks by objective contribution, risk retired, information gain, deadline pressure, cost/reversibility. Select one proof frontier or deliberate pair and state claim, proof bundle, cut, issue/gap projection, stop/replan trigger.\n - Execute through real production entrypoints/wiring; no fixture-supplied product wiring. Require runnable proof plus immutable legibility snapshot in another register; human witness required for UX, interpretation, live runtime, and demo-comprehension claims unless explicitly inapplicable.\n - Define the lifecycle `corpus/case -> reviewed fixture -> production-path run -> immutable run snapshot -> validated claim -> executable oracle`, with information-wall note that hidden answer keys/oracles are not interviewee inputs.\n - Reconcile each changed truth into exactly one authority: STEERING strategy, COORDINATION issue graph projection/soft edges, Linear state/hierarchy/hard blockers (external writes only after explicit approval), specs behavior, ADR decisions, evidence observed proof, reference stable explanation, archive historical context. Confidence changes cite evidence. Link, do not copy.\n - Replan on triggers; otherwise continue. Include concise checkable completion criteria and proof-bundle fields. Link to documentation.md, legibility.md, issue-tracker.md, arc-close.md instead of repeating their mechanics.\n2. Update AGENTS.md with compact trigger-based protocol routing. Add steering trigger. Preserve arc-close, Flue, issue/domain/documentation guidance but route by condition rather than only an undifferentiated list. Keep always-loaded text short.\n3. Update docs/agents/documentation.md to make this role model authoritative:\n - docs/control: compact mutable strategic/coordination/obligation control surfaces\n - docs/specs: required behavior\n - docs/adr: accepted decisions (already exists)\n - docs/evidence/proofs: immutable observed proof/witness/implementation snapshots\n - docs/evidence/evaluations: immutable evaluation runs/readouts\n - docs/reference: stable explanatory/source material\n - docs/archive: historical, superseded, or settled material with no current authority\n - docs/inbox: transient intake\n - executable evaluation assets outside docs under evaluations/{cases,protocols,oracles}\n - docs/agents remains operating guidance; INDEX remains complete registry.\n - Existing docs/planning and docs/history are legacy paths pending an atomic migration; add no new files there.\n - Effort is metadata, not placement.\n - Define case/corpus, fixture, run snapshot, oracle; promotion/disposition rules; immutable legibility snapshot and witness record; external pointer repair before moves; keep mutable controls compact and link history/evidence rather than accumulating prose.\n4. Update docs/agents/issue-tracker.md so its open/unblocked/unassigned list is called the `claimable queue` or `mechanical frontier`, not the strategic proof frontier. Linear owns issue facts; COORDINATION projects topology/soft edges; STEERING selects by objective/risk/info gain. Add explicit approval gate before issue creation, edits, comments, hierarchy/state/project mutations.\n5. Update docs/agents/arc-close.md minimally: conditional steering reconciliation when a trigger fired; proof/witness snapshot indexed; validated categorical claims considered for oracle promotion; external gates not stale/ownerless; coordination remains issue projection, not strategy. Avoid copying the loop.\n6. Update docs/agents/domain.md only if its current proposed tree or authority language conflicts with the role topology.\n7. Update docs/INDEX.md to register docs/agents/steering.md and revise only the topology/protocol wording needed now. Do not pretend physical moves already occurred.\n8. Update packages/core/test/architecture/docs-index.test.ts only if needed to enforce reachability of steering or allow the declared transitional state. Do not weaken existing coverage.\n9. Inspect CONTEXT.md. Add only already-resolved glossary terms if absent and genuinely domain-level; do not put implementation/process details there.\n\nDo NOT add a local steering skill yet: one canonical protocol plus AGENTS pointer is sufficient for this first cycle. Do NOT create evaluations/ yet because no executable assets move in this slice. Do not stage/commit. Run focused docs-index tests and git diff --check for files you changed. Return exact files changed, concise semantic summary, test results, and blockers left for the physical migration.", - "description": "Lock steering guidance" -} -``` - -## User - -**Tool Result:** `TU-034Ch0qrBl9tTNCfgS48XA` - -``` -Implemented the steering/control-protocol slice. - -Changed: -- `AGENTS.md` -- `docs/INDEX.md` -- `docs/agents/arc-close.md` -- `docs/agents/documentation.md` -- `docs/agents/issue-tracker.md` -- `docs/agents/steering.md` — new canonical loop - -Key outcomes: -- Established role-based documentation authority and evidence lifecycle. -- Distinguished strategic proof frontiers from Linear’s mechanical frontier. -- Added explicit approval gates for all Linear writes. -- Added trigger-based protocol routing and arc-close steering reconciliation. -- Left `CONTEXT.md`, domain guidance, tests, foreign-edited files, and Linear untouched. - -Verification: -- Docs-index tests: **7 passed** -- Formatting check: passed -- `git diff --check`: passed - -Migration blockers remain: external pointer repair, concurrent foreign edits, and an atomic move from legacy `docs/planning`/`docs/history`. No directories were moved or created; nothing staged or committed. -``` - -## Assistant - -The protocol slice is written and its focused checks pass. I’m having a separate agent cold-review the resulting diff for authority duplication, accidental overreach, and whether the completion criteria are genuinely checkable before I commit it. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY REVIEW of the current uncommitted diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Focus only on changes to AGENTS.md, docs/INDEX.md, docs/agents/arc-close.md, documentation.md, issue-tracker.md, and new steering.md. Do not modify anything. Preserve and ignore unrelated foreign diffs in git-workflow.md, issue-writing.md, legibility.md, STEERING.md, and hash-monorepo-import-plan.md. Review against these intents: one authoritative steering loop; compact trigger-based AGENTS routing; role topology docs/control, specs, adr, evidence/{proofs,evaluations}, reference, archive, inbox; legacy planning/history transitional and no new files; mechanical frontier vs proof frontier; explicit approval for external writes; corpus->fixture->run snapshot->oracle lifecycle; immutable legibility + conditional human witness; arc-close does not duplicate steering. Report only actionable findings with file/line references and suggested smallest fixes, then a pass/fail verdict. Also identify exactly which diff paths belong to this slice for staging.", - "description": "Review steering guidance diff" -} -``` - -## User - -**Tool Result:** `TU-034Ch5XPVoXEDFKaRqMUJF` - -``` -- **`docs/agents/arc-close.md:25`** — Exempting archive documents from indexing conflicts with `documentation.md:83` and the existing index test. - **Fix:** Replace “every non-archive Markdown document” with “every non-agent document.” - -- **`docs/agents/arc-close.md:38-39`** — Requires Linear repairs/closure without explicitly applying the external-write approval gate. - **Fix:** Prefix these mutations with “With explicit approval,” or directly reference `issue-tracker.md`’s approval gate. - -- **`docs/agents/arc-close.md:68-73`** — Duplicates steering’s proof/witness/oracle/gate reconciliation checklist despite saying not to copy the loop. - **Fix:** Remove the duplicated bullets and require running `steering.md` when triggered. - -- **`docs/agents/steering.md:85`** — “Observed proof → `docs/evidence/`” is less precise than the authoritative topology’s separate `proofs/` and `evaluations/` authorities. - **Fix:** Split into observed proof/witness → `docs/evidence/proofs/`, and evaluation runs/readouts → `docs/evidence/evaluations/`. - -**Verdict: FAIL** — four actionable authority/control inconsistencies. - -**Exact slice paths for staging:** -- `libs/@hashintel/brunch-agent/AGENTS.md` -- `libs/@hashintel/brunch-agent/docs/INDEX.md` -- `libs/@hashintel/brunch-agent/docs/agents/arc-close.md` -- `libs/@hashintel/brunch-agent/docs/agents/documentation.md` -- `libs/@hashintel/brunch-agent/docs/agents/issue-tracker.md` -- `libs/@hashintel/brunch-agent/docs/agents/steering.md` -``` - -## Assistant - -The review caught four small authority inconsistencies: archive indexing, the Linear approval gate at arc-close, a duplicated close checklist, and an imprecise evidence destination. I’m correcting those without widening the slice. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT the current steering-guidance slice in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Read files before patching. Do not touch foreign-edited files. Apply only these review fixes:\n1. docs/agents/arc-close.md around line 25: align indexing with docs/agents/documentation.md and existing tests; use 'every non-agent document' rather than exempting archive.\n2. arc-close Linear mutation steps: explicitly require approval by linking/referencing issue-tracker.md's external-write approval gate; do not duplicate its mechanics.\n3. arc-close final steering section: remove duplicated proof/witness/oracle/external-gate bullets; when a steering trigger fired, require running/reconciling docs/agents/steering.md and rely on its completion criteria. Keep only close-specific deposit/index checks that are not already part of steering, if any.\n4. docs/agents/steering.md authority routing: split observed proof/witness -> docs/evidence/proofs/ and evaluation runs/readouts -> docs/evidence/evaluations/.\nDo not stage or commit. Run the docs-index test and git diff --check for the slice. Return summary and results.", - "description": "Fix steering guidance review" -} -``` - -## User - -**Tool Result:** `TU-034Ch7Bmpkjc9C1sjzUs2A` - -``` -Applied requested review fixes: - -- Updated indexing language to “every non-agent document.” -- Linked Linear mutations to `issue-tracker.md`’s approval gate. -- Simplified arc-close steering reconciliation to rely on `steering.md`. -- Split proof/witness and evaluation authority paths in `steering.md`. - -Verification: -- Docs-index test: **7/7 passed** -- `git diff --check`: **passed** -- Nothing staged or committed. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY re-review the current steering-guidance diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent after fixes. Scope: AGENTS.md, docs/INDEX.md, docs/agents/{arc-close,documentation,issue-tracker,steering}.md. Ignore all foreign diffs. Verify the four prior findings are resolved and check for any remaining contradiction, duplication, broken relative link, ambiguous completion criterion, or role-topology mismatch. Do not modify. Return pass/fail, concise findings, and exact slice paths.", - "description": "Recheck steering guidance" -} -``` - -## User - -**Tool Result:** `TU-034Ch8hfN4Kflot50yERja` - -``` -**FAIL** - -Four prior findings appear resolved: - -- External Linear writes require approval. -- Mechanical queue is distinct from strategic proof frontier. -- Legacy paths remain authoritative pending atomic migration. -- Agent protocols route through `AGENTS.md`, not `docs/INDEX.md`. - -Remaining finding: - -- **Ambiguous/impossible index completion criterion:** “every document outside `docs/agents/`” includes `docs/INDEX.md` itself—and potentially `AGENTS.md`/`CONTEXT.md`—yet they are not indexed. Narrow the scope explicitly, excluding the index itself. - - `docs/agents/documentation.md:83` - - `docs/agents/arc-close.md:24` - - Completion impact: `docs/agents/arc-close.md:84` - -All checked Markdown relative links resolve; no remaining role-topology contradiction or material duplication found. - -Exact reviewed slice: - -- `AGENTS.md` -- `docs/INDEX.md` -- `docs/agents/arc-close.md` -- `docs/agents/documentation.md` -- `docs/agents/issue-tracker.md` -- `docs/agents/steering.md` -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT only docs/agents/documentation.md and docs/agents/arc-close.md in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Read packages/core/test/architecture/docs-index.test.ts to use its exact governed scope. Fix the ambiguous index criterion so it explicitly excludes docs/INDEX.md and docs/agents/** (and does not accidentally claim AGENTS.md/CONTEXT.md if outside the test's scope). Use one concise phrase consistently in both docs. Do not modify tests, foreign-edited files, stage, or commit. Run focused docs-index tests and git diff --check. Return exact wording and results.", - "description": "Clarify index scope" -} -``` - -## User - -**Tool Result:** `TU-034Ch9o6UBmu4H1uBXeq3r` - -``` -Updated only the two requested docs. - -Exact shared wording: - -> every document under `docs/` except `docs/INDEX.md` and `docs/agents/**` - -Also removed the ambiguous “every other document” claim. - -Verification: -- Focused docs-index tests: **7/7 passed** -- `git diff --check`: **passed** -- No tests modified; nothing staged or committed. -``` - -## Assistant - -The control protocol now passes its consistency review. Before committing this finished unit, I’m running the package’s full applicable stack—not only the focused documentation test—while scoping any writable tooling to the six files in this slice. - -**Tool Use:** `Task` - -```json -{ - "prompt": "VERIFY the completed steering-guidance slice in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify source files, run fixers, stage, or commit. Manifest: AGENTS.md, docs/INDEX.md, docs/agents/arc-close.md, documentation.md, issue-tracker.md, steering.md. Ignore unrelated foreign diffs. Inspect package scripts/config and run all applicable checks in this order, scoped to @hashintel/brunch-agent and these files where possible: static lint, formatting check, TypeScript typecheck, full package unit tests (not only focused), build, plus the focused docs-index test and git diff --check. If a check is inapplicable because docs are excluded, state that with the config evidence; do not invent a check. Stop and report on the first failure, per repo discipline. Return exact commands, pass/fail, test counts, and whether any source file changed unexpectedly.", - "description": "Verify steering protocol slice" -} -``` - -## User - -**Tool Result:** `TU-034ChAzOWI0O490gW6KPlw` - -``` -Verification passed. No source files changed unexpectedly. - -- Static lint: `yarn exec turbo run lint:eslint --filter @hashintel/brunch-agent --` — PASS, 0 warnings/errors across 29 files. -- Formatting: scoped `yarn exec oxfmt --check …` — PASS. Only `AGENTS.md` matched; `oxfmt.config.ts` explicitly excludes `libs/@hashintel/brunch-agent/docs/**`. -- Typecheck: `yarn exec turbo run lint:tsc --filter @hashintel/brunch-agent --` — PASS. -- Full unit tests: `yarn exec turbo run test:unit --filter @hashintel/brunch-agent -- --run` — PASS, 12 files / 114 tests. -- Build: `yarn exec turbo run build --filter @hashintel/brunch-agent --` — PASS. -- Focused index test: `yarn workspace @hashintel/brunch-agent vitest run test/architecture/docs-index.test.ts` — PASS, 1 file / 7 tests. -- Diff hygiene: scoped `git diff --check -- …` — PASS. - -Markdownlint is inapplicable: `.markdownlint-cli2.jsonc` excludes both `**/AGENTS*.md` and `libs/@hashintel/brunch-agent/docs/**`. - -One initial malformed invocation (`yarn turbo …`) failed because no Yarn script named `turbo` exists; the corrected `yarn exec turbo …` check passed. Foreign diffs remained untouched. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "COMMIT the verified steering-guidance unit in /Users/lunelson/Code/hashintel/hash. Shared worktree rules are strict. First inspect git status. Stage exactly these literal paths and no others:\n- libs/@hashintel/brunch-agent/AGENTS.md\n- libs/@hashintel/brunch-agent/docs/INDEX.md\n- libs/@hashintel/brunch-agent/docs/agents/arc-close.md\n- libs/@hashintel/brunch-agent/docs/agents/documentation.md\n- libs/@hashintel/brunch-agent/docs/agents/issue-tracker.md\n- libs/@hashintel/brunch-agent/docs/agents/steering.md\nVerify the staged set equals exactly those six files and that all foreign diffs remain unstaged. Then commit with concise message: `Add strategic steering protocol`. Do not amend, push, move files, or touch Linear. Return commit SHA and post-commit status separating remaining foreign modifications from clean slice state.", - "description": "Commit steering protocol" -} -``` - -## User - -**Tool Result:** `TU-034ChDtLVTQ4JkC1RDaOCd` - -``` -Committed `397405a27602508520959747dce4395d4553fde9` — `Add strategic steering protocol`. - -- Slice: clean; exactly six requested files committed. -- Foreign modifications: five files remain unstaged (`git-workflow.md`, `issue-writing.md`, `legibility.md`, and two planning files). -- No push, amend, moves, or Linear changes. -``` - -## Assistant - -The routine is now locked in commit `397405a276`: - -- `orient → choose → execute → reconcile → replan` -- strategic proof frontiers are distinct from the issue tracker’s claimable queue -- proof requires production wiring, immutable legibility, and witness where interpretation matters -- evaluation material has an explicit case/fixture/run-snapshot/oracle lifecycle -- confidence moves only with cited evidence -- external writes require approval - -The remaining cleanup is now a migration, not a design question. I’m doing one live, read-only Linear pointer audit so I can ask for a bounded authorization rather than a vague “may I edit Linear?” request. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY external audit. Work from /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify repository files or Linear. Use the local Linear tooling/CLI and load cli-linear skill if needed. Find current FE team issues in the brunch-agent project whose live descriptions contain repository-relative paths under `libs/@hashintel/brunch-agent/docs/planning/`, `docs/history/`, or the exact current `docs/spec.md` path. Historical comments need not be exhaustively searched unless tooling makes it cheap; focus on current issue descriptions because they are canonical pointers. Build a complete table: issue key/title/state, each old path, whether the issue is still open/current versus completed/canceled, and the proposed role destination using this locked topology: docs/control, docs/specs, docs/evidence/{proofs,evaluations}, docs/reference, docs/archive, executable evaluations/{cases,protocols,oracles}. For any path whose destination is ambiguous, flag it rather than inventing. Also count affected issues and path occurrences, and state the smallest set of external mutations required to make the physical repo move atomic. Do not write or comment. Return direct evidence of the queries/commands used and any access limitation.", - "description": "Audit live Linear path pointers" -} -``` - -## User - -**Tool Result:** `TU-034ChF8JY7AasEKwuWjqFt` - -``` -## Audit result - -Queried all **82 FE issues** in Linear project `brunch-agent`; pagination reported `hasNextPage: false`. - -- **Affected issues:** 16 -- **Open/current:** 7 -- **Completed:** 9 -- **Canceled affected:** 0 -- **Path occurrences:** 30 -- **Distinct matched path strings:** 22 -- `docs/history/` occurrences: 0 -- Exact `docs/spec.md` occurrences: 0 - -| Issue / state | Current? | Old path(s) → proposed role destination | -|---|---|---| -| FE-1464 — Close the final stack review findings before repository handoff — Done | Completed | `…/baseline/run.ts` → `evaluations/protocols`; `…/plugin-contract-spec.md` → `docs/specs` | -| FE-1437 — Move brunch-agent into hashintel/hash with its history — Ready for review | Open/current | `…/_shared/hash-monorepo-import-plan.md` ×2 → `docs/control` while active; later archival disposition is separate | -| FE-1432 — Resolve the stack's open review threads — Done | Completed | `…/review-remediation-2026-08-18.md` → `docs/evidence/proofs` | -| FE-1431 — Define declarative plugin authoring — Todo | Open/current | `…/plugin-contract-spec.md` → `docs/specs` | -| FE-1424 — Complete the documentation protocol — Done | Completed | `docs/planning/_shared/` → **ambiguous/mixed directory**; contents require per-document destinations | -| FE-1422 — Move the portable ask protocol into core — Done | Completed | `…/remediation-plan-2026-08-17.md` → **ambiguous:** proof record vs archive; `…/_shared/topology.md` → `docs/evidence/proofs`; `…/deep-read-fe-1389.md` → `docs/evidence/proofs`; `…/_shared/CONVERGENCE.md` → **ambiguous:** likely retired control/archive | -| FE-1420 — Make affordance handling safe under retries and abandonment — Next up | Open/current | `…/deep-read-fe-1389.md` → `docs/evidence/proofs` | -| FE-1419 — Align capture-store rules and verification claims — Done | Completed | `…/refactor-queue-2026-08-14.md` → **ambiguous:** settled proof record vs archive | -| FE-1407 — Catalogue elicitor failures that published measures miss — Next up | Open/current | `docs/planning/process-model-elicitation/` → **ambiguous directory pointer**; intended catalogue likely `docs/reference` | -| FE-1404 — Run the third baseline with completion and interview guidance — Next up | Open/current | `…/baseline/` → **mixed:** cases → `evaluations/cases`; procedure/runner → `evaluations/protocols`; transcripts/readouts → `docs/evidence/evaluations` | -| FE-1400 — Strengthen verification, dev storage, and the baseline runner — Done | Completed | `…/baseline/run.ts:213` → `evaluations/protocols` | -| FE-1383 — Build the first complete elicitation interview — In progress | Open/current | `docs/planning/elicitation-kernel/spec.md` → `docs/specs` | -| FE-1382 — Compile the truck-fleet source dossier — Done | Completed | `…/research/` → `docs/reference` | -| FE-1374 — Assemble the elicitation harness specification — Done | Completed | `docs/planning/elicitation-kernel/spec.md` → `docs/specs` | -| FE-1366 — Document the elicitation harness architecture — Done | Completed | `…/spec.md` ×2 → `docs/specs`; `docs/planning/elicitation-kernel/` → **mixed:** spec to `docs/specs`, remaining settled effort records to `docs/archive` | -| FE-1357 — Plan the September elicitation demo and plugin specification — In progress | Open/current | process-model root → **mixed/ambiguous**; kernel spec → `docs/specs`; notes directory → **ambiguous per document**; recommendation → `docs/archive`; truck-fleet dossier/research → `docs/reference`; IR design → `docs/specs`; baseline directory → split among `evaluations/{cases,protocols}` and `docs/evidence/evaluations`; grilling inputs → **ambiguous:** control/reference/archive | - -## Minimum external mutation set - -To make the physical repository migration pointer-atomic: - -1. Decide final filenames for the ambiguous/mixed destinations. -2. Update the **16 affected Linear issue descriptions** in the same approved cutover as the repository move and `docs/INDEX.md` reconciliation. -3. Repair all **30 description occurrences**; directory pointers must be rewritten into explicit role-specific pointers where content splits. -4. No Linear comments or state changes are required. - -Completed issues still require description edits because their live descriptions remain canonical pointers. Historical comments were not exhaustively searched, as requested. - -## Direct query evidence - -```sh -linear auth whoami -linear api <<'GRAPHQL' -query { - projects(first: 100, filter: {name: {eq: "brunch-agent"}}) { - nodes { id name state } - } -} -GRAPHQL -``` - -Resolved project ID: `74c3fa2d-202d-4344-96e3-30ee3a86784f`. - -```sh -linear api --variable projectId=74c3fa2d-202d-4344-96e3-30ee3a86784f <<'GRAPHQL' -query($projectId: ID!) { - issues(first: 250, filter: { - project: {id: {eq: $projectId}}, - team: {key: {eq: "FE"}} - }) { - pageInfo { hasNextPage endCursor } - nodes { identifier title description state { name type } updatedAt url } - } -} -GRAPHQL -``` - -Then descriptions were filtered locally for `docs/planning/`, `docs/history/`, and exact `docs/spec.md`. - -**Access limitations:** none. Linear authentication succeeded as Lu Nelson in the HASH workspace. Repository and Linear remained unmodified. Existing unrelated worktree modifications were observed and untouched. -``` - -## Assistant - -The steering protocol is locked and committed as `397405a276`. - -Key gaps now covered: human witness, external-gate ownership, confidence transitions, evaluation lifecycle, oracle promotion, and strategic vs mechanical frontiers. A local skill is deliberately deferred until a second cycle proves it necessary. - -The physical topology migration is ready but blocked by: - -1. **Linear approval:** update 16 issue descriptions containing 30 old paths. No comments, state, or hierarchy changes. -2. **Concurrent edits:** five files must be committed or explicitly handed over before moving: - - `git-workflow.md` - - `issue-writing.md` - - `legibility.md` - - `STEERING.md` - - `hash-monorepo-import-plan.md` - -May I update those Linear descriptions and incorporate the concurrent edits into the migration? - -## User - -working state is now clean. Linear changes approved (observe our issue authoring guidance and patterns); topology migration approved. - -## Assistant - -Approval received. I’ll perform this as one coordinated cutover: migrate repository paths and evaluation assets, repair internal links/tests/guidance, update the 16 Linear descriptions without changing issue contracts beyond path repair, then run the full verification and close-out pass. - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-writing-issues", - "arguments": "Apply Brunch issue-contract rules while repairing repository paths in 16 existing Linear issue descriptions. Preserve titles, prose contracts, states, hierarchy, and agent-note boundaries; change only stale artifact pointers necessary for the approved topology migration." -} -``` - -**Tool Use:** `skill` - -```json -{ - "name": "cli-linear", - "arguments": "Update approved repository-path pointers in existing FE brunch-agent issue descriptions during an atomic docs topology migration; no comments, state, hierarchy, assignment, or project changes." -} -``` - -## User - -**Tool Result:** `TU-034Ci0oG04hUrouxThB5wB` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-writing-issues\">\n# ds-writing-issues Skill\n\n\n<!-- dogsled original — carries the issue contract designed in https://github.com/lunelson/dogsled/issues/13 -->\n\n# Writing Issues\n\nEvery issue serves two audiences: the team, who scan for direction and progress, and the agent, which needs precise state to continue the work. Don't make one body serve both equally. Give the issue a **contract** — the human-readable surface — above, and an **execution record** — the agent's working state — below. One source of truth, two projections.\n\nThe rhetorical mode changes at each layer: **outcome language for scanning, explanatory language for understanding, specification language for execution.** The content guidance here is tracker-agnostic; how this repo's tracker expresses labels, parents, and sub-issues lives in its tracker doc (`docs/agents/issue-tracker.md`).\n\n## Who carries the contract\n\nThe test is structural, not type-based. An issue **with no parent** (\"root\") carries the contract. An issue **with a parent** (\"child\") is exempt — it inherits legibility from its parent and keeps whatever shape its driving workflow needs (a wayfinder ticket's `## Question`, a build ticket's own template). Label every child **`dogsled:unframed`** at creation so team-facing views can filter it out. The exemption is automatic the moment a skill creates a child issue — no registry of exempt types. (`dogsled:unframed` marks framing-exemption; it is distinct from type labels like `wayfinder:<type>`.)\n\n## Title — the scan layer\n\nName the outcome: the behavior that becomes possible, the incorrect behavior that stops, the property that becomes reliable, the question the work will answer. Verb + user/system outcome, mechanism only as a trailing qualifier. Two tests, not a vibe:\n\n- **Plan-change test** — the title stays substantially true if the implementation approach changes; swapping the technology must not force a rewrite.\n- **Concept vs. mechanism** — not technical vs. non-technical: domain terms the wider team already uses (\"webhook\", \"workspace\", \"regional outage\") belong in a title; internal class, table, framework, and algorithm names don't.\n\n| Mechanism-shaped | Outcome-shaped |\n| ---------------------------------------------------- | --------------------------------------------------------------- |\n| Add idempotent webhook persistence with dedupe key | Prevent duplicate customer notifications from retried webhooks |\n| Backfill `reports.organization_id` and add NOT NULL FK | Make every report belong to the correct workspace |\n| Implement HNSW search over issue embeddings | Decide how agents should retrieve older planning decisions |\n\nTitle a bug by its observable symptom and affected experience, not the hypothesized root cause. Title research by the question or decision — never disguise a favored implementation as the purpose of an investigation. And internal work names its real engineering outcome — \"safer to test and release\", \"faster incident diagnosis\", \"clearer ownership\" — rather than inventing a tenuous end-user story.\n\n## Context — the prose layer\n\nOne or two short paragraphs of plain prose at the top of the body, mandatory on every root issue; two to four sentences suffice for a small task — don't pad. The reader should be able to recover, where relevant: **current state → consequence → intended change → material status or uncertainty.**\n\nThe central rule: **use a list when the list itself is the information; use prose when the relationship between the facts is the information.** Cause, impact, direction, status, and uncertainty are relationships — prose. A list earns its place only when enumeration is genuinely the point: independent alternatives, several affected products, a set the reader must compare.\n\nThe failure mode is the **property-bag** — facts without their relationship:\n\n```text\nProblem: Users see stale data\nImpact: Confusing UX\nSolution: Cache invalidation\nStatus: In progress\n```\n\nWrite the explanation instead:\n\n> After a user saves a profile change, the page can continue to show the old value until the browser is refreshed, making a successful save look as though it failed. This work will make the page reflect the saved server state immediately. The basic update path is complete, but concurrent edits still need testing.\n\nStatus inside the context describes achieved state and remaining uncertainty in the same prose voice — not \"In progress, PR open, blocked\".\n\n**Synchronization.** Update the context only on a *material* change — one that affects outcome, scope, status, risk, or timing — never on routine technical progress (a commit landing, a pull request opening).\n\n## Aggregating root issues\n\nA root issue **with children** — a map, an epic — additionally carries list-shaped sections, because enumerating many children's state genuinely is the information:\n\n- **Decisions so far** — durable, append-only: one line per closed child, gist + link.\n- **Current plan** — mutable; changes as the work replans.\n- **Open questions** — the one named home for known-unknowns, pruned as items resolve.\n\nThese sections are earned by aggregation, not universal. A single-narrative root issue folds its state into the context prose (\"the recovery design is agreed, implementation has started\") rather than bolting on a one-entry list.\n\n## The execution record\n\nEverything below a `---` divider is the agent's and driving engineer's own — optional, and schema-free: the narrowest readership, so cross-issue consistency has no payoff. Hold whatever the workflow needs — constraints, assumption tables, decisions-vs-current-plan, acceptance criteria, asset links, a posture check. Present when there's something to hold; never mandatory boilerplate on a trivial issue. Technical detail is additive, never deleted merely to simplify the issue.\n\n## One kind of entity\n\nEphemeral or investigative work — research, a prototype, a spike — becomes a **child issue** holding both the query and the result. Never a comment thread used as a workspace; never a body checklist as decomposition. The single exception is the immutable **resolution comment** posted when an issue closes — a closing act, not a workspace.\n\n## Ownership direction\n\nState a fact once; everywhere else links. Within an issue: `Current plan` links to a decision, never restates it. Across issues: a child never re-explains a fact its parent's context already states.\n\n## At close\n\nAn aggregating issue prunes its mutable sections (`Current plan`, `Open questions`) to empty, leaving context + `Decisions so far` + the resolution comment as the durable record. A non-aggregating root issue gets one final context edit reflecting closure. The execution record is left as-is — it never carried a durability promise, so pruning it is ceremony.\n\n## Before publishing\n\n- **Scan test** — can a teammate understand the direction from the title alone?\n- **Plan-change test** — would the title survive a different implementation?\n- **Prose test** — does the context explain causality, or list fragments?\n- **Containment test** — are code-level details below the divider?\n- **List test** — does every list hold genuinely parallel or ordered items?\n- **Uncertainty test** — are open questions presented as uncertainty, not fact?\n\n\nARGUMENTS: Apply Brunch issue-contract rules while repairing repository paths in 16 existing Linear issue descriptions. Preserve titles, prose contracts, states, hierarchy, and agent-note boundaries; change only stale artifact pointers necessary for the approved topology migration.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-writing-issues\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-writing-issues/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -**Tool Result:** `TU-034Ci0oGUHehmqaGNel84M` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"cli-linear\">\n# cli-linear Skill\n\n\n# Linear CLI\n\nA CLI to manage Linear issues from the command line, with git and jj integration.\n\n## Prerequisites\n\nThe `linear` command must be available on PATH. To check:\n\n```bash\nlinear --version\n```\n\nIf not installed, follow the instructions at:\\\nhttps://github.com/schpet/linear-cli?tab=readme-ov-file#install\n\n## Best Practices for Markdown Content\n\nWhen working with issue descriptions or comment bodies that contain markdown, **always prefer using file-based flags** instead of passing content as command-line arguments:\n\n- Use `--description-file` for `issue create` and `issue update` commands\n- Use `--body-file` for `comment add` and `comment update` commands\n\n**Why use file-based flags:**\n\n- Ensures proper formatting in the Linear web UI\n- Avoids shell escaping issues with newlines and special characters\n- Prevents literal `\\n` sequences from appearing in markdown\n- Makes it easier to work with multi-line content\n\n**Example workflow:**\n\n```bash\n# Write markdown to a temporary file\ncat > /tmp/description.md <<'EOF'\n## Summary\n\n- First item\n- Second item\n\n## Details\n\nThis is a detailed description with proper formatting.\nEOF\n\n# Create issue using the file\nlinear issue create --title \"My Issue\" --description-file /tmp/description.md\n\n# Or for comments\nlinear issue comment add ENG-123 --body-file /tmp/comment.md\n```\n\n**Only use inline flags** (`--description`, `--body`) for simple, single-line content.\n\n## Available Commands\n\n```\nlinear auth # Manage Linear authentication\nlinear issue # Manage Linear issues\nlinear team # Manage Linear teams\nlinear project # Manage Linear projects\nlinear project-update # Manage project status updates\nlinear cycle # Manage Linear team cycles\nlinear milestone # Manage Linear project milestones\nlinear initiative # Manage Linear initiatives\nlinear initiative-update # Manage initiative status updates (timeline posts)\nlinear label # Manage Linear issue labels\nlinear document # Manage Linear documents\nlinear config # Interactively generate .linear.toml configuration\nlinear schema # Print the GraphQL schema to stdout\nlinear api # Make a raw GraphQL API request\n```\n\n## Reference Documentation\n\n- [auth](references/auth.md) - Manage Linear authentication\n- [issue](references/issue.md) - Manage Linear issues\n- [team](references/team.md) - Manage Linear teams\n- [project](references/project.md) - Manage Linear projects\n- [project-update](references/project-update.md) - Manage project status updates\n- [cycle](references/cycle.md) - Manage Linear team cycles\n- [milestone](references/milestone.md) - Manage Linear project milestones\n- [initiative](references/initiative.md) - Manage Linear initiatives\n- [initiative-update](references/initiative-update.md) - Manage initiative status updates (timeline posts)\n- [label](references/label.md) - Manage Linear issue labels\n- [document](references/document.md) - Manage Linear documents\n- [config](references/config.md) - Interactively generate .linear.toml configuration\n- [schema](references/schema.md) - Print the GraphQL schema to stdout\n- [api](references/api.md) - Make a raw GraphQL API request\n\nFor curated examples of organization features (initiatives, labels, projects, bulk operations), see [organization-features](references/organization-features.md).\n\n## Discovering Options\n\nTo see available subcommands and flags, run `--help` on any command:\n\n```bash\nlinear --help\nlinear issue --help\nlinear issue list --help\nlinear issue create --help\n```\n\nEach command has detailed help output describing all available flags and options.\n\n## Using the Linear GraphQL API Directly\n\n**Prefer the CLI for all supported operations.** The `api` command should only be used as a fallback for queries not covered by the CLI.\n\n### Check the schema for available types and fields\n\nWrite the schema to a tempfile, then search it:\n\n```bash\nlinear schema -o \"${TMPDIR:-/tmp}/linear-schema.graphql\"\ngrep -i \"cycle\" \"${TMPDIR:-/tmp}/linear-schema.graphql\"\ngrep -A 30 \"^type Issue \" \"${TMPDIR:-/tmp}/linear-schema.graphql\"\n```\n\n### Make a GraphQL request\n\n**Important:** GraphQL queries containing non-null type markers (e.g. `String` followed by an exclamation mark) must be passed via heredoc stdin to avoid escaping issues. Simple queries without those markers can be passed inline.\n\n```bash\n# Simple query (no type markers, so inline is fine)\nlinear api '{ viewer { id name email } }'\n\n# Query with variables — use heredoc to avoid escaping issues\nlinear api --variable teamId=abc123 <<'GRAPHQL'\nquery($teamId: String!) { team(id: $teamId) { name } }\nGRAPHQL\n\n# Search issues by text\nlinear api --variable term=onboarding <<'GRAPHQL'\nquery($term: String!) { searchIssues(term: $term, first: 20) { nodes { identifier title state { name } } } }\nGRAPHQL\n\n# Numeric and boolean variables\nlinear api --variable first=5 <<'GRAPHQL'\nquery($first: Int!) { issues(first: $first) { nodes { title } } }\nGRAPHQL\n\n# Complex variables via JSON\nlinear api --variables-json '{\"filter\": {\"state\": {\"name\": {\"eq\": \"In Progress\"}}}}' <<'GRAPHQL'\nquery($filter: IssueFilter!) { issues(filter: $filter) { nodes { title } } }\nGRAPHQL\n\n# Pipe to jq for filtering\nlinear api '{ issues(first: 5) { nodes { identifier title } } }' | jq '.data.issues.nodes[].title'\n```\n\n### Advanced: Using curl directly\n\nFor cases where you need full HTTP control, use `linear auth token`:\n\n```bash\ncurl -s -X POST https://api.linear.app/graphql \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: $(linear auth token)\" \\\n -d '{\"query\": \"{ viewer { id } }\"}'\n```\n\n\nARGUMENTS: Update approved repository-path pointers in existing FE brunch-agent issue descriptions during an atomic docs topology migration; no comments, state, hierarchy, assignment, or project changes.\n\nBase directory for this skill: file:///Users/lunelson/.agents/skills/cli-linear\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.agents/skills/cli-linear/SKILL.template.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/api.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/auth.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/commands.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/config.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/cycle.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/document.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/initiative-update.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/initiative.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/issue.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/label.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/milestone.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/organization-features.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/project-update.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/project.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/schema.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/references/team.md</file>\n<file>/Users/lunelson/.agents/skills/cli-linear/scripts/generate-docs.ts</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -I’m using the locked authority model as the migration rule, not inventing a new taxonomy: - -- mutable current state → `docs/control` -- required behavior → `docs/specs` -- observed proof and evaluation records → `docs/evidence` -- stable explanation/research → `docs/reference` -- settled or superseded material → `docs/archive` -- runnable evaluation inputs/protocols/oracles → `evaluations` - -The first repository pass moves all non-baseline material and repairs its internal consumers. The baseline then gets split by information role rather than moved wholesale. - -**Tool Use:** `Task` - -```json -{ - "prompt": "IMPLEMENT the approved non-baseline documentation topology migration in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Worktree is expected clean except your work; inspect status first and preserve any unexpected changes. Read every file before moving/editing. Do not touch Linear yet. Do not stage/commit. Leave docs/planning/process-model-elicitation/baseline/** for a separate pass, but repair links to other files you move when necessary.\n\nCanonical roles are already locked in docs/agents/documentation.md. Keep docs/agents, docs/adr, docs/inbox, and existing docs/reference source materials. Delete docs/inbox/.gitkeep if present because inbox is populated.\n\nUse git mv and these destination decisions, preserving names unless specified:\n\nCONTROL\n- docs/planning/_shared/STEERING.md -> docs/control/STEERING.md\n- .../COORDINATION.md -> docs/control/COORDINATION.md\n- .../SPEC-LEDGER.md -> docs/control/SPEC-LEDGER.md\n\nSPECS\n- docs/spec.md -> docs/specs/elicitation-kernel.md\n- process-model-elicitation/ir-design.md -> docs/specs/intermediate-representation.md\n- ir-design-plain.md -> docs/specs/intermediate-representation-plain.md (mark/link as non-authoritative legibility companion if the content currently risks dual authority; minimal edit only)\n- plugin-contract-spec.md -> docs/specs/plugin-contract.md\n- petrinaut-integration-spec.md -> docs/specs/petrinaut-integration.md\n\nREFERENCE\n- _shared/topology.md -> docs/reference/architecture/topology.md\n- _shared/flue-architecture-cheatsheet.md -> docs/reference/architecture/flue-architecture-cheatsheet.md\n- process-model-elicitation/capture-store-plain.md -> docs/reference/architecture/capture-store.md\n- research/elicitation-strategy-literature.md -> docs/reference/research/elicitation/elicitation-strategy-literature.md\n- research/re-interviewing-literature-worker-report.md -> docs/reference/research/elicitation/interviewing-literature-source-catalog.md\n- research/petrinaut-survey.md -> docs/reference/research/petrinaut-survey.md\n- research/voice-feasibility.md -> docs/reference/research/voice-feasibility.md\n\nEVIDENCE/PROOFS\n- ir-worked-examples.md -> docs/evidence/proofs/design/intermediate-representation-worked-examples.md\n- spikes/fe-1434-suspension-* -> docs/evidence/proofs/spikes/ with names preserved\n- adapter-panel-spike-2026-08-19.md -> docs/evidence/proofs/spikes/fe-1435-adapter-panel-2026-08-19.md\n- transport-aisdk-implementation-2026-08-19.md -> docs/evidence/proofs/implementations/fe-1436-transport-aisdk-2026-08-19.md\n- ask-return-implementation-2026-08-19.md -> docs/evidence/proofs/implementations/fe-1449-ask-return-2026-08-19.md\n- notes/deep-read-fe-1389.md and deep-read-fe-1390.md -> docs/evidence/proofs/audits/\n- notes/research-patterns-audit.md -> docs/evidence/proofs/audits/research-patterns-audit.md\n- notes/expert-meeting-findings-2026-08-11.md -> docs/evidence/proofs/research/expert-meeting-findings-2026-08-11.md\n- planning/legibility-sweep/flue-patterns-audit-* and flue-entry-projection-source-read-* -> docs/evidence/proofs/audits/\n\nARCHIVE\n- docs/history/planning/elicitation-kernel/** -> docs/archive/elicitation-kernel/** preserving relative subtree\n- _shared/hash-monorepo-import-plan.md -> docs/archive/migrations/hash-monorepo-import-plan.md\n- planning/legibility-sweep/issue-pr-migration-2026-08-20/** -> docs/archive/migrations/issue-pr-legibility-2026-08-20/**\n- legibility refactor-queue/remediation-plan/review-remediation -> docs/archive/engineering/legibility/ names preserved\n- recommendation-demo-vehicle.md -> docs/archive/decisions/superseded/recommendation-demo-vehicle.md\n- notes/open-questions-elicitation-design-* -> docs/archive/external-snapshots/\n- notes/expert-meeting-prep-* -> docs/archive/meetings/\n- notes/grilling-inputs-* and penciled-directions-* -> docs/archive/planning-inputs/\n\nAfter inventorying, classify any omitted non-baseline file still under docs/planning by the same authority rules rather than leaving an unexplained remnant; if materially ambiguous, stop and report instead of inventing.\n\nRepair all internal Markdown links, exact path mentions that are operational instructions, README/AGENTS/protocol pointers, docs/INDEX registry entries/status/digests, package/test/config consumers, and relative links inside moved trees. Preserve historical evidence: do not rewrite committed migration source snapshots, hashes, quoted historical commands, or plain-text old paths whose evidentiary meaning is the old location; add a concise migration note only where necessary to prevent reader confusion. Do not create compatibility stubs.\n\nUpdate packages/core/test/architecture/docs-index.test.ts for the role-based zones while retaining complete indexing, target resolution, relative-link checks, and protocol reachability. During this transitional pass it may explicitly allow only the baseline subtree under legacy docs/planning; no other planning/history documents should remain. Do not weaken the test broadly.\n\nRun focused docs-index tests and git diff --check. Return: full move list including omitted files classified, all edited files, tests, remaining docs/planning remnants, and any concern for the baseline pass.", - "description": "Migrate role-based documents" -} -``` - -## User - -**Tool Result:** `TU-034Ci1TKTLf7qgVBnTZCOT` - -``` -Implemented the non-baseline documentation topology migration. - -### Moves - -- Controls → `docs/control/{STEERING,COORDINATION,SPEC-LEDGER}.md` -- Specs → `docs/specs/`: - - `elicitation-kernel.md` - - `intermediate-representation.md` - - `intermediate-representation-plain.md` - - `plugin-contract.md` - - `petrinaut-integration.md` -- Architecture/research sources → `docs/reference/{architecture,research}/` -- Proofs → `docs/evidence/proofs/{design,spikes,implementations,audits,research}/` -- Elicitation-kernel history → `docs/archive/elicitation-kernel/` -- Migration records → `docs/archive/migrations/` -- Legibility plans → `docs/archive/engineering/legibility/` -- Superseded decision → `docs/archive/decisions/superseded/` -- Meeting, snapshot, and planning inputs → corresponding `docs/archive/` zones -- Deleted populated inbox’s `.gitkeep` - -All specified files moved, including renamed FE-1435/1436/1449 records. No ambiguous omitted non-baseline planning files remained. - -### Edited files - -Updated links, operational path references, authority wording, or index entries in: - -- `README.md` -- `docs/INDEX.md` -- `docs/adr/0001`–`0004` -- `docs/agents/{arc-close,documentation,flue-routing,issue-tracker,issue-writing}.md` -- `docs/control/{COORDINATION,STEERING}.md` -- moved specs, architecture references, proofs, and affected archive Markdown -- `docs/planning/process-model-elicitation/baseline/protocol.md` -- `packages/core/test/architecture/{boundaries,docs-index}.test.ts` - -The plain IR document is explicitly marked non-authoritative. Immutable migration snapshots and historical path evidence were preserved; their narrow link-check exemptions are documented. - -### Verification - -- Formatting: passed -- ESLint: passed -- TypeScript: passed -- Unit tests: **114 passed** -- Build: passed -- `git diff --check`: passed -- Index coverage, target resolution, relative links, and protocol reachability: passed - -### Remaining legacy planning content - -Only: - -`docs/planning/process-model-elicitation/baseline/**` - -No `docs/history/` content remains. The baseline runner/package paths intentionally remain unchanged for the separate baseline pass. - -Nothing is staged or committed. -``` - -## Assistant - -The non-baseline move is complete and the full package suite passes. Before committing, I’m checking the large rename/link diff for semantic misclassification, hidden path breakage, and whether the test exemptions preserve historical evidence without becoming a general escape hatch. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY cold review of the current non-baseline topology migration diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify/stage/commit. Review intent-first: role topology docs/control, specs, evidence/proofs, reference, archive; only baseline may remain under legacy docs/planning until next pass; docs/agents and adr stay; no compatibility stubs; historical snapshots/hashes retain evidentiary old paths; INDEX/test coverage remains strict. Inspect git diff --find-renames, current tree, all changed tests, INDEX, guidance, moved controls/specs/proofs/reference/archive, and internal links. Check specifically: lost files, duplicate authorities, misclassified active controls, broken operational paths, overbroad docs-index exceptions, accidental edits to immutable migration evidence, stale `docs/history` or non-baseline `docs/planning` references that imply live authority, and co-tenancy/staging risk. Return actionable findings with file/line and smallest fix; pass/fail; exact manifest paths suitable for staging if passed after fixes.", - "description": "Review topology migration" -} -``` - -## User - -**Tool Result:** `TU-034CiAOOAqbD9YZ3bL5kCp` - -``` -## Verdict: **FAIL** - -### Findings - -1. **Stale operational path in active evidence** - - `docs/evidence/proofs/audits/deep-read-fe-1390.md:7` - - References ``../capture-store-plain.md``, which no longer exists. - - Smallest fix: change it to `../../../reference/architecture/capture-store.md`, preferably as a Markdown link so link tests enforce it. - -2. **Stale live-authority filename in glossary** - - `CONTEXT.md:74,87` - - References ``plugin-contract-spec.md`` although authority moved to `docs/specs/plugin-contract.md`. - - Smallest fix: replace both references with `docs/specs/plugin-contract.md`. - -### Evidence - -- No staged changes; co-tenancy risk currently limited to the unstaged migration. -- File accounting is complete: 73 replacement files for 73 moved documents; only deleted `.gitkeep` has no replacement. -- No duplicate byte-identical documents found. -- Immutable issue/PR migration snapshots and hash files remain byte-identical. -- Legacy-path scan found no inappropriate live `docs/history` or non-baseline `docs/planning` authority beyond the two stale references above and intentionally preserved historical evidence. -- Link exemptions cover the byte-preserved migration corpus; no overbroad non-archive exception found. -- `git diff --check`: passed. -- Focused architecture tests: **36/36 passed**. - -Because findings remain, no staging manifest is approved. After fixing them, rerun the focused tests and use the complete then-current `git status --porcelain` path set as the staging manifest; do not stage by directory or with `git add .`. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT only these two files in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent after reading them:\n1. docs/evidence/proofs/audits/deep-read-fe-1390.md: replace the stale ../capture-store-plain.md reference with a working Markdown link to ../../../reference/architecture/capture-store.md, preserving sentence meaning.\n2. CONTEXT.md: replace both live-authority references to plugin-contract-spec.md with the canonical docs/specs/plugin-contract.md path; preserve glossary meaning.\nDo not touch other files, stage, or commit. Run focused architecture/docs-index tests and git diff --check. Return exact changes and results.", - "description": "Fix topology review findings" -} -``` - -## User - -**Tool Result:** `TU-034CiCrAtqHOpSU1rW1G4n` - -``` -Updated only: - -- `deep-read-fe-1390.md`: replaced stale reference with [`capture-store.md`](../../../reference/architecture/capture-store.md). -- `CONTEXT.md`: replaced both `plugin-contract-spec.md` references with `docs/specs/plugin-contract.md`. - -Verification: -- Architecture/docs-index suite: **114 tests passed** -- `git diff --check`: **passed** -- Both targets exist -- Nothing staged or committed. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final review of the non-baseline topology migration in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Confirm the two prior stale references are fixed, no other live non-baseline docs/planning or docs/history references remain outside intentionally immutable archive snapshots, no duplicate authority/file loss, and current tests enforce that only baseline remains under legacy planning. Inspect git status for unexpected foreign changes. Return pass/fail, any actionable finding, and the complete explicit manifest of current migration changes (renames/edits/deletions) suitable for literal staging. Do not modify/stage/commit.", - "description": "Recheck topology migration" -} -``` - -## User - -**Tool Result:** `TU-034CiF8RUPYBrT00cFsjVg` - -``` -## PASS - -- Both prior stale references are fixed. -- No live non-baseline `docs/planning/**`, `docs/history/**`, or stale references remain. -- Historical references remain only in immutable snapshots, including `docs/reference/amp-analysis-flue-vs-tilde.md`. -- Legacy planning contains only the baseline experiment; `docs/history/` is absent. -- 73 documents migrated; 50 byte-identical and 23 pointer-edited. No duplicate content or file loss. -- Sole true deletion: `docs/inbox/.gitkeep`. -- No foreign changes outside `libs/@hashintel/brunch-agent`. - -Verification passed: - -- ESLint: 0 errors/warnings -- Type check -- Unit tests: 114/114 -- Build -- `git diff --check` - -## Literal staging manifest - -```sh -git add -- \ - CONTEXT.md \ - README.md \ - docs/INDEX.md \ - docs/adr/0001-brunch-is-the-product-name.md \ - docs/adr/0002-topology-and-placement-rules.md \ - docs/adr/0003-three-register-ir.md \ - docs/adr/0004-in-petrinaut-staging-and-the-monorepo-import.md \ - docs/agents/arc-close.md \ - docs/agents/documentation.md \ - docs/agents/flue-routing.md \ - docs/agents/issue-tracker.md \ - docs/agents/issue-writing.md \ - docs/planning/process-model-elicitation/baseline/protocol.md \ - packages/core/test/architecture/boundaries.test.ts \ - packages/core/test/architecture/docs-index.test.ts \ - docs/history/planning/elicitation-kernel \ - docs/inbox/.gitkeep \ - docs/planning/_shared \ - docs/planning/legibility-sweep \ - docs/planning/process-model-elicitation \ - docs/spec.md \ - docs/archive \ - docs/control \ - docs/evidence \ - docs/reference/architecture \ - docs/reference/research \ - docs/specs -``` - -This stages all 73 renames, 15 tracked edits, and the `.gitkeep` deletion without including unrelated paths. -``` - -## Assistant - -The topology review now passes: 73 documents migrated, no duplicate authorities or lost files, immutable migration evidence unchanged, and only the baseline remains in the legacy planning path. I’m committing this as the first migration unit before splitting the executable baseline. - -**Tool Use:** `Task` - -```json -{ - "prompt": "COMMIT the verified non-baseline topology migration in /Users/lunelson/Code/hashintel/hash. Inspect status first; abort and report if any change exists outside libs/@hashintel/brunch-agent or if an unexpected file appeared since review. Build an explicit literal path list from the current status (including both sides of renames/deletions as Git requires); do not use `git add .`, `git add -A`, wildcard staging, or stage outside the manifest. Stage all and only current migration paths under libs/@hashintel/brunch-agent. Verify staged diff/accounting: 73 migrated documents, tracked pointer/guidance/test edits, and deletion of docs/inbox/.gitkeep; baseline files remain except protocol link edits. Verify no unstaged migration changes remain and no unrelated staged files exist. Commit with message `Organize Brunch documentation by role`. Do not push or touch Linear. Return commit SHA, staged statistics, and post-commit status.", - "description": "Commit document topology migration" -} -``` - -## User - -**Tool Result:** `TU-034CiLI9i0zOlKWGX8xk8g` - -``` -Committed `3b4a73f4a7f5544f1ac90b5f3dacfffe509e5d96`. - -- 89 files: 73 renames, 15 modifications, 1 deletion -- 156 insertions, 140 deletions -- Pre-commit checks passed -- Post-commit worktree clean -- No push or Linear changes performed -``` - -## Assistant - -The remaining migration is the important semantic split: source material, execution protocol, and observed results stop living together. The runner will remain reproducible, tests will write only to isolated output, and no oracle directory will be invented until a reviewed answer key exists. - -**Tool Use:** `Task` - -```json -{ - "prompt": "IMPLEMENT the approved baseline split in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Worktree must be clean at start; stop on unexpected changes. Read all baseline files, package scripts, tests, and relevant workspace/prune config before editing. Do not touch Linear yet. Do not stage/commit.\n\nUse git mv and this exact role split:\nCASES/CORPUS TRUTH\n- docs/planning/process-model-elicitation/baseline/opening-message.md -> evaluations/cases/process-model-elicitation/baseline/opening-message.md\n- situation-pack.md -> evaluations/cases/process-model-elicitation/baseline/situation-pack.md\n\nPROTOCOL/RUNNER\n- protocol.md -> evaluations/protocols/process-model-elicitation/baseline/protocol.md\n- v0-prompt.md -> evaluations/protocols/process-model-elicitation/baseline/v0-prompt.md\n- run.ts -> evaluations/protocols/process-model-elicitation/baseline/run.ts\n\nIMMUTABLE EVALUATION EVIDENCE\nMove into docs/evidence/evaluations/process-model-elicitation/baseline/:\n- readout.md\n- transcripts/** (all current files, relative structure preserved)\n\nNo oracle exists. Do not create an empty evaluations/oracles directory or claim that situation-pack is an answer key.\n\nAdd evaluations/AGENTS.md because evaluations is a new responsibility/trust boundary. Keep it compact and specific:\n- cases own reusable domain/source truth and interviewee-visible inputs;\n- protocols own prompts/runners/procedures;\n- oracles own reviewed expected claims/answer keys and must remain outside interviewee/model inputs;\n- generated/observed run evidence belongs under docs/evidence/evaluations, not evaluations;\n- preserve provenance and never silently overwrite immutable snapshots;\n- use test-only output paths for tests.\nIf root Brunch AGENTS.md needs a one-line pointer for this boundary, add only the minimal trigger.\n\nUpdate run.ts minimally:\n- resolve v0-prompt beside runner;\n- resolve case inputs from the cases path without duplicating files;\n- default production output to docs/evidence/evaluations/process-model-elicitation/baseline/transcripts;\n- support one explicit test-only output-directory environment variable or existing local pattern so tests never mutate committed evidence;\n- update generated metadata/path comments without changing experiment behavior.\n\nUpdate:\n- packages/core/package.json baseline:run path\n- packages/core/test/architecture/baseline-runner.test.ts to copy/use protocol and cases separately and isolated output\n- workspace/prune/copy config only if inspection proves evaluations would otherwise be omitted from the actual execution/build boundary\n- docs/INDEX.md: register the immutable evidence bundle; executable evaluations files are not docs index entries\n- docs/agents/documentation.md: remove transitional legacy planning/history exception now that migration is complete; retain role rules\n- packages/core/test/architecture/docs-index.test.ts: remove transitional baseline exception and prohibit all docs/planning and docs/history documents; retain coverage/link/protocol rules\n- docs/reference/architecture/topology.md and every operational/internal Markdown link/path consumer\n- README/AGENTS only if needed\n\nDelete empty docs/planning directories as a natural result; do not create stubs. Preserve transcript/readout bytes except required link repairs. Do not rewrite immutable raw JSON. Do not alter evaluation semantics or prompts.\n\nRun formatting/static lint/typecheck/full package unit tests/build/focused docs-index and baseline-runner tests/git diff --check. Return complete move/edit manifest, exact environment variable/API behavior added, verification results, and confirmation no docs/planning or docs/history remains.", - "description": "Split baseline evaluation assets" -} -``` - -## User - -**Tool Result:** `TU-034CiNAIx9e8QMh2A4PNDB` - -``` -Implemented the approved baseline split. - -- Moved cases, protocols/runner, and immutable evidence to the specified locations. -- Added compact `evaluations/AGENTS.md` and root pointer. -- Added `BRUNCH_BASELINE_TEST_OUTPUT_DIR`; production defaults to `docs/evidence/evaluations/.../transcripts`. -- Updated package scripts, tests, docs/index/topology, links, and prune config. -- Transcript and raw JSON bytes remain unchanged. -- No `docs/planning`, `docs/history`, or `evaluations/oracles` directory remains. -- Nothing staged or committed; Linear untouched. - -Verification passed: -- Formatting and ESLint -- Typecheck -- Full core unit suite: 114 tests -- Build -- Focused docs-index/baseline-runner: 11 tests -- Runner syntax, Python compile, `git diff --check` -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY cold review of the current baseline/evaluation split diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify/stage/commit. Intent: cases and interviewee-visible truth under evaluations/cases; runner/prompts/procedures under evaluations/protocols; no oracle until reviewed expected claims exist; immutable observed output under docs/evidence/evaluations; test runs never mutate committed evidence; production command still works from repo/package contexts; prune/build includes required assets; docs planning/history fully gone; transcript/raw JSON semantics preserved. Inspect git diff --find-renames, run.ts path resolution, package script, baseline-runner test, workspace/prune config, AGENTS guidance, INDEX/docs tests, topology links, and current tree. Check for information-wall violations, path arithmetic errors, unsafe output override available in production, tests that recreate wiring the production entrypoint lacks, overbroad test exceptions, accidental evidence edits, and missing consumers. Return actionable findings with file/line and smallest fix; pass/fail; exact manifest paths if passed after fixes.", - "description": "Review evaluation split" -} -``` - -## User - -**Tool Result:** `TU-034CiUV29OXgYfHk0gz4Qh` - -``` -## Verdict: **FAIL** - -### Finding - -**High — production can redirect or overwrite output through a test-only environment variable.** - -- `evaluations/protocols/process-model-elicitation/baseline/run.ts:118-125` -- `packages/core/turbo.json:4-10` - -`BRUNCH_BASELINE_TEST_OUTPUT_DIR` is accepted unconditionally and explicitly forwarded by the production Turbo task. A real API-backed run can therefore write outside immutable evidence storage, defeating the evidence-location contract. - -**Smallest fix:** only honor the override when `BRUNCH_BASELINE_ANTHROPIC_MODULE` is also set; otherwise reject it. Remove it from `baseline:run.passThroughEnv`. Add a regression test proving an override without the stub is refused before any API call. - -### Passed checks - -- Case/protocol information wall is correct; no oracle exists. -- Historical transcripts and raw JSON are byte-identical after relocation. -- No accidental evidence edits beyond readout link repair. -- Production runner works from HASH root and package directory. -- Path arithmetic and package script work. -- Focused tests: 11 passed. -- Typecheck, ESLint, formatting, build, and `git diff --check` passed. -- Prune dry-run includes `evaluations/`. -- Retired `docs/planning` and `docs/history` trees are absent. -- INDEX and topology links resolve. -- Tests use isolated output and preserve production-relative topology. - -No files were modified, staged, or committed. A passing manifest is not applicable until the finding is fixed. -``` - -## Assistant - -The review found one real trust-boundary flaw: the test output override was available to production runs. I’m constraining it to the existing stubbed-model test mode and adding a regression test that rejects any production override before an API call. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT the current evaluation split in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Read current files before patching. Fix only the review finding:\n- In evaluations/protocols/process-model-elicitation/baseline/run.ts, BRUNCH_BASELINE_TEST_OUTPUT_DIR may be honored only when BRUNCH_BASELINE_ANTHROPIC_MODULE is also set. If output override is set without the stub module, fail clearly before any API/module call or output mutation.\n- Remove BRUNCH_BASELINE_TEST_OUTPUT_DIR from packages/core/turbo.json baseline:run.passThroughEnv. Preserve whatever pass-through is actually needed for production and stub tests.\n- Add a focused regression test in packages/core/test/architecture/baseline-runner.test.ts proving an override without the stub module is rejected before any API call and does not create output.\nDo not alter experiment behavior, prompt/case files, evidence snapshots, stage, commit, or touch Linear. Run formatting, lint, typecheck, full unit tests, build, focused baseline/docs tests, runner syntax, and git diff --check. Return exact change and results.", - "description": "Constrain baseline test output" -} -``` - -## User - -**Tool Result:** `TU-034CiYHsMgYJPEYxMWvWp6` - -``` -Implemented the evaluation-split review fix: - -- `run.ts`: rejects `BRUNCH_BASELINE_TEST_OUTPUT_DIR` unless `BRUNCH_BASELINE_ANTHROPIC_MODULE` is set, before SDK/module loading or filesystem mutation. -- `turbo.json`: removed output override pass-through; retained API key and stub-module pass-through. -- `baseline-runner.test.ts`: added regression coverage proving exit failure, zero API requests, and no output directory creation. - -Verification passed: - -- Formatting and ESLint -- Typecheck -- Full unit suite: 115/115 -- Build -- Focused baseline/docs tests: 12/12 -- Runner syntax check -- `git diff --check` - -Nothing staged, committed, or sent to Linear. Existing unrelated worktree changes were untouched. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final review of the baseline/evaluation split in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Confirm prior output-override finding is fixed, test catches it before module/API/filesystem effects, Turbo no longer exposes the override to production, no unexpected/foreign changes exist, immutable evidence bytes remain unchanged except required readout links, no docs/planning/history remains, and all consumers resolve. Return pass/fail, any actionable finding, exact current diff manifest for literal staging, and clarify what the prior implementer meant by 'unrelated worktree changes'. Do not modify/stage/commit.", - "description": "Recheck evaluation split" -} -``` - -## User - -**Tool Result:** `TU-034Cic59BsYV5E0hbM154G` - -``` -## PASS - -No actionable findings. - -- Output override is rejected before SDK/module import, API calls, input reads, or output-directory creation. -- Regression test proves zero API calls and no filesystem output; all 5 runner tests pass. -- Turbo passes only `ANTHROPIC_API_KEY` and the test module override—not `BRUNCH_BASELINE_TEST_OUTPUT_DIR`. -- Immutable transcript/raw/model bytes are unchanged. Readout changed only to repair its protocol link. -- `docs/planning/` and `docs/history/` are absent. Remaining mentions are historical/archive references. -- Documentation links and consumers resolve. -- No foreign changes detected. The sole change outside Brunch, `.github/actions/prune-repository/prune.py`, is directly required to include `evaluations/` in pruned CI checkouts. Thus “unrelated worktree changes” appears to have meant “outside the Brunch directory,” not semantically unrelated work. - -### Verification - -- ESLint: pass -- Formatting: pass -- Typecheck: pass -- Unit tests: 115/115 pass -- Build: pass - -### Exact staging manifest - -```text -.github/actions/prune-repository/prune.py -libs/@hashintel/brunch-agent/AGENTS.md -libs/@hashintel/brunch-agent/docs/INDEX.md -libs/@hashintel/brunch-agent/docs/agents/arc-close.md -libs/@hashintel/brunch-agent/docs/agents/documentation.md -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/readout.md -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-1-model.txt -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-1.md -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-1.raw.json -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-2-model.txt -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-2.md -libs/@hashintel/brunch-agent/docs/evidence/evaluations/process-model-elicitation/baseline/transcripts/condition-2.raw.json -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/opening-message.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/protocol.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/readout.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/run.ts -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/situation-pack.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-1-model.txt -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-1.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-1.raw.json -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-2-model.txt -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-2.md -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/transcripts/condition-2.raw.json -libs/@hashintel/brunch-agent/docs/planning/process-model-elicitation/baseline/v0-prompt.md -libs/@hashintel/brunch-agent/docs/reference/architecture/topology.md -libs/@hashintel/brunch-agent/evaluations/AGENTS.md -libs/@hashintel/brunch-agent/evaluations/cases/process-model-elicitation/baseline/opening-message.md -libs/@hashintel/brunch-agent/evaluations/cases/process-model-elicitation/baseline/situation-pack.md -libs/@hashintel/brunch-agent/evaluations/protocols/process-model-elicitation/baseline/protocol.md -libs/@hashintel/brunch-agent/evaluations/protocols/process-model-elicitation/baseline/run.ts -libs/@hashintel/brunch-agent/evaluations/protocols/process-model-elicitation/baseline/v0-prompt.md -libs/@hashintel/brunch-agent/packages/core/package.json -libs/@hashintel/brunch-agent/packages/core/test/architecture/baseline-runner.test.ts -libs/@hashintel/brunch-agent/packages/core/test/architecture/docs-index.test.ts -``` -``` - -## Assistant - -The evaluation split now passes review and adds one necessary repository-wide change: the prune action copies `evaluations/` into CI checkouts. No unrelated work is present. I’m committing this second unit before touching Linear. - -**Tool Use:** `Task` - -```json -{ - "prompt": "COMMIT the verified evaluation split in /Users/lunelson/Code/hashintel/hash. Inspect status first; abort if anything differs from the reviewed manifest or a foreign change appears. Stage exactly the literal paths in current status for this split, including `.github/actions/prune-repository/prune.py`, old baseline deletions/new destinations, AGENTS/index/guidance/topology updates, package.json, turbo.json if currently changed, runner test, and docs-index test. Do not use `git add .`, `git add -A`, wildcard staging, or stage unrelated paths. Verify staged diff recognizes moves where possible, no immutable transcript/raw/model content changed, no docs/planning or docs/history remains, no unstaged split changes remain, and no unrelated staged files exist. Commit with message `Separate evaluation assets from evidence`. Do not push or touch Linear. Return SHA, staged stats, and post-commit status.", - "description": "Commit evaluation topology" -} -``` - -## User - -**Tool Result:** `TU-034CifxaWzAh8mkiqryjoa` - -``` -Committed `Separate evaluation assets from evidence`. - -- SHA: `daf7778bdc229b4565ef6c7202052e6977f02f5a` -- Stats: 22 files, 168 insertions, 49 deletions -- Immutable transcript/raw/model files: unchanged, recognized as 100% renames -- Pre-commit checks: passed -- Post-commit status: clean -- No push or Linear changes performed. -``` - -## Assistant - -Repository cutover is committed and clean. I’m now repairing the approved Linear pointers against the final tree. The issue bodies will otherwise remain byte-for-byte stable: no title, prose-contract, execution-record, state, hierarchy, assignment, label, or comment changes. Mixed directory pointers will be replaced by the smallest explicit role-specific path list their sentence actually requires. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the approved Linear path repair for FE project `brunch-agent`, using /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent as source of truth. External writes are explicitly approved, but ONLY issue-description artifact-pointer repairs are authorized. Read docs/agents/issue-writing.md and issue-tracker.md first and load cli-linear/ds-writing-issues if available. Do not modify repo files. Do not change titles, states, hierarchy, assignments, labels, projects, comments, or substantive issue contracts/execution records.\n\nProcess:\n1. Re-query all live descriptions and identify every occurrence of `libs/@hashintel/brunch-agent/docs/planning/`, `docs/history/`, exact `docs/spec.md`, or the old relative Brunch paths found in the prior 16-issue audit.\n2. Save original descriptions outside the repo in a temporary directory for comparison/recovery during this operation. Use file-based description updates, not inline shell strings.\n3. Replace only path/link text and the minimum connective text required when one old mixed directory becomes multiple destinations. Preserve Markdown, issue contract, agent notes, and all non-path wording.\n4. Use these final canonical destinations:\n- docs/planning/_shared/STEERING.md -> docs/control/STEERING.md\n- COORDINATION.md -> docs/control/COORDINATION.md\n- SPEC-LEDGER.md -> docs/control/SPEC-LEDGER.md\n- hash-monorepo-import-plan.md -> docs/archive/migrations/hash-monorepo-import-plan.md\n- docs/spec.md or docs/planning/elicitation-kernel/spec.md -> docs/specs/elicitation-kernel.md\n- plugin-contract-spec.md -> docs/specs/plugin-contract.md\n- ir-design.md -> docs/specs/intermediate-representation.md\n- petrinaut-integration-spec.md -> docs/specs/petrinaut-integration.md\n- topology.md -> docs/reference/architecture/topology.md\n- capture-store-plain.md -> docs/reference/architecture/capture-store.md\n- research/** -> docs/reference/research/** using actual final filenames/tree\n- deep-read and Flue audit records -> docs/evidence/proofs/audits/**\n- completed review/remediation/refactor records -> docs/archive/engineering/legibility/**\n- recommendation-demo-vehicle -> docs/archive/decisions/superseded/recommendation-demo-vehicle.md\n- grilling/penciled notes -> docs/archive/planning-inputs/**\n- old elicitation-kernel non-spec history -> docs/archive/elicitation-kernel/**\n- baseline/opening-message.md and situation-pack.md -> evaluations/cases/process-model-elicitation/baseline/**\n- baseline/protocol.md, v0-prompt.md, run.ts -> evaluations/protocols/process-model-elicitation/baseline/**\n- baseline/readout.md and transcripts/** -> docs/evidence/evaluations/process-model-elicitation/baseline/**\nFor old directory pointers spanning mixed roles, inspect the sentence and replace with the smallest explicit list of relevant canonical paths; do not invent a new umbrella directory. If intent is materially ambiguous (rather than mechanically inferable from file names/context), skip that occurrence and report it.\n5. Update each affected issue once. Treat connection failures as unknown outcome: re-read before any retry.\n6. Re-query all 82 project issues after writes. Verify zero remaining live description occurrences of old planning/history/spec paths, and compare each changed issue pre/post to prove only path/link/connective text changed. Report issue keys changed, exact old->new substitutions per issue, skipped ambiguities, remaining old occurrences, and confirmation no other fields/actions changed.\n\nPrior audit found 16 issues/30 occurrences: FE-1464, 1437, 1432, 1431, 1424, 1422, 1420, 1419, 1407, 1404, 1400, 1383, 1382, 1374, 1366, 1357. Re-query rather than assuming this list is unchanged.", - "description": "Repair Linear artifact pointers" -} -``` - -## User - -**Tool Result:** `TU-034CijpQ3BHuczbWRHejnC` - -``` -Completed the Linear path repair. - -- Updated 17 issues: FE-1357, FE-1366, FE-1374, FE-1382, FE-1383, FE-1397, FE-1400, FE-1404, FE-1407, FE-1419, FE-1420, FE-1422, FE-1424, FE-1431, FE-1432, FE-1437, FE-1464. -- FE-1397 was newly discovered beyond the prior 16-issue audit. -- Re-queried all 82 project issues: zero old planning/history/spec path occurrences remain. -- Post-write descriptions exactly matched prepared file-based updates. -- Titles, states, hierarchy, assignments, labels, projects, and all other queried fields were unchanged. -- No ambiguities were skipped. -- No repository files or comments were changed. - -Substitutions followed the requested mappings. FE-1424 and FE-1357 mixed-directory references were replaced with explicit canonical destination lists; all other changes were direct path substitutions. Temporary recovery copies were retained during execution and removed after successful verification. -``` - -## Assistant - -The approved Linear repair updated 17 issues—the original 16 plus FE-1397, discovered by the fresh query—and reports zero obsolete pointers. I’m independently checking every new path resolves in the migrated tree and that the issue edits did not introduce malformed or umbrella-path substitutions. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY independent verification of the just-completed Linear description path cutover. Work from /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent; do not modify repo or Linear. Query all 82 FE brunch-agent project issue descriptions. Verify: (1) zero remaining live description occurrences of old docs/planning, docs/history, docs/spec.md, docs/planning/elicitation-kernel, old baseline paths; (2) every new Brunch repository path inserted into the 17 changed issues exists in the current committed tree or is an intentional directory that exists; (3) mixed replacements in FE-1424 and FE-1357 are explicit role-specific paths rather than a new umbrella; (4) Markdown link syntax/path connective wording is not malformed; (5) issue activity/updated fields are consistent with description-only edits and no comments/state changes if accessible. Sample or inspect all 17 descriptions, not just grep counts. Return pass/fail, exact issues/paths for any finding, count of current canonical path occurrences, and any limitation of independent verification.", - "description": "Verify Linear path cutover" -} -``` - -## User - -**Tool Result:** `TU-034Cip0by9WTu0Fx23KNq0` - -``` -## Verdict: **FAIL — one path finding** - -Audited all 82 project descriptions and manually inspected all 17 changed issues. - -- **Old paths:** PASS — zero occurrences of `docs/planning`, `docs/history`, `docs/spec.md`, `docs/planning/elicitation-kernel`, or old baseline paths. -- **New paths:** FAIL — FE-1357 references `docs/reference/research/truck-fleet-source-dossier.md`, which is absent from the current committed tree. The description says it exists only on branch `research/truck-fleet-dossier`. All other inserted concrete paths/directories exist. -- **Mixed replacements:** PASS — FE-1424 and FE-1357 use explicit role-specific `archive`, `control`, `evidence`, `reference`, `specs`, and `evaluations` paths; neither introduces a replacement umbrella. -- **Markdown/connective wording:** PASS — no malformed links, unbalanced delimiters, or broken path wording found. -- **Activity:** PASS with limitation — the 17 updates are sequential at `13:15:40–56Z`, attributed to Lu, with no contemporaneous comments, state changes, or title changes. Linear’s latest history records expose `changes: null`, so the API does not explicitly label them description-only edits. - -Current canonical path occurrences across all 82 descriptions: **56**: - -- `docs/archive/`: 11 -- `docs/control/`: 3 -- `docs/evidence/`: 6 -- `docs/reference/`: 20 -- `docs/specs/`: 12 -- `evaluations/cases/`: 1 -- `evaluations/protocols/`: 3 - -Repository and Linear were not modified. -``` - -## Assistant - -One verification exception remains: FE-1357 now names the canonical future destination for a truck-fleet dossier that the issue itself says exists only on another branch. I’m checking whether that is a valid branch-qualified pointer or an accidental claim that the file exists here before changing the issue again. - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-adjudicate", - "arguments": "Adjudicate whether FE-1357's path docs/reference/research/truck-fleet-source-dossier.md is a valid branch-qualified future destination or a broken current-tree pointer requiring repair after the approved topology migration." -} -``` - -## User - -**Tool Result:** `TU-034CivO2bdh5ABHZYV3i7U` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-adjudicate\">\n# ds-adjudicate Skill\n\n\n<!-- Claim discrimination adapted from antithesishq/antithesis-skills@b69ee7c (Apache-2.0); procedure specified by https://github.com/lunelson/dogsled/issues/54 -->\n\n# Adjudicate\n\nA claim is untrusted until a run in the **real environment** could have falsified it. The output is a **verdict** backed by a **discriminator**.\n\n`ds-tdd` owns the red-green loop. `ds-diagnosing-bugs` owns a tight loop for a known failure — including after this skill returns CONFIRMED and the job is to fix. This skill owns the gate from claim to verdict.\n\n## Gate\n\nValidate the claim when the work would otherwise state it as behavior or a guarantee, derive a requirement, risk, test, or fix from it, or use it to accept or reject another finding. Leave every other claim **inert**.\n\n**Done when** the claim is inert and you stopped, or it is named as load-bearing and you continue.\n\n## Discriminate\n\nFill the **claim record** as you go:\n\n```markdown\nClaim:\nRelied on by:\nClaimed guarantee or observed behavior:\nCompeting explanation:\nPrimary evidence examined:\nDiscriminating probe:\nDiscriminating observation:\nRemaining uncertainty:\n```\n\n1. **Normalize** to the narrowest falsifiable statement. Separate the claimed guarantee from anything observed.\n2. **Name the reliance** — the decision that changes if the claim is true. None means inert; stop.\n3. **Name a competing explanation before searching.** Prefer an ordinary rival: reporter configuration, environment, stale documentation, misuse, an adjacent failure, or a different mechanism with the same symptom.\n4. **Inspect primary evidence** to locate the probe: the code path, raw logs, configuration, chronology, issue resolution. Summaries locate evidence; they do not replace it.\n5. **Design a discriminating probe** — an observation that would differ under the claim and its competitor. A run that fits both is not a discriminator.\n\n**Done when** the claim record has a falsifiable statement, a named reliance, a competitor written before any probe ran, and a probe that would come out differently under each — or you stopped at inert.\n\n`ds-triage` reproduces reporter claims; it does not invoke this procedure.\n\n## Confirm\n\nRun that probe in the **real environment** — the one where the bug is claimed to occur. Record the command and its output, secrets redacted as `<REDACTED>`. Quote the discriminating observation into the claim record.\n\nA verdict input is a run that could have falsified the claim. A reading of the code is not a verdict input. A constructed environment is **fabricated**. If you cannot run in the real environment, skip Independent review: go to Verdict as **FABRICATED-ENV** when the offered repro was constructed; otherwise leave **unresolved** and stop.\n\nIf the observation is missing or fits both explanations, leave the claim **unresolved** and stop.\n\n**Done when** the probe has been run in the real environment and the observation is quoted, or you left the claim unresolved, or you skipped to Verdict as FABRICATED-ENV.\n\n## Independent review\n\n**Lint-adjacent** claims skip to Verdict. **Shipped-code** or **security** claims take the full gate.\n\nIndependent re-check, split review, and the contrarian pass are the **measurable-hypothesis** tier. This context is the `ds-cold-review` **caller**.\n\n1. **Re-check.** Question: re-derive the repro from the claim text and observe the discriminator.\n2. **Split.** Produce an inspectable artifact (failing test, trace, screenshot). Question A: does the artifact show the claim? Question B: does the producing code generate that artifact?\n3. **Contrarian.** Question: refute the claim.\n\nAn unresolved call from those reviews blocks CONFIRMED.\n\n**Done when** lint-adjacent skipped these, or every dispatched review has returned.\n\n## Verdict\n\nRecord exactly one:\n\n- **CONFIRMED** — the discriminator supports the claim over the competitor, from a real-environment run. Shipped-code and security claims also need the independent-review returns. Pin a regression that goes red on this bug before reporting the finding resolved (`ds-tdd` owns what that test is). For lint-adjacent, an existing lint rule that already catches it is the pin.\n- **UNREPRODUCIBLE** — the real-environment run does not produce the claimed behavior, or the discriminator favors the competitor.\n- **FABRICATED-ENV** — the only run was in an environment the claimant constructed.\n- **DUPLICATE** — an existing pin or confirmed finding is the same discriminator.\n- **WONT-FIX** — discriminated, and we will not act (intended behavior, accepted limitation, out of scope). Distinct from inert.\n\nUnresolved is not a verdict.\n\n**Done when** exactly one verdict is recorded with the claim record, and CONFIRMED includes its pin.\n\n## Failure modes\n\n- **Headline confirmation** — repeating a report's conclusion after reproducing only its symptom.\n- **Non-discriminating execution** — a run that could look the same under every plausible explanation.\n- **Guarantee laundering** — treating documentation or design intent as a fact about the implementation.\n- **Effort promotion** — treating a claim as true because it was investigated at length.\n- **Source deference** — accepting a high-standing source without checking the evidence the claim depends on.\n- **Fabricated-environment proof** — treating a demo from a constructed environment as evidence about the real one.\n\n\nARGUMENTS: Adjudicate whether FE-1357's path docs/reference/research/truck-fleet-source-dossier.md is a valid branch-qualified future destination or a broken current-tree pointer requiring repair after the approved topology migration.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-adjudicate\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-adjudicate/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY adjudication in /Users/lunelson/Code/hashintel/hash. Do not modify repo, branches, or Linear. Claim: FE-1357's `docs/reference/research/truck-fleet-source-dossier.md` is a valid branch-qualified future destination rather than a broken current-tree pointer. Reliance: whether to edit FE-1357 again. Competing explanation: the migration update invented a nonexistent path and removed the only usable pointer. Inspect the exact FE-1357 sentence/context and available local/remote git refs for the branch it names (`research/truck-fleet-dossier` or exact stated ref). Without checking out or changing worktree, use git show/ls-tree against that branch/ref to locate the dossier and its current old path; inspect its content/title if needed. Determine whether the new role path is the mechanically correct destination when that branch is reconciled with the topology migration, and whether the issue clearly qualifies it as branch-only/not current-main. Return a ds-adjudicate claim record, evidence commands/observations, and verdict or unresolved. If no ref is available, state that; do not infer.", - "description": "Adjudicate FE-1357 dossier path" -} -``` - -## User - -**Tool Result:** `TU-034CivaXZXmHHSQ52I98WM` - -``` -## Claim record - -**Claim:** FE-1357’s `docs/reference/research/truck-fleet-source-dossier.md` is a branch-qualified future destination, not a current-tree pointer. -**Relied on by:** Whether FE-1357 needs another edit. -**Competing explanation:** Migration invented a nonexistent path and removed the only usable pointer. - -**Primary evidence:** -- Current `linear issue view FE-1357` says: - > “dossier on branch `research/truck-fleet-dossier` (`docs/reference/research/truck-fleet-source-dossier.md`).” -- Archived pre-migration FE-1357 used the same branch qualifier with: - `docs/planning/process-model-elicitation/research/truck-fleet-source-dossier.md`. -- Commit `3b4a73f4a7` moved research documents from that legacy research directory into `docs/reference/research/`; the documentation protocol assigns stable source material there. -- `git ls-remote`, `git show-ref`, `git cat-file`, and local ref scans found no available `research/truck-fleet-dossier` ref. HEAD also contains no dossier. - -**Discriminating probe:** Inspect the named branch with `git ls-tree`/`git show`, confirming its old path and applying the topology migration mechanically. - -**Observation:** The required branch/ref is unavailable locally and from `origin`; therefore its tree and dossier content could not be inspected. - -**Remaining uncertainty:** Whether the branch actually contains the dossier at the stated old path. - -## Result - -**Unresolved — no verdict.** - -The issue clearly qualifies the path as branch-only, so it is not presented as a current-main pointer. The new path is mechanically consistent with the role-topology migration. However, without the named ref, the dossier’s existence and exact reconciliation cannot be confirmed. Do not edit FE-1357 merely because the path is absent from the current tree; recover the branch/ref before asserting full validity. No repository or Linear changes made. -``` - -## Assistant - -FE-1357 is not actually a broken current-tree claim: its sentence explicitly qualifies the dossier as branch-only. The named branch is no longer available locally or on origin, so existence remains unresolved, but the role path is the correct migration target if that branch is recovered. I’m leaving the issue unchanged and treating this as an external evidence gap, not a topology failure. - -Repository and Linear cutovers are complete. I’m running the mandatory Brunch arc-close control pass now to reconcile index, issue graph, steering/coordination authority, and stale-path residue. - -**Tool Use:** `skill` - -```json -{ - "name": "arc-close", - "arguments": "Close the approved Brunch documentation topology and steering-protocol work arc. Verify repository migration, Linear pointer cutover, INDEX, role authorities, evaluation lifecycle, STEERING/COORDINATION drift, stale paths, and evidence/legibility deposits. Do not create or edit issues without further approval beyond the already-completed path repairs." -} -``` - -## User - -**Tool Result:** `TU-034Cj1Dhf9ouWFYKqDXC8w` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"arc-close\">\n# arc-close Skill\n\n\nRead `docs/agents/arc-close.md` from the Brunch context root and execute its five steps in order.\nThis wrapper carries no duplicate procedure. Report the required checks and whether each\nconditional control surface changed; never persist a no-op evaluation.\n\n\nARGUMENTS: Close the approved Brunch documentation topology and steering-protocol work arc. Verify repository migration, Linear pointer cutover, INDEX, role authorities, evaluation lifecycle, STEERING/COORDINATION drift, stale paths, and evidence/legibility deposits. Do not create or edit issues without further approval beyond the already-completed path repairs.\n\nBase directory for this skill: file:///Users/lunelson/Code/hashintel/hash/libs/%40hashintel/brunch-agent/.agents/skills/arc-close\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the canonical Brunch arc-close procedure after reading /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/agents/arc-close.md. Work from that context root. Current repo worktree should be clean after commits 397405a276, 3b4a73f4a7, daf7778bdc. Linear description path repairs are complete and approved; do not perform any further external writes/comments/state/hierarchy changes. Use read-only Linear queries/linear:graph where required. Follow every step in order and report conditional no-op vs changed.\n\nScope of this arc: steering protocol + role-based docs topology + evaluation asset/evidence split + Linear path repair. Verify docs inbox/INDEX, fresh Linear registry/graph, control/spec ledger/coordination/steering drift, stale tense/paths, proof/witness/oracle disposition, external gates, and repository status. FE-1357's branch-qualified truck-fleet dossier remains unresolved because the named branch/ref is unavailable; the issue explicitly calls it branch-only, so treat it as an external evidence gap, not an existing-tree path failure.\n\nIf the canonical close procedure requires repository edits because this arc changed durable truth, make the smallest correct edits, read before patching, do not touch unrelated content, and run applicable checks. Do not stage or commit—return edits for parent review. If no edit is warranted, persist nothing. Return each close step's evidence/result, any changed files, remaining blockers, and recommended final status.", - "description": "Run Brunch arc-close" -} -``` - -## User - -**Tool Result:** `TU-034Cj1OkGORaj5Una48yGB` - -``` -Executed canonical arc-close in order. - -1. **Inbox/index — no-op** - - Seven SDCPN inbox files remain intentionally unsettled and are covered by `docs/INDEX.md`. - - Authoritative `docs-index` tests passed. - -2. **Linear registry/references — no external writes** - - Fresh graph: 35 open issues, 16 hard edges, no cycles. - - All 13 parentless issues are recognized roots or documented exceptional roots. - - Open issue descriptions showed no obsolete role-topology paths. - - FE-1357’s truck-fleet dossier branch/ref is unavailable; treated as an external evidence gap, not a tree failure. - -3. **Spec ledger — no-op** - - Arc changed documentation/control topology, not milestone-one obligation evidence. - -4. **Coordination — changed** - - Updated stale FE-1437 landing tense while retaining the `main` state gate. - -5. **Steering/proof — changed** - - Added explicit external-gate records for Dora’s use-case confirmation and the unavailable truck-fleet dossier. - - Proof/witness/oracle disposition otherwise unchanged; no new proof was produced. - -6. **Tense/status — changed as above** - - Remaining historical/provisional language is appropriate to archived evidence or current uncertainty. - -**Files changed** -- `docs/control/COORDINATION.md` -- `docs/control/STEERING.md` - -**Verification** -- ESLint: passed -- TypeScript: passed -- Unit tests: 115 passed -- Build: passed -- `git diff --check`: passed -- Final worktree contains only the two unstaged edits above. - -**Remaining blockers** -- Truck-fleet dossier branch/ref unavailable. -- Dora’s business-use-case confirmation remains an external gate. -- Existing verification gaps remain: FE-1396, FE-1386, FE-1394. - -**Recommended status:** arc-close complete with two control-surface edits ready for parent review; do not close the external evidence gap as resolved. -``` - -## Assistant - -Arc-close found two durable updates rather than a no-op: FE-1437’s tense had drifted, and the active external gates were not represented in the steering surface. I’m reviewing those two compact control edits for factual accuracy and churn before committing them. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY review of the current diff in docs/control/COORDINATION.md and docs/control/STEERING.md under /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify/stage/commit. Check against fresh facts: commits 397405a276, 3b4a73f4a7, daf7778bdc exist locally; FE-1437 live state is Ready for review and import authority threshold crossed but `main` gate remains; Dora business-use-case decision is not supplied in this thread; FE-1357 dossier points to unavailable branch `research/truck-fleet-dossier`. Ensure edits are compact, current-state not history accumulation, use precise owner/source/watch trigger/last-checked/consequence fields for external gates, do not claim unknown facts, and preserve strategic vs coordination authority. Return pass/fail and smallest fixes with lines.", - "description": "Review arc-close control edits" -} -``` - -## User - -**Tool Result:** `TU-034Cj7IUHWzXPI7InpTsuq` - -``` -## Result: Fail - -1. **`COORDINATION.md:18–19`** — FE-1437 wording is imprecise. It should state: authority threshold crossed; issue is **Ready for review**; work remains gated until the branch reaches `main`. “Completed the authority cutover” overstates completion. - -2. **`STEERING.md:49`** — gate ownership/source is ambiguous. `FE-1382` is presented as owner without identifying an accountable person, while FE-1357 is the confirmed dossier source. Separate or clarify owner and source; do not assert ownership if unknown. - -Everything else passes: Dora’s decision remains explicitly unknown, the unavailable dossier branch is correctly treated as an evidence gap, the controls remain compact, and strategic versus coordination authority is preserved. All three supplied commits exist locally; the dossier branch does not. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT only docs/control/COORDINATION.md and docs/control/STEERING.md in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent after reading current diff.\n1. COORDINATION FE-1437 line: state precisely that the authority threshold crossed, FE-1437 is Ready for review, and the work remains gated until the branch reaches main. Avoid saying the cutover is completed.\n2. STEERING truck-fleet dossier external gate: do not use FE-1382 as a person/owner. Record owner as unresolved/unknown; source as FE-1357 plus historical build ticket FE-1382 (if that matches current text/evidence); retain named unavailable branch, watch trigger, last-checked date, and consequence. Keep one compact line/row consistent with the table.\nDo not modify other content, stage, commit, or touch Linear. Run docs-index tests and git diff --check. Return exact wording and results.", - "description": "Fix arc-close controls" -} -``` - -## User - -**Tool Result:** `TU-034Cj8rnuYK375vLJxYiVP` - -``` -Updated only the requested files; nothing staged or committed. - -- **COORDINATION.md:** “FE-1437 crossed the authority threshold in this branch and is Ready for review; the work remains gated until the branch reaches `main`.” -- **STEERING.md:** “Owner unresolved/unknown; source: FE-1357 plus historical build ticket FE-1382; FE-1357 names branch `research/truck-fleet-dossier` and `docs/reference/research/truck-fleet-source-dossier.md`” - -Verification: -- Docs-index: **7/7 passed** -- `git diff --check`: **passed** -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final review of current docs/control/COORDINATION.md and STEERING.md diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Verify both prior findings resolved, external gate rows remain compact/checkable, and no unsupported assertion or authority overlap remains. Do not modify. Return pass/fail, exact changed paths, and whether prior full verification plus focused docs tests are sufficient for this docs-only close unit.", - "description": "Recheck arc-close controls" -} -``` - -## User - -**Tool Result:** `TU-034CjB58VSNdhOccpquhOG` - -``` -**PASS — no findings.** - -Changed paths: -- `docs/control/COORDINATION.md` -- `docs/control/STEERING.md` - -Both prior findings are resolved: -- FE-1437 now distinguishes branch authority from the `main` landing gate. -- External gates are compact, checkable, and include all required fields. - -No unsupported assertions or authority overlap found. Branch/PR and missing-dossier claims were independently confirmed. - -Prior full verification plus focused docs tests are sufficient for this docs-only close unit. `git diff --check` passes. No files modified. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "COMMIT the verified arc-close reconciliation in /Users/lunelson/Code/hashintel/hash. Inspect status; stage exactly these two literal paths and no others:\n- libs/@hashintel/brunch-agent/docs/control/COORDINATION.md\n- libs/@hashintel/brunch-agent/docs/control/STEERING.md\nVerify staged set and no unstaged/foreign changes. Commit message: `Reconcile Brunch control surfaces`. Do not amend, push, or touch Linear. Return SHA and post-commit status.", - "description": "Commit arc-close reconciliation" -} -``` - -## User - -**Tool Result:** `TU-034CjDykjOAv2LcMOSbYWk` - -``` -Committed `Reconcile Brunch control surfaces`. - -SHA: `28f3e3bfa820265a9e6ac25e7f684cb7d2952cf3` - -Post-commit status: clean. No unstaged or foreign changes. -``` - -## Assistant - -The close reconciliation is committed. I’m doing one final read-only ledger check—clean worktree, expected four commits, zero stale Linear paths—then I’ll hand back the locked protocol, migrated topology, and remaining strategic gates. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final state check in /Users/lunelson/Code/hashintel/hash. Do not modify anything. Verify: git worktree clean; latest relevant commits 397405a276, 3b4a73f4a7, daf7778bdc, 28f3e3bfa8 present in order; no docs/planning or docs/history directories under Brunch; canonical docs/control/specs/evidence/reference/archive and evaluations cases/protocols exist; no evaluations/oracles; 82 live Linear project issue descriptions contain zero obsolete Brunch planning/history/spec paths; FE-1357 branch-only unresolved pointer remains explicitly qualified. Return concise pass/fail and any discrepancy.", - "description": "Verify final migration state" -} -``` - -## User - -**Tool Result:** `TU-034CjF1yt1gueWZT2KR2nX` - -``` -**PASS — no discrepancies.** - -- Git worktree clean. -- Commits present in required ancestry order: `397405a276` → `3b4a73f4a7` → `daf7778bdc` → `28f3e3bfa8`. -- Brunch has no `docs/planning`, `docs/history`, or `evaluations/oracles`. -- Required canonical docs and evaluation directories exist. -- All 82 Linear project issue descriptions contain zero obsolete planning/history/spec paths. -- FE-1357’s unresolved branch-only dossier pointer is explicitly qualified; branch/ref and file remain absent. -``` - -## Assistant - -Completed and locked: - -- Role-based docs topology and evaluation split. -- Canonical [steering protocol](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/agents/steering.md). -- 17 Linear descriptions repaired; all 82 verified free of stale paths. -- 115 tests, lint, typecheck, build, and link checks pass. -- Worktree clean; four local commits created, not pushed. - -Remaining external gates: Dora’s use-case decision and the unavailable FE-1357 dossier branch. - -## User - -Use-case decision will still be outstanding for a while; all Linear issues scoped to `brunch-agent` project are now assigned to me and new issues should also be so; but now didn't we say wanted to reduce the prose content of @docs/control/STEERING.md as much as possible? -- and OTOH if we do that, where does our agreed-upon strategy live? Do we need something like a decision log perhaps, which is not ADR decisions but strategic ones? (this could be append-only, to avoid churn). And finally: while I like the new topology, it doesn't really feel like we need both @docs/control/COORDINATION.md and @docs/control/STEERING.md , and I worry about how we keep the lifecycle of this setup disciplined and not drifting... - -FWIW I had a fresh agent "orient" itself in the migrated file structure and this is what it wrote; you can validate this against what you'd expect an agent to read: - ---- - -This directory is the **Brunch context root**: domain language, decisions, agent operating protocols, and the four child packages live here. It is not a Yarn/Turbo root. HASH’s repository root still owns lockfile, workspace discovery, and CI. HASH guidance wins on conflict. - -The distinctive fact is not the package layout. It is that this context treats **authority, proof, and close-out as executable protocol**, not as documentation hygiene. - ---- - -### What Brunch is - -Brunch is a **stateful elicitation harness**: an agent interviews a person about a target domain, extracts structured evidence, folds that evidence into a model, and projects the model (today: Gherkin; September: a CPS requirements model → SDCPN in Petrinaut). - -The glossary in `CONTEXT.md` is load-bearing. Shells are **substrate / UI / harness / plugin / binding**. Durable truth lives on a **target-document** (capture store + session logs), not on a rendered artifact. Sessions go quiet rather than close. Vocabulary avoidances are real: do not say “kernel,” “host,” “adapter,” or “spec” for the workpiece. - -Four packages here, one application elsewhere: - -- `packages/core` — harness (`@hashintel/brunch-agent`) -- `packages/binding-flue` — Flue substrate adapter -- `packages/transport-aisdk` — wire transport; must not depend on a binding -- `packages/plugin-gherkin` — first target plugin -- `apps/brunch-agent` — remote server and diagnostics -- `apps/petrinaut-website` — the only compile-time meeting point with Petrinaut - -Reusable Brunch and Petrinaut libraries stay mutually unaware (ADR-0004). - ---- - -### Authority: one truth, one home - -The central convention. Every durable fact has exactly one owner. Link; do not copy. - -| Truth | Authority | -| --- | --- | -| Current objective, cuts, beliefs, gates | `docs/control/STEERING.md` | -| Soft sequencing, seams, exceptional roots | `docs/control/COORDINATION.md` | -| Issue state, parentage, hard blockers | Linear (`FE` / project `brunch-agent`) | -| Required behavior | `docs/specs/` | -| Accepted decisions | `docs/adr/` | -| Observed proof | `docs/evidence/proofs/` | -| Evaluation runs | `docs/evidence/evaluations/` | -| Stable explanation | `docs/reference/` | -| Settled history | `docs/archive/` | -| Untriaged intake | `docs/inbox/` | -| Executable cases / protocols / oracles | `evaluations/` | -| Milestone-one spec discharge | `docs/control/SPEC-LEDGER.md` | - -`docs/INDEX.md` is the registry. Effort is metadata, not a folder. Retired trees (`docs/planning/`, `docs/history/`, `.scratch/`) must not come back. Git is the history of control surfaces; the files themselves stay current-only. - -Linear writes need **explicit approval** per operation. Reading is free. Approval for one mutation is not approval for adjacent ones. - ---- - -### The operating loop - -Work is not “pick the next unblocked ticket.” The claimable queue is an availability filter. Strategy lives in `STEERING`. - -**Steering** runs on start/resume without a proof target, or when objective, proof result, authority conflict, external gate, frontier value, or arc-close drift changes. Ordinary ticket movement does not trigger it. - -The pass is: **orient → choose → execute → reconcile → replan**. - -- Classify inputs as **fact**, **belief** (with confidence and evidence), **unknown** (with cheapest probe), or **external gate** (owner, source, last-checked, consequences). -- Choose **one proof frontier**, or a named pair. Record claim, proof bundle, cut, issue projection, stop/replan trigger. -- Execute on **real production entrypoints**. A fixture may supply domain inputs; it must not supply missing product wiring. -- Deposit each changed truth in exactly one authority. - -The evidence lifecycle is itself a convention: - -```text -corpus/case → reviewed fixture → production-path run → immutable snapshot - → validated claim → executable oracle -``` - -Hidden answer keys and oracles stay behind an **information wall**. They never become interviewee or elicitor inputs. UX, live-runtime, and demo-comprehension claims need a **human witness** unless the claim records why not. - ---- - -### Protocols as ceremonies - -`AGENTS.md` / `CLAUDE.md` route by trigger. Load only the matching compact protocol. - -| Trigger | Protocol | What it actually is | -| --- | --- | --- | -| Start, resume, pressure/proof/authority change | `steering.md` | Choose a falsifiable frontier | -| Create/mutate/structure issues | `issue-tracker.md`, `issue-writing.md`, `triage-labels.md` | Linear mechanics + house style | -| Add/move/settle documents | `documentation.md` | Intake → promote → index | -| Change terms or decisions | `domain.md` | Glossary + ADR conflict surfacing | -| Flue design choice | `flue-routing.md` | Symptom → Flue affordance, before writing a new layer | -| Significant agent-authored artifact | `legibility.md` | Re-render into another register; strain is the review | -| Architecture-sensitive move | `posture.md` | Epistemic defaults | -| Branches, stacks, PRs | `git-workflow.md` | Graphite, one issue per branch | -| Close a work arc | `arc-close` skill + `arc-close.md` | Mandatory landing control pass | - -**Issue writing** splits two audiences: a human-owned **contract** (plain technical prose: current state → consequence → intended change) above, and agent-maintained **`🏗️ Agent notes`** below. Titles start with an active verb. Do not rewrite a teammate-authored issue’s structure. In Linear bodies, issue references are full URLs; in the repo, they are bare IDs with a gloss. - -**Git**: Graphite (`gt`) for stack operations; plain git for commit/status/diff. Never `gh stack`. Branch `ln/fe-xxxx-keywords`. PR title `FE-XXXX: Linear title in sentence case`. Description is deposited at authoring time; an empty PR body on a heavy branch is a defect. HASH commit style applies: sentence case, imperative, no `feat:` prefixes. - -**Wayfinding**: a Linear map issue with typed children. Claim = assign yourself. Resolve = comment + Done + gist on the map. Product stubs are related, never duplicated, never closed by the map. Every project issue must be reachable from a recognized root (currently FE-1383 build, FE-1357 demo) or named under COORDINATION’s exceptional roots. - -**Arc close** is the required ceremony before landing a branch that closes an arc. Sequence: settle inbox + index → Linear orphan audit (`turbo run linear:graph --filter '@hashintel/brunch-agent'`) → spec ledger if affected → coordination if the graph or seams changed → steering only if a trigger fired → repair stale tense. Do not append no-op dated evaluations. Git is the history. - -**Legibility**: no claim without a way for it to fail. Re-render the central artifact into another register (plain prose, STE, or worked examples) and treat **strain** as the review yield. Capture channels must name their consolidation target. A handoff note is a deferral, not a deposit. - -**Posture** (the interesting override): `prototype`, but **stakes: high** and **horizon: current-milestone**. That combination means: prove and rewrite freely, but persisted capture data and merge gates **fail loudly**. Do not abstract until two real callers. Do not shim across a rewrite unless the boundary cannot be updated atomically. - ---- - -### Architecture conventions that constrain code - -**Three lanes** (ADR-0002, `flue-routing.md`): - -1. Shell-facing (UI, evals, deploy) — consume Flue directly; never wrap. -2. Agent-loop (tools, state, suspension) — translate in the binding against a named capability list. -3. Elicitation semantics + capture store — ours outright. - -Placement rules N1–N6 are gates, not taste: ask/sweep mechanism in core; plugin content exported from plugin packages and registered by hosts; applications are the only composition boundary; storage-port implementations live in the binding; experiment runners do not live in `packages/`. - -**Three-register IR** (ADR-0003): - -1. Typed **captures** (every semantic judgment at write time, contestable) -2. **Model** — a pure fold over active captures; never stored; never interprets -3. **Projections** (net, loss report) — consume the model, not the transcript - -Hiding LLM inference inside a read-time projection is not an available shortcut. FE-1480 currently challenges this; the honest responses are record the judgment as a capture, or amend the ADR. - -Executable gates to preserve: package-direction, Flue entrypoint, bundle, hermetic runtime. The walking skeleton is a retained CI pin, not disposable scaffolding. - ---- - -### Current strategic reading (as of 2026-08-24) - -The September outcome is a **review-and-revise** demo, not a cold-start interview: a reviewer opens a prebuilt CPS model/net, asks why an element is there, traces it to an utterance, makes a scoped correction in 3–5 turns, sees the live net change, hands off to optimisation. - -Most of the contract-bearing middle is still absent. Ask/suspend/sweep/store are real. Fold, demand, model, projection, controller, CPS plugin, existing-target session binding, and machine client-tool round-trip are not. - -The bet: build the smallest honest vertical loop through all three registers and the real Petrinaut entrypoint; let CPS discover the minimum plugin interface; generalize only what CPS and Gherkin both need. - -Four frontiers, ordered by learning dependency. **Frontier 0** (make the demo claim decidable, including the FE-1480 authority question) is the named next move; then Frontiers 1 and 2 in parallel; join at targeted correction; then deploy. Generic Gherkin completion and a second-target contract freeze no longer gate September. - -Cuts are sequencing, not dismissal: no frozen generic SDK, no full CPS ontology, no cold-start interview, no requirements-graph UI before the loop is green, no fake net mutation. - -External gates still open: Dora has not confirmed the use case; Linear has not been mutated to match the recommended FE-1476 hierarchy; FE-1331 (create-new-net) is an exposed conflict with ADR-0004 that this steering document does not silently re-defer. - ---- - -### How I will behave here - -I will load the matching protocol by trigger rather than carrying all of them. I will use glossary terms and surface ADR conflicts instead of overriding them. I will not treat “unblocked” as “next,” will not write Linear without approval, will not invent product wiring in a fixture, and will not close an arc without the arc-close pass. Proofs need a production path and a second-register snapshot. - -If you want a live steering pass next — classifying current facts vs beliefs and confirming whether Frontier 0 is still the right claim — say so and I will run that loop against Linear and the inbox rather than restating the documents. - -## Assistant - -Yes—the concern is real. The migration clarified *where* things live, but it may have preserved too many mutable control authorities. A compact current-state control plus an append-only strategic decision record is a plausible correction; I want to test it against the actual overlap and the fresh agent’s read behavior before recommending the exact cut. I’ll also fold in the new policy that every Brunch project issue defaults to you as assignee. - -**Tool Use:** `skill` - -```json -{ - "name": "dogsled:ds-codebase-design", - "arguments": "Evaluate whether Brunch's mutable STEERING and COORDINATION control surfaces should merge, with strategic decisions moved to an append-only record, while preserving a deep/simple authority model and disciplined lifecycle." -} -``` - -## User - -**Tool Result:** `TU-034CjQzANJ6KpLMAQAiF36` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"dogsled:ds-codebase-design\">\n# ds-codebase-design Skill\n\n\n<!-- Vendored from mattpocock/skills@84fdeff: skills/engineering/codebase-design -->\n\n# Codebase Design\n\nDesign **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.\n\n## Glossary\n\nUse these terms exactly — don't substitute \"component,\" \"service,\" \"API,\" or \"boundary.\" Consistent language is the whole point.\n\n**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service.\n\n**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface).\n\n**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for \"adapter\" when the seam is the topic; \"implementation\" otherwise.\n\n**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation.\n\n**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context).\n\n**Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside).\n\n**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.\n\n**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.\n\n## Deep vs shallow\n\n**Deep module** = small interface + lots of implementation:\n\n```\n┌─────────────────────┐\n│ Small Interface │ ← Few methods, simple params\n├─────────────────────┤\n│ │\n│ Deep Implementation│ ← Complex logic hidden\n│ │\n└─────────────────────┘\n```\n\n**Shallow module** = large interface + little implementation (avoid):\n\n```\n┌─────────────────────────────────┐\n│ Large Interface │ ← Many methods, complex params\n├─────────────────────────────────┤\n│ Thin Implementation │ ← Just passes through\n└─────────────────────────────────┘\n```\n\nWhen designing an interface, ask:\n\n- Can I reduce the number of methods?\n- Can I simplify the parameters?\n- Can I hide more complexity inside?\n\n## Principles\n\n- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.\n- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.\n- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape.\n- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.\n\n## Designing for testability\n\nGood interfaces make testing natural:\n\n1. **Accept dependencies, don't create them.**\n\n ```typescript\n // Testable\n function processOrder(order, paymentGateway) {}\n\n // Hard to test\n function processOrder(order) {\n const gateway = new StripeGateway();\n }\n ```\n\n2. **Return results, don't produce side effects.**\n\n ```typescript\n // Testable\n function calculateDiscount(cart): Discount {}\n\n // Hard to test\n function applyDiscount(cart): void {\n cart.total -= discount;\n }\n ```\n\n3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup.\n\n## Relationships\n\n- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).\n- **Depth** is a property of a **Module**, measured against its **Interface**.\n- A **Seam** is where a **Module**'s **Interface** lives.\n- An **Adapter** sits at a **Seam** and satisfies the **Interface**.\n- **Depth** produces **Leverage** for callers and **Locality** for maintainers.\n\n## Rejected framings\n\n- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.\n- **\"Interface\" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.\n- **\"Boundary\"**: overloaded with DDD's bounded context. Say **seam** or **interface**.\n\n## Going deeper\n\n- **Deepening a cluster given its dependencies** — see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing.\n- **Exploring alternative interfaces** — see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): several radically different interface sketches, then compare on depth, locality, and seam placement.\n- **Whether the module or seam should exist at all** — use /ds-subtraction.\n\n\nARGUMENTS: Evaluate whether Brunch's mutable STEERING and COORDINATION control surfaces should merge, with strategic decisions moved to an append-only record, while preserving a deep/simple authority model and disciplined lifecycle.\n\nBase directory for this skill: file:///Users/lunelson/.config/amp/plugins/dogsled/skills/ds-codebase-design\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n\n<skill_files>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-codebase-design/DEEPENING.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-codebase-design/DESIGN-IT-TWICE.md</file>\n<file>/Users/lunelson/.config/amp/plugins/dogsled/skills/ds-codebase-design/agents/openai.yaml</file>\n</skill_files>\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY design audit in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify anything. Analyze docs/control/STEERING.md, COORDINATION.md, SPEC-LEDGER.md, docs/agents/steering.md, issue-tracker.md, arc-close.md, documentation.md, docs/INDEX.md, and relevant git history since STEERING was introduced. Quantify current size/prose, headings, duplicated facts/issue mappings/sequencing/gates, update cadence/churn, and authority ambiguity between STEERING and COORDINATION. Test this proposed shape: one compact mutable current-state control surface plus an append-only strategic decision log distinct from ADRs. Compare at least: A) keep both and tighten, B) merge STEERING+COORDINATION, C) replace both with compact STATUS/CONTROL plus strategic decision log. Recommend the smallest deep interface, exact responsibilities, lifecycle rules, and what current sections move/delete/condense. Address whether SPEC-LEDGER remains separate. Return exact file/line evidence and a concrete compact document sketch; no implementation.", - "description": "Analyze control-surface overlap" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY orientation audit in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify anything. A fresh agent reported: Brunch is a stateful elicitation harness; glossary and package roles; one-truth-one-home table; steering loop/evidence lifecycle; all protocol ceremonies; issue-writing/git/wayfinding/arc-close/legibility/posture details; three-lane architecture and three-register IR; and a detailed current strategic reading (September review-and-revise, missing middle, current bet, four frontiers, cuts, open gates). Determine what an agent following current AGENTS.md pointers should normally read for a generic 'orient in this context root' request, and trace each category of the report to source files. Identify: (1) accurate and expected synthesis, (2) material inaccuracies/stale claims, especially cold-start scope and current Linear/assignment state, (3) content it likely over-read because trigger-specific protocols should stay unloaded, (4) important content it missed, (5) whether current guidance reliably produces a compact useful orientation or encourages reading the entire control apparatus. Return file/line evidence and a recommended default orientation read set versus conditional pointers.", - "description": "Validate fresh-agent orientation" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY control-lifecycle audit in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify repo or Linear. Read AGENTS.md, docs/agents/{steering,arc-close,documentation,issue-tracker,issue-writing}.md, docs/control files, docs-index/architecture tests, and current Linear project settings/issues. Verify the user's new fact: all current `brunch-agent` project issues are assigned to the current user, and inspect whether Linear offers a project/team default assignee mechanism available here. Determine the smallest durable rule ensuring every newly created project issue is assigned to the current user, while respecting issue authoring patterns and external-write approval. Then identify lifecycle drift risks if mutable control becomes one file plus append-only strategic decision log: trigger ownership, append criteria, supersession links, compaction, VCS conflict behavior, arc-close checks, index/test gates, and how no-op updates are prevented. Recommend exact guidance/test changes, but do not implement. Include query evidence and any Linear limitation.", - "description": "Audit lifecycle and assignment policy" -} -``` - -## User - -**Tool Result:** `TU-034CjRQDoNDE4tFerNeTHa` - -``` -# Read-only design audit - -No files were modified. - -## Finding - -The documented authority split is clear in theory but not preserved in practice. - -- `STEERING` owns objectives, choices, cuts, beliefs, and gates (`docs/agents/steering.md:76-84`). -- `COORDINATION` owns issue projection and soft edges (`docs/agents/steering.md:80-82`). -- Linear owns issue state, hierarchy, and hard blockers (`docs/control/STEERING.md:8-13`; `docs/control/COORDINATION.md:5-8`). - -Yet both mutable documents now restate the same strategic sequencing, issue mappings, gates, and unresolved decisions. The interface requires readers and writers to decide repeatedly which of two files owns a changed fact. - -**Recommendation: option C — replace both with one compact mutable `CONTROL.md` plus an append-only strategic decision log.** - -This is the smallest deep interface: one place answers “what now?”, while the log answers “why did strategy change?” ADRs continue answering “what architecture or durable operating rule was accepted?” - ---- - -## Quantitative inventory - -| Document | Lines | Words | Headings | Issue mentions / unique | -| --- | ---: | ---: | ---: | ---: | -| `STEERING.md` | 303 | 3,505 | 15 | 48 / 22 | -| `COORDINATION.md` | 147 | 1,114 | 5 | 97 / 32 | -| Combined current-strategy surface | **450** | **4,619** | **20** | **145 mentions** | -| `SPEC-LEDGER.md` | 126 | 2,498 | 10 | 83 / 25 | -| `agents/steering.md` | 103 | 627 | 7 | 0 | -| `agents/issue-tracker.md` | 135 | 1,192 | 9 | 9 | -| `agents/arc-close.md` | 92 | 662 | 9 | 0 | -| `agents/documentation.md` | 87 | 688 | 6 | 0 | -| `INDEX.md` | 122 | 2,225 | 10 | 86 / 36 | - -`STEERING` and `COORDINATION` share **20 issue IDs**—20 of STEERING’s 22 unique issues. The overlap includes the whole active September spine: FE-1476–1482, FE-1438/1439/1440/1441, FE-1480, FE-1387, and the FE-1402/1403/1406/1431 inputs. - -### Churn and cadence - -- `STEERING`: introduced and revised in **3 commits on 2026-08-24**, 305 changed lines. -- `COORDINATION`: **10 commits across Aug 20–24**, 343 changed lines; four revisions on Aug 21 and three on Aug 24. -- The STEERING-introduction commit added 296 lines while simultaneously rewriting COORDINATION by **61 additions/37 deletions**. -- `SPEC-LEDGER`: 3 commits, 154 changed lines, concentrated around implementation evidence. - -The immediate synchronized rewrite is evidence that STEERING did not merely add a distinct strategic layer: it forced the existing current-state layer to restate the new strategy. - ---- - -## Duplication and authority ambiguity - -### Sequencing is represented at least four times - -1. Four proof frontiers in `STEERING.md:167-227`. -2. Narrative “current choice” in `STEERING.md:292-303`. -3. Narrative sequencing recommendation in `COORDINATION.md:15-33`. -4. Issue-node/edge map in `COORDINATION.md:35-68`. - -Examples repeated across both files: - -- Parallel semantic and experience lanes joining at targeted correction: - - `STEERING.md:169-171,294-298` - - `COORDINATION.md:20-27,45-67` -- Generic Gherkin and contract freeze no longer gate September: - - `STEERING.md:233-241,265-275,297-298` - - `COORDINATION.md:29-33` -- YAML/Markdown before broad UI: - - `STEERING.md:236-237,260` - - `COORDINATION.md:31-33,52` -- FE-1480’s pure-projection authority conflict: - - `STEERING.md:114-119,175-187,259,286` - - `COORDINATION.md:21-22,46,62,99-102` -- Existing-target session identity: - - `STEERING.md:80,204-217,258` - - `COORDINATION.md:23-25,49,106-108` - -### Issue mapping crosses the declared seam - -`STEERING` says COORDINATION projects strategy onto work (`STEERING.md:8-13`), but STEERING itself contains an extensive **Issue projection** section (`STEERING.md:246-278`). COORDINATION then repeats those roles and dependencies in its recommendation and graph (`COORDINATION.md:15-68`). - -### COORDINATION mirrors tracker state despite forbidding it - -It says “Do not … mirror issue status” (`COORDINATION.md:10-13`) but records that FE-1437 is “Ready for review” and waits to reach `main` (`COORDINATION.md:17-20`), then repeats its executed/landing state in the graph (`COORDINATION.md:43,58-61`) and handoff history (`COORDINATION.md:73-95`). - -### Gates and seams are split by interpretation, not stable ownership - -Strategic gates live in STEERING (`STEERING.md:44-63`), while issue/state gates and unresolved seams live in COORDINATION (`COORDINATION.md:35-68,97-127`). The distinction fails for FE-1480, target identity, contract freeze, and Dora’s decision: each is simultaneously a strategic belief, a sequencing input, and a coordination seam. - -### Existing lifecycle rules already point toward one mutable surface - -- Controls should contain only current objective, topology, obligations, choices, gates, and stop conditions (`documentation.md:71-76`). -- Git carries mutable-control history (`STEERING.md:15-19`; `arc-close.md:12-13`). -- Unchanged conditional passes should leave controls untouched (`arc-close.md:62-73`). - -The current 450-line pair is too broad to satisfy “compact.” - ---- - -## Options - -### A. Keep both and tighten - -**Pros:** smallest path-level change; preserves the protocol’s declared split. - -**Required repair:** delete issue projection from STEERING; delete strategic explanation and open strategic seams from COORDINATION. - -**Problem:** the seam remains unstable. A new gate or authority conflict often changes objective, sequencing, and issue projection together. Arc close would still require deciding whether one or both documents change (`arc-close.md:53-73`). - -**Verdict:** viable but shallow—two interfaces expose one underlying decision. - -### B. Merge STEERING and COORDINATION - -**Pros:** removes ownership ambiguity and duplicate sequencing immediately. - -**Problem:** a straight merge produces a 450-line current-state document and still loses strategically meaningful rationale whenever current truth is overwritten. Git technically contains history, but discovering “why did generic-first become CPS-first?” requires commit archaeology. - -**Verdict:** better interface, insufficient lifecycle design. - -### C. Compact `CONTROL` plus strategic decision log - -**Pros:** one current-state interface; durable rationale without polluting current truth; strategic decisions no longer masquerade as ADRs. - -**Verdict:** recommended. - ---- - -## Recommended responsibilities - -### `docs/control/CONTROL.md` — mutable current truth - -It should answer only: - -1. **Objective and acceptance proof** -2. **Current choice** -3. **Frontier:** now / next / join -4. **Gates and stop/replan conditions** -5. **Cuts** -6. **Focused issue projection:** only IDs needed to act now -7. **Exceptional roots:** retained because the tracker protocol needs a repository-owned exception list - -Target: **60–100 lines**, one sequencing representation, ideally a compact pseudo-map. - -It must not contain: - -- system architecture explanations; -- historical handoff narratives; -- full issue-role tables; -- settled seam history; -- tracker state readily obtained from Linear; -- multiple equivalent descriptions of sequencing. - -### `docs/control/STRATEGIC-DECISIONS.md` — append-only rationale - -One entry only when a strategic choice changes: - -```md -## SD-0003 — Prefer the CPS vertical proof over generic plugin completion - -Date: 2026-08-24 -Status: current -Trigger: FE-1476 established the review-and-revise scenario. -Decision: Build one CPS semantic slice and one reviewer-session slice in parallel. -Because: The generic path did not cross the September proof spine. -Cuts: Gherkin completion, second-target freeze, broad requirements UI. -Revisit when: Dora changes the scenario, FE-1480 fails, or either first proof fails. -Supersedes: SD-0002 -Evidence: [links only] -``` - -Lifecycle: - -- Existing entries are immutable except typo/link repair. -- A changed choice appends a new entry and names what it supersedes. -- Do not append ticket movement, status summaries, proof runs, or no-op steering passes. -- `CONTROL` links the currently governing decision IDs. -- Git remains the history of the mutable control; the log preserves only strategically meaningful decisions. - -### Distinction from ADRs - -ADRs record accepted architecture and durable operating rules (`documentation.md:12-14`; `steering.md:83-85`). Examples are the three-register IR and application-placement rules. - -The strategic log records reversible choices under current time, evidence, and product pressure—the exact judgment STEERING claims at `STEERING.md:11-13`. A strategic entry may expose the need for an ADR amendment, but cannot amend one itself. - ---- - -## Section disposition - -### From `STEERING.md` - -- **Condense:** September outcome (`21-42`) into objective + six-beat acceptance proof. -- **Move to CONTROL:** external gates (`44-49`), proof spine (`51-63`), current choice (`292-303`), concise cuts (`229-244`). -- **Condense heavily:** current-system table (`65-87`) to only facts that alter the active frontier. -- **Move durable architecture elsewhere or delete duplication:** strategic bet invariants and elicitor architecture (`89-165`). Existing ADR/spec/plugin-contract material should remain authoritative; only unresolved choices stay in CONTROL. -- **Collapse:** four frontier essays (`167-227`) into now/next/join plus proof and stop condition. -- **Delete as a separate section:** issue projection (`246-278`); represent each issue once in the compact execution map. -- **Move to strategic log:** the generic-first → CPS-first choice and its rationale. -- **Condense:** beliefs table (`280-290`) to only low-confidence beliefs that can trigger replanning. - -### From `COORDINATION.md` - -- **Merge and deduplicate:** current recommendation and graph (`15-71`) into CONTROL’s single execution map. -- **Archive/delete from live control:** repository handoff threshold (`73-95`); it is completed historical migration context. -- **Classify open seams (`97-127`):** - - active decision/gate → CONTROL; - - accepted architectural rule → ADR/spec link; - - settled history → archive or delete from current control. -- **Retain:** exceptional roots (`129-147`), but only IDs, gist, intended parent/disposition—no duplicated strategic explanation. - ---- - -## SPEC-LEDGER - -**Keep it separate.** - -It has a different key, cadence, and terminal lifecycle: - -- Keyed by specification obligation rather than current objective or issue sequence. -- Code and tests are authoritative (`SPEC-LEDGER.md:3-6`). -- It tracks nuanced states not represented by tracker status: partial, contradicted, superseded, orphaned (`SPEC-LEDGER.md:8-14`). -- It becomes a settled terminal record at milestone-one closure (`SPEC-LEDGER.md:5-6`; `arc-close.md:46-51`). - -Its 126 lines are predominantly nine obligation tables, not narrative duplication. Folding it into CONTROL would make the current-state interface substantially shallower. CONTROL should link only the few ledger rows that gate the active frontier. - ---- - -## Compact document sketch - -```md -# Brunch control - -Governing strategic decision: SD-0003 -Tracker authority: Linear owns state, hierarchy, and hard blockers. - -## Objective -Prove one source-grounded CPS review-and-revise loop through the deployed -Petrinaut entrypoint. - -Acceptance: [six concise beats] - -## Current choice -Run the CPS semantic proof and existing-target reviewer proof in parallel. -Join at one production-swept correction; deployment follows that join. - -## Execution -now: - FE-1480 -> settle semantic authority - FE-1482 + FE-1478 -> fold/projection/provenance proof - FE-1438 + FE-1439 -> existing-target tool/session proof -join: - FE-1479 -> correction changes live net -then: - FE-1440 + FE-1423 -> FE-1441 - -## Gates -| Gate | Owner/source | Last checked | Effect | Replan trigger | - -## Cuts -- Generic Gherkin completion does not gate September. -- Contract freeze follows the CPS proof. -- Structured export precedes broad UI. -- No cold-start or comprehensive CPS ontology before the join. - -## Stop/replan -- Dora changes the scenario. -- FE-1480 cannot preserve the three-register rule. -- Either first production proof fails. -- Deployment is not viable after the join. - -## Exceptional roots -| Issue | Why root | Intended disposition | -``` - -This provides one low-cost interface for action while hiding issue-graph mechanics, strategic history, and specification accounting behind links to their proper authorities. -``` - -**Tool Result:** `TU-034CjRQFrREfGryK47EKAL` - -``` -## Audit verdict - -The report mixed a strong project orientation with substantial trigger-specific over-reading. Current guidance is explicitly route-by-trigger, but “orient” can still expand into roughly 500 lines of glossary and steering material. - -### 1. Accurate, expected synthesis - -| Report category | Source evidence | Assessment | -|---|---|---| -| Brunch is a stateful elicitation harness | `README.md:3-16` | Expected. | -| Package roles | `AGENTS.md:6-17`; `README.md:11-16` | Expected. The README additionally names `transport-aisdk`, which the summary should retain. | -| Glossary/domain model | `CONTEXT.md:7-27`, `29-48`, `50-121` | Expected for meaningful orientation. | -| Steering loop and evidence lifecycle | `docs/agents/steering.md:19-72` | Expected when starting/resuming without a proof target, per `AGENTS.md:30-33`. | -| One-truth-one-home table | `docs/agents/steering.md:74-94` | Accurate and useful. | -| September review-and-revise objective | `docs/control/STEERING.md:21-42` | Accurate current strategy, though conditional. | -| Missing implementation middle | `docs/control/STEERING.md:65-87`, especially `75-81` | Accurate. | -| Current strategic bet | `docs/control/STEERING.md:89-119` | Accurate. | -| Four frontiers, cuts, gates | `docs/control/STEERING.md:167-244`; gates at `44-50` | Accurate, expected only for current-work orientation. | -| Three-register IR | `CONTEXT.md:73-75`; authoritative detail in `docs/adr/0003-three-register-ir.md:24-63` | Accurate. The glossary-level account is enough for generic orientation. | -| Three-lane architecture | `docs/adr/0002-topology-and-placement-rules.md:10-43` | Accurate, but detailed reading is conditional on architecture/package placement. | - -### 2. Material inaccuracies or stale-risk claims - -#### Cold-start scope - -A generic description may say Brunch ultimately supports elicitation, but **a complete cold-start CPS interview is not the current September scope**: - -- Current demonstration is review-and-revise: `docs/control/STEERING.md:39-42`. -- Cold-start is explicitly cut: `docs/control/STEERING.md:229-244`. -- The provisional runbook starts from an existing target: `docs/control/STEERING.md:150-165`. -- Whether review-and-revise supersedes create-new-net remains an external gate: `docs/control/STEERING.md:44-49`. -- The belief that this can carry the demo is only medium-confidence and awaits Dora: `docs/control/STEERING.md:282-285`. -- There is an acknowledged conflict with ADR-0004 and cold-start integration stories: `docs/control/STEERING.md:263-278`. - -Therefore any unqualified claim that cold-start is either the current target or permanently out of scope is wrong. It is deferred by the present strategy pending an external decision. - -`CONTEXT.md:138-140` still describes the older “revision story” as a working hypothesis, not ratified. It must not be promoted over the newer steering document. - -#### Current Linear and assignment state - -Local controls correctly say Linear owns state, hierarchy, assignment, and hard blockers: - -- `docs/control/STEERING.md:8-13` -- `docs/agents/issue-tracker.md:73-90` - -A live read-only Linear query on 2026-08-24 found: - -- FE-1357 and FE-1383: **In progress**, assigned to Lu Nelson. -- FE-1477 and FE-1482: **Next up**, assigned to Lu Nelson. -- FE-1476–FE-1482 remain unparented. -- Nearly every open project issue is assigned to Lu; FE-1472 is the notable unassigned issue. - -Because a “claimable queue” requires open, unblocked, **unassigned** work (`docs/agents/issue-tracker.md:87-91`), “Next up” does not mean claimable. Any report describing FE-1477/FE-1482 as available for a fresh agent is materially wrong. - -The unparented statement remains current and agrees with `docs/control/COORDINATION.md:129-147` and `docs/control/STEERING.md:246-251`. - -### 3. Likely over-read content - -A generic orientation should not load every ceremony. `AGENTS.md:30-43` explicitly says “Route by trigger; load only the applicable compact protocol.” - -These are conditional: - -- Issue writing/tracker/triage: only issue operations (`AGENTS.md:34-35`) -- Documentation protocol: only document settlement/indexing (`AGENTS.md:36`) -- Domain protocol: only terminology or accepted-context changes (`AGENTS.md:37`) -- Flue routing: only Flue design choices (`AGENTS.md:38`) -- Legibility: significant artifacts/proofs (`AGENTS.md:39`) -- Posture: architecture-sensitive moves (`AGENTS.md:40`) -- Git workflow: branch/commit/PR work (`AGENTS.md:41`) -- Arc-close: closing a work arc (`AGENTS.md:42-43`) - -Thus detailed issue-writing, Git, wayfinding, arc-close, legibility, posture, triage, and all protocol ceremonies were over-read for a read-only generic orientation. - -Full ADR-0002 and ADR-0003 readings were also unnecessary unless the request asked about topology, IR design, or architecture. Their summaries in `docs/INDEX.md:101-106` plus the glossary are adequate initially. - -### 4. Important content missed or under-emphasized - -1. **Current application boundary** - The accepted topology is in-Petrinaut staging with a separate remote server, not a standalone demo shell: `docs/adr/0004-in-petrinaut-staging-and-the-monorepo-import.md:30-65`. - -2. **Transport and evaluation surfaces** - `README.md:11-16` names the AI SDK transport and remote application. `AGENTS.md:13` and `evaluations/AGENTS.md:1-8` identify evaluation cases, protocols, oracles, and the information wall. - -3. **Current implementation limitations** - Session identity, machine client-tool refusal, and the stock deployed website route are decisive present constraints: `docs/control/STEERING.md:79-81`. - -4. **Authority and status discipline** - `docs/INDEX.md:3-11` explains document statuses and authority. This is more useful in orientation than detailed ceremony mechanics. - -5. **Posture, only as a warning** - Current posture is prototype/high-stakes/current-milestone: `docs/agents/posture.md:1-4`. It matters before architecture-sensitive work, but need not be expanded during generic orientation. - -## Recommended read sets - -### Default generic orientation - -1. `README.md:1-19` — identity and concrete surfaces. -2. `AGENTS.md:1-43` — scope and trigger routing. -3. `CONTEXT.md` — glossary; initially headings plus relevant terms. -4. `docs/INDEX.md:1-12`, `73-82`, `95-116` — authority map and concise current-document summaries. -5. If orienting to current work: - `docs/agents/steering.md:1-42`, then `docs/control/STEERING.md:21-119`, `167-244`, `280-303`. -6. Query Linear live before asserting availability, assignment, hierarchy, or blockers. - -### Conditional pointers - -- Architecture/package placement → ADR-0002, topology reference. -- IR semantics → ADR-0003, IR/plugin specs. -- Petrinaut integration → ADR-0004, integration spec. -- Current sequencing → `COORDINATION.md` plus live Linear. -- Evaluation work → `evaluations/AGENTS.md`. -- Flue choice → `flue-routing.md`. -- Issue, documentation, Git, artifact, posture, or close protocols → only on their named triggers. - -## Overall assessment - -Current guidance **can produce a compact, useful orientation** because `AGENTS.md:30-43` explicitly enforces trigger routing. The fresh report’s ceremony coverage indicates the agent ignored that instruction. - -The main remaining weakness is that “start or resume without a proof target” routes a generic orientation into the large steering apparatus. A disciplined agent should read only the steering sections needed to establish current objective, uncertainty, and next proof—not every protocol and historical artifact. No files were modified. -``` - -**Tool Result:** `TU-034CjRQGXdoJ4wQ2O3j55L` - -``` -## Outcome - -Read-only audit completed. No repository or Linear changes were made; `git status --short` remained empty. - -## Linear evidence - -GraphQL query on 2026-08-24 found: - -- Viewer: **Lu Nelson** (`ln@hash.ai`) -- Project: `brunch-agent`, team `FE` -- Project lead/member: Lu Nelson -- 82 project issues: - - 81 assigned to Lu Nelson - - 0 assigned to another user - - **1 unassigned:** FE-1472, “Evaluate the cost of pinning bedrock-sdk's nested Anthropic SDK,” in Triage - -Thus the stated fact is almost, but not completely, true. FE-1472 is the exception; assigning it would require explicit approval. - -### Linear limitation - -Linear’s current API schema and CLI expose no project-level default assignee: - -- `Project`/`ProjectUpdateInput` has lead and members, but no default assignee. -- `linear project update` has no assignee option. -- FE has team-wide default issue-template facilities, but both member and non-member defaults are unset. -- A team template could carry an assignee, but would affect all FE issues, is not project-specific, and can be bypassed. -- `linear issue create` supports `--assignee self`. - -No available project setting therefore guarantees assignment specifically for `brunch-agent`. - -## Smallest durable assignment rule - -Update `docs/agents/issue-tracker.md` so every issue created for this project uses: - -```sh -linear issue create \ - --team FE \ - --project brunch-agent \ - --assignee self \ - --parent FE-XXXX -``` - -State explicitly: - -1. Assignment is part of the approved creation operation, not a later cleanup write. -2. The creator must verify the returned issue has project, parent, and current-user assignee before reporting success. -3. Creating or repairing assignment still requires explicit external-write approval. -4. Existing teammate-authored issues are not reassigned without separate approval. - -Also revise the wayfinding model: its “claimable queue” currently requires issues to be **unassigned**, and says unassigned project work is normal. That directly conflicts with the proposed invariant. Replace claimability with open + unblocked, with assignment indicating the current owner; remove “claim by assigning yourself” for project-created issues. - -## Control-lifecycle risks - -If the three controls become one mutable control plus an append-only strategic decision log: - -- **Trigger ownership:** Steering triggers are defined, but nobody is explicitly responsible for evaluating them. Name the arc driver as owner; arc-close is the backstop. -- **Append criteria:** Without a materiality threshold, routine ticket movement becomes chronology. Append only when objective, proof frontier, authority boundary, cut, gate consequence, or confidence materially changes. -- **Supersession:** Entries need stable IDs and explicit `supersedes` links. The mutable control must link the active decision rather than restating history. -- **Compaction:** Never rewrite append-only entries. Close a bounded volume, index/archive it unchanged, and start another volume. Define a size or settled-frontier threshold. -- **VCS conflicts:** A mutable monolith and a shared EOF are both conflict hotspots. Require one decision entry per commit, append only after rebasing, and resolve conflicts by retaining both entries and reconciling ordering/supersession—never by dropping one side. -- **Arc close:** Current checks separately name STEERING, COORDINATION, and SPEC-LEDGER. Consolidation would otherwise silently lose obligation and coordination checks. -- **Index/tests:** Existing `docs-index.test.ts` proves coverage, links, retired paths, and AGENTS reachability, but not control shape, decision identity, append discipline, or supersession integrity. -- **No-op prevention:** Existing guidance is strong but prose-only. A decision entry must cite the triggering changed fact and affected current-control section; otherwise no append is allowed. - -## Exact recommended guidance changes - -- **`docs/agents/documentation.md`** - - Define the single mutable control’s owned fields. - - Define the strategic log as immutable historical decisions, not evidence or routine updates. - - Specify entry IDs, trigger/evidence, decision, consequence, supersedes, and current-control link. - - Specify volume rollover instead of in-place compaction. - -- **`docs/agents/steering.md`** - - Assign trigger evaluation to the arc driver. - - Add exact append criteria and require supersession links. - - Require current control to point to active strategic decisions. - -- **`docs/agents/arc-close.md`** - - Replace the three conditional control passes with one reconciliation pass that still checks: - issue topology, soft edges, exceptional roots, specification obligations, strategy, gates, and proof frontier. - - Verify every new log entry has a material trigger and changed current truth. - - Verify no no-op entry was appended. - - Audit project issues for missing/current-user assignee, but repair only with approval. - -- **`docs/agents/issue-tracker.md`** - - Add mandatory `--assignee self`. - - Reconcile the claimable-queue semantics. - - Document the lack of a project-level Linear default. - -- **`docs/INDEX.md` / `AGENTS.md`** - - Replace control registrations and protocol wording atomically; retain one covering row per new log volume. - -## Recommended tests - -Extend `packages/core/test/architecture/docs-index.test.ts` or add a focused control-lifecycle architecture test to assert: - -- exactly one current mutable control; -- every decision-log entry has a unique ID, date, trigger/evidence, decision, and consequence; -- every `supersedes` target exists and no supersession cycle exists; -- the current control references only existing active decision IDs; -- closed log volumes remain indexed; -- legacy control files are absent after migration. - -Extend `linear-project-graph` and its test to include assignee identity and surface any project issue not assigned to the current user. Keep this as an audit gate rather than an auto-repair: Linear writes remain approval-controlled. -``` - -## Assistant - -Yes: the current setup is too wide. `STEERING` is 303 lines, `COORDINATION` 147, and they share 20 issue IDs. Their conceptual seam is not holding. - -I recommend: - -- Keep one compact mutable [`STEERING.md`](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/control/STEERING.md), targeting 60–100 lines. -- Delete `COORDINATION.md`; fold only active soft edges and exceptional roots into `STEERING`. -- Add append-only `STRATEGY-LOG.md` for material strategic choices—not ticket movement. -- Keep `SPEC-LEDGER.md` separate. - -`STEERING` answers **what now**: - -- objective and acceptance proof -- governing strategy-log entries -- now → join → next issue map -- cuts -- external gates and replan triggers -- exceptional roots - -`STRATEGY-LOG` answers **why this strategy**. Each immutable entry records: - -```text -ID · date · trigger/evidence · decision · consequences/cuts -revisit condition · supersedes · links -``` - -Append only when the objective, proof frontier, authority boundary, material cut, gate consequence, or confidence changes. No-op steering passes write nothing. ADRs remain architectural decisions; the strategy log records reversible choices under current pressure. - -Arc-close should enforce: - -1. exactly one mutable strategic control; -2. each new log entry has a material trigger; -3. `STEERING` references current decision IDs; -4. supersession links resolve; -5. Linear remains authoritative for live issue facts. - -The fresh agent’s orientation was strong but over-read almost every trigger-specific protocol. A generic orientation should normally read `README`, `AGENTS`, `CONTEXT`, `INDEX`, compact `STEERING`, then live Linear. Git, issue-writing, legibility, Flue, and arc-close protocols should load only on their triggers. - -One substantive correction: **cold-start does not gate the current demo, but it remains an important benchmark/contingency lane**. “No cold-start interview” is too categorical. - -Finally, live Linear shows 81/82 issues assigned to you; FE-1472 remains unassigned. New issue creation should always use `--assignee self`, and assignment should mean human ownership—not work claiming. May I assign FE-1472 and implement this control-surface consolidation? - -## User - -Yes, to both - -## Assistant - -I’ll keep the established name `STEERING.md` for the single mutable control, add an append-only `STRATEGY-LOG.md`, retire `COORDINATION.md`, and leave `SPEC-LEDGER.md` separate. The consolidation will include executable lifecycle checks and the new assignment invariant; issue assignment will become ownership, not a work-claim mechanism. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the explicitly approved Linear assignment repair. In the FE `brunch-agent` project, re-read FE-1472 and current viewer identity. Assign FE-1472 to the current user (Lu Nelson) and make no other change: no title/body/comment/state/parent/project/label changes. Treat connection failure as unknown and re-read before retry. Then query all 82 project issues and verify every issue is assigned to the current user, with zero unassigned or differently assigned. Do not modify repository files. Return before/after issue fields, operation used, and aggregate verification.", - "description": "Assign FE-1472" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "IMPLEMENT the approved Brunch control-surface consolidation in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Inspect clean status, read all affected files, and preserve unrelated work. Do not stage/commit or mutate Linear. Outcome: one compact mutable current control (`docs/control/STEERING.md`) plus one append-only material strategic decision record (`docs/control/STRATEGY-LOG.md`); remove `docs/control/COORDINATION.md`; keep `SPEC-LEDGER.md` separate.\n\nDESIGN\n1. Rewrite STEERING to approximately 60–100 lines (slightly over only if a checkable gate/table requires it). It owns current truth only:\n- objective and concise acceptance proof;\n- links to governing strategy decision IDs;\n- one compact now -> join -> next execution tree with only actionable issue IDs and gaps;\n- active gates, owner/source/watch trigger/last checked/consequence;\n- only decision-relevant beliefs/unknowns with confidence/evidence or cheapest probe;\n- cuts stated as sequencing, including the corrected rule: full cold-start does not gate the current review-and-revise proof, but cold-start quality remains an important benchmark/contingency lane and can activate if the use-case decision changes;\n- stop/replan conditions;\n- exceptional roots with intended disposition.\nDo not mirror Linear status/assignment/hard blockers, duplicate architecture/spec rationale, retain completed handoff history, or explain the generic steering procedure. Link authorities.\nUse current facts from existing STEERING/COORDINATION and this context: Dora’s use-case decision will remain outstanding for a while, so make it event-watched/non-blocking rather than a frequently polled gate; FE-1480 remains unresolved; truck-fleet dossier ref unavailable; review-and-revise is current proof but not permanent product scope.\n\n2. Add STRATEGY-LOG.md as an append-only material-decision log, explicitly distinct from ADRs and a diary. Seed only the minimum current strategic choices recovered from existing controls:\n- review-and-revise is the September proof while cold-start remains benchmark/contingency;\n- CPS establishes the minimum plugin contract before generic contract freeze;\n- semantic and reviewer lanes run in parallel and join at targeted correction before deployment.\nUse stable IDs S-001..S-003. Each entry: Date, Trigger/evidence, Decision, Consequences/cuts, Revisit when, Supersedes (`none` for initial seeded decisions), Evidence links. Keep entries compact. Existing entries immutable except typo/link repairs; changes append a superseding entry. STEERING links all governing IDs.\n\n3. Delete COORDINATION.md without archive/stub. Merge only its live unique responsibilities into STEERING: current soft execution edges and exceptional roots. Git carries handoff history. Repair all active repo links/path references. Historical plain-text evidence of old paths may remain only if intentionally evidentiary and not a live link.\n\nLIFECYCLE GUIDANCE\nUpdate minimally and without duplicated procedures:\n- docs/agents/steering.md: arc driver owns trigger evaluation; current control references governing log IDs; append only when objective, proof frontier, authority boundary, material cut, gate consequence, or confidence materially changes; require trigger/evidence and supersedes; no-op writes nothing.\n- docs/agents/documentation.md: one compact mutable STEERING authority; strategy log immutable append-only rationale; ADR distinction; Git is mutable history; no diary/status entries.\n- docs/agents/arc-close.md: one control reconciliation, still conditional; check issue soft edges/exceptional roots, strategy/gates/frontier; validate any new strategy entry is material and referenced; no-op persists nothing; SPEC-LEDGER remains separately conditional.\n- docs/agents/issue-tracker.md: all new brunch-agent project issues use `--assignee self` as part of the approved create operation and verify assignee/project/parent; assignment denotes accountable human ownership, not agent claim. Replace unassigned claimable-queue semantics with a mechanical frontier of open+unblocked work; before work, check state/active branch or PR to avoid duplication. Existing reassignments remain separately approval-gated.\n- AGENTS.md/README/docs/INDEX and other active guidance only where pointers/authority table need repair.\n\nEXECUTABLE GATES\n- Update/add architecture tests so exactly one current mutable strategic control exists, COORDINATION is absent, and STRATEGY-LOG entry structure is validated: unique IDs, required fields, valid supersedes targets/no cycles, STEERING governing IDs resolve. Keep tests simple and tied to the exact Markdown schema.\n- Inspect the current Linear graph generator/tests. Extend its read-only audit to include assignee identity and surface any brunch-agent project issue not assigned to the current authenticated user, if this fits the existing module without broad refactor. Do not auto-repair. Add/update tests first or alongside. If it does not fit cleanly, report rather than force it.\n- Repair INDEX/link tests and all consumers.\n\nDo not add log-volume rollover machinery now; milestone close can revisit if the file becomes large. Do not add a skill or new abstraction. Do not write Linear descriptions in this task.\n\nRun formatting, ESLint, typecheck, full package unit tests, build, focused docs/control and Linear graph tests, and git diff --check. Return exact files changed/deleted/added, final STEERING line/word count, test results, any live external pointers to retired COORDINATION requiring separately approved repair, and any unresolved concern.", - "description": "Consolidate Brunch controls" -} -``` - -## User - -**Tool Result:** `TU-034Cjetkn4lWzDnHjRAHCr` - -``` -Assignment repair completed. - -- **Viewer:** Lu Nelson (`ln@hash.ai`) -- **Operation:** `linear issue update FE-1472 --assignee self` -- **FE-1472 before:** assignee `null` -- **FE-1472 after:** assignee Lu Nelson -- **Unchanged:** title, body, state (`Triage`), parent, project (`brunch-agent`), labels -- **Project verification:** 82/82 assigned to Lu Nelson; 0 unassigned; 0 differently assigned; no additional pages -- **Repository files:** none modified -``` - -**Tool Result:** `TU-034CjetlHD9Wq00JGIE0a8` - -``` -Implemented the Brunch control-surface consolidation. - -**Changed** -- Added `docs/control/STRATEGY-LOG.md` -- Added `packages/core/test/architecture/control-surfaces.test.ts` -- Deleted `docs/control/COORDINATION.md` -- Reworked `docs/control/STEERING.md`: **93 lines, 915 words** -- Updated: - - `README.md` - - `docs/INDEX.md` - - `docs/agents/{arc-close,documentation,issue-tracker,steering}.md` - - `docs/control/SPEC-LEDGER.md` - - `docs/specs/plugin-contract.md` - - `scripts/linear-project-graph.ts` - - `packages/core/test/architecture/linear-project-graph.test.ts` - -Linear graph now audits assignees against the authenticated user without mutation. - -**Verification** -- ESLint: passed -- Formatting: passed -- Typecheck: passed -- Unit tests: **119 passed** -- Build: passed -- Focused control/index/Linear tests: **13 passed** -- Live Linear graph: passed; 35 open issues, 0 assignee mismatches, no cycles -- `git diff --check`: passed -- Arc-close reconciliation: completed -- No staging, commits, or Linear mutations performed - -No active repository references to `COORDINATION` remain. Historical archive references remain intentionally evidentiary. External Linear/Notion bodies were not exhaustively audited, so separately approved pointer repair may still be needed there. -``` - -## Assistant - -FE-1472 is now assigned; live verification shows 82/82 project issues assigned to you. - -The consolidation is implemented at 93 lines for `STEERING`, with three seeded strategy entries and executable lifecycle/assignment checks. I’m now cold-reviewing the content and the Linear audit independently, and checking whether retiring `COORDINATION` left any live external pointers. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY cold review of the current control-consolidation diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify/stage/commit. Review docs/control/STEERING.md, new STRATEGY-LOG.md, deleted COORDINATION.md, and all guidance/index/spec pointer edits. Intent: STEERING is compact current-only (target 60–100 lines, minimal prose), log is append-only material strategy distinct from ADR/diary, SPEC-LEDGER separate, cold-start corrected to benchmark/contingency not current gate, use-case decision event-watched/non-blocking, no tracker-state mirroring, unique live soft edges/exceptional roots retained, no strategy loss. Check whether 915 words is still unnecessarily prose-heavy; identify exact deletions/condensations. Validate seeded S-001..S-003 against prior controls/agreed strategy and check no architectural ADR decision was misfiled. Check lifecycle rules for drift/no-op/churn/conflicts. Return actionable findings with file/line and smallest fixes, pass/fail, and a judgment on whether the fresh agent would now get a compact accurate current orientation.", - "description": "Review consolidated controls" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY code/test review of current changes to scripts/linear-project-graph.ts, packages/core/test/architecture/linear-project-graph.test.ts, control-surfaces.test.ts, and issue-tracker guidance in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify. Verify: live graph remains read-only; authenticated viewer identity is authoritative; all project issues (not just open) are audited or any scope is explicitly correct; mismatch/unassigned failures are clear; pagination unaffected; tests cover unassigned, wrong assignee, all-good, absent viewer/assignee fields, and existing graph behavior; issue creation uses --assignee self and assignment no longer means agent claim; external-write approval remains. Check test parser for strategy log is robust enough without overengineering. Return actionable findings with lines, pass/fail, smallest fixes.", - "description": "Review assignment audit code" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY external/internal pointer audit after deleting docs/control/COORDINATION.md. Do not modify repository or external systems. Query all 82 live Linear brunch-agent issue descriptions and current repository Markdown/link paths for `COORDINATION.md`, the retired path, or text that treats COORDINATION as active authority. If accessible, inspect project documents/Notion references only when directly linked from current Brunch docs; do not broad-search unrelated systems. Return exact live pointers needing repair, issue IDs and context, whether a direct replacement is STEERING.md or STRATEGY-LOG.md, and count. Distinguish intentional historical archive mentions. Also verify no current active repo link target is broken.", - "description": "Audit retired coordination pointers" -} -``` - -## User - -**Tool Result:** `TU-034CjpNKnCAwZG7iMF7Qg6` - -``` -## Verdict: **FAIL — close, but not lossless** - -The consolidation is substantially better and gives a fresh agent a compact, mostly accurate current orientation. However, one live seam has a broken pointer, several unique coordination seams disappear without another authority, and lifecycle guidance can still cause tracker-driven churn. - -### Findings - -1. **Broken absence-locator authority** - - `docs/specs/plugin-contract.md:366` now points to `STEERING`, but `STEERING` contains no absence-locator seam. - - Smallest fix: add one compact soft-edge sentence under `STEERING.md:38`, or point the spec to the existing SPEC-LEDGER obligation if that is the intended owner. - -2. **Potential strategy loss from deleted COORDINATION** - - Deleted unique live seams have no clear replacement: - - structured-tap evidence (`FE-1395 × capture store`) - - telemetry vocabulary (`FE-1385 × FE-1404 × FE-1423`) - - living-prototype charter - - Guidance placement is adequately covered by ADR-0002; projection authority, controller, target identity, contract freeze, and absence locator are otherwise represented. - - Smallest fix: retain only genuinely live unique seams as terse bullets under soft edges, or link each to its actual spec/ledger authority. - -3. **Tracker changes can trigger unnecessary STEERING churn** - - `docs/agents/arc-close.md:55-60` requires reassessment for hard blockers, parentage, and project membership, although `docs/agents/steering.md:17` says ordinary ticket movement is not a trigger and Linear owns these facts. - - Smallest fix: remove hard blocker/parent/project changes as independent triggers; retain exceptional-root or sequencing-impact changes. - -4. **Strategy-log lifecycle underspecifies conflicts and drift** - - `docs/agents/steering.md:101-103` and `STRATEGY-LOG.md:3-6` cover append-only, supersession, and no-op behavior, but not: - - conflicting still-live entries; - - removing superseded IDs from STEERING; - - complementary decisions versus superseding decisions; - - ID sequencing. - - The test only checks resolvable backward references and cycles. - - Smallest fix: state that conflicting decisions must supersede prior entries and only unsuperseded governing IDs may remain in STEERING. Optionally enforce monotonically increasing IDs. - -### Seeded strategy review - -- **S-001:** Correctly strategic, not architectural. It narrows the current proof while explicitly preserving ADR-0004 reconciliation and cold-start as benchmark/contingency. -- **S-002:** Faithfully captures the prior CPS-before-generic-freeze strategy. -- **S-003:** Faithfully captures the parallel semantic/reviewer lanes and mandatory FE-1479 join. -- **No ADR decision appears misfiled.** - -### Prose/size judgment - -`STEERING.md` is 93 lines but still 915 words; compact by line count, moderately prose-heavy by scan cost. - -Exact reductions: - -- `3-5`: collapse to one ownership sentence. -- `38-42`: replace with 3 terse soft-edge bullets. -- `56-60`: shorten each belief row to evidence fragment + probe; much repeats gates and stop conditions. -- `64-67`: collapse into one “not gates” line referencing S-001/S-002. -- `73-81`: convert to five one-line triggers; remove explanatory tails already present in gates. -- Preserve objective, acceptance proof, execution tree, gates, roots, and production-wiring warning. - -This should reach roughly 650–750 words without losing current strategy. - -### Fresh-agent orientation - -**Mostly yes:** objective, proof, execution order, cold-start posture, use-case watch behavior, cuts, and exceptional roots are immediately legible. -**Not fully accurate yet:** the missing absence-locator destination and silently dropped live seams prevent a clean pass. - -No files modified; review was read-only. -``` - -**Tool Result:** `TU-034CjpNLNTQdjTVPmdXHcO` - -``` -## Review verdict: **FAIL** - -### Findings - -1. **High — audit contradicts “every project issue.”** - `docs/agents/issue-tracker.md:122` claims every project issue is audited, but the documented command omits `--all` (`:117`), while `fetchProjectGraph()` defaults to open issues only (`scripts/linear-project-graph.ts:441,459-460`). Closed mismatches are therefore excluded. - **Smallest fix:** document the audit command with `-- --all`, or explicitly limit the guidance to open issues. - -2. **High — required fetch behavior is effectively untested.** - `linear-project-graph.test.ts` only tests rendering. It does not exercise GraphQL parsing, viewer-ID authority, missing viewer/assignee fields, pagination, closed issue inclusion, or read-only CLI invocation. Consequently, requested unassigned/wrong-assignee/all-good cases are only partially represented; absent-field and pagination cases are absent. - **Smallest fix:** export/inject the query boundary or page parser and add focused fixtures for: - - viewer ID match despite names; - - unassigned and wrong assignee; - - zero mismatches; - - missing viewer fails clearly; - - absent/null assignee becomes unassigned; - - two-page accumulation and cursor failure; - - default-open versus `--all`. - -3. **Medium — missing viewer failure is misleading.** - At `scripts/linear-project-graph.ts:299-311`, an absent/malformed viewer produces “Expected exactly one Linear project,” even when the project is valid. - **Smallest fix:** validate viewer separately and report “Linear returned an unexpected/missing authenticated viewer.” - -4. **Medium — strategy-log parser can silently skip malformed entries.** - `control-surfaces.test.ts:26-40` only returns entries matching the exact shape. A malformed `### S-…` entry can disappear while tests still pass because only `entries.length > 0` is asserted. - **Smallest fix:** separately count strategy-ID headings and assert that count equals parsed entries. No full Markdown parser is needed. - -### Passed checks - -- Live graph remains read-only: only `linear api` is invoked. -- Viewer ID, not display name, determines assignment (`:435`). -- Mismatch/unassigned rendering is clear. -- Existing graph cycle behavior remains covered. -- Issue creation now uses `--assignee self`; assignment is explicitly human ownership, not agent claim. -- External writes remain approval-gated. -- Focused tests: **6 passed**. -- ESLint: **passed**. -- Type check: **passed**. -- `git diff --check`: **passed**. - -No files modified. -``` - -**Tool Result:** `TU-034CjpNM3jBKBe3MybSm3G` - -``` -## Audit result - -**1 live pointer needs repair.** - -- `docs/agents/legibility.md:70` — “durable artifacts (coordination, ledger, docs, issues)” - - Treats coordination as a current control. - - Replace with **`STEERING.md`** / “steering”. - - Not `STRATEGY-LOG.md`: this sentence concerns current reconciliation, not historical rationale. - -## Linear - -Queried all **82/82** `brunch-agent` project issue descriptions. - -- **No live issue pointer needs repair.** -- `FE-1464`, line 117, mentions `COORDINATION.md` only in a completed historical account of commit `0629903`. Intentional history; retain. -- Other lowercase “coordination” occurrences are ordinary prose/headings, not control-document authority. - -## Historical repository mentions - -`docs/archive/migrations/hash-monorepo-import-plan.md`: - -- Line 6: historical link to `./COORDINATION.md` -- Line 138: historical cutover-gate reference -- Line 177: historical statement that the file was live at import time - -These are intentionally historical. The line-6 link is now unresolved, but it is not an active authority pointer and has no honest direct `STEERING.md`/`STRATEGY-LOG.md` replacement. - -## Link integrity - -- Checked **69 current Brunch Markdown/MDX files**: **0 broken active local link targets**. -- Embedded old-worktree links in the preserved framework-comparison transcript are historical snapshot content, not current navigation. -- Linear project has **no attached documents**. -- Four directly linked Notion pages were identified; their contents were inaccessible due authentication/redirects, so they could not be audited. - -No repository or external-system changes were made. -``` - -## Assistant - -The cold review found useful tightening rather than a design reversal: - -- `STEERING` can lose another 150–250 words. -- Three genuinely live soft seams from `COORDINATION` need terse retention. -- Arc-close was still letting ordinary tracker movement provoke strategy churn. -- The strategy-log test could miss malformed entries. -- The assignment audit needs an explicit all-issues mode and tests at its query boundary. -- One live legibility reference still said “coordination.” - -I’m applying those corrections and then will re-review the whole slice. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT the current uncommitted control-consolidation slice in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Read current files and deleted COORDINATION from HEAD/diff before patching. Do not stage/commit or mutate Linear. Apply the review findings with the smallest coherent changes:\n\nCONTROL CONTENT\n1. Reduce docs/control/STEERING.md from 915 words toward 650–750 while preserving objective, acceptance proof, execution tree, gates, exceptional roots, and production-wiring rule. Specifically collapse ownership intro; use terse soft-edge bullets; shorten beliefs to evidence fragment + probe; collapse cuts/not-gates with strategy-log links; turn replan into one-line triggers without duplicated explanations.\n2. Retain exactly the genuinely live unique seams from deleted COORDINATION as terse active soft edges, using exact issue IDs/meaning from HEAD:\n- structured-tap evidence into capture-store truth;\n- telemetry vocabulary across its named issues/exposure gate;\n- living-prototype charter.\nAlso ensure the absence-locator seam has an honest current authority. Inspect SPEC-LEDGER: if it already owns the exact obligation, point docs/specs/plugin-contract.md there; otherwise add one terse STEERING soft edge and keep the spec pointer accurate. Do not resurrect handoff history or full issue graph.\n3. docs/agents/arc-close.md: ordinary hard-blocker/parent/project movement is not an independent steering trigger. Reconcile STEERING only when exceptional roots or sequencing/strategy materially change. Keep assignment audit as a separate read-only close check if present.\n4. Strategy lifecycle in docs/agents/steering.md and STRATEGY-LOG.md: conflicting decisions must append a superseding entry; complementary decisions need no supersedes; only unsuperseded governing IDs may remain in STEERING; IDs increase monotonically. Keep compact.\n5. docs/agents/legibility.md: replace current-authority 'coordination' reference with steering/STEERING. Preserve historical archive mentions.\n\nEXECUTABLE CHECKS\n6. control-surfaces.test.ts: separately count all strategy-ID headings and assert count equals successfully parsed entries, so malformed entries cannot disappear. Enforce monotonically increasing unique IDs. Assert STEERING governing IDs are unsuperseded, while allowing complementary initial entries. Keep simple regex; no parser abstraction.\n7. Assignment audit:\n- docs/agents/issue-tracker.md must use `turbo run linear:graph --filter '@hashintel/brunch-agent' -- --all` when claiming to audit every project issue. Preserve normal open-only graph use elsewhere.\n- scripts/linear-project-graph.ts: validate authenticated viewer separately and give a precise missing/malformed-viewer error.\n- Add focused tests at the query/page parsing boundary for viewer ID vs name, unassigned, wrong assignee, all-good, missing viewer, null/absent assignee, two-page accumulation/cursor behavior, and default-open vs --all. Export/inject only the smallest boundary needed; do not broad-refactor or add dependencies. Preserve read-only behavior and existing graph output.\n8. Repair any pointers affected by these changes.\n\nRun formatting, ESLint, typecheck, full unit suite, build, focused control/docs/Linear graph tests, live read-only linear:graph with --all, and git diff --check. Return exact edits, STEERING line/word count, test counts, live assignment audit result, and remaining concerns.", - "description": "Fix control consolidation review" -} -``` - -## User - -**Tool Result:** `TU-034CjtjYtb0f427e05OEHt` - -``` -Implemented the control-consolidation review changes without staging, committing, or mutating Linear. - -Key edits: -- Condensed `STEERING.md` to **93 lines / 726 words**. -- Preserved required proof, execution tree, gates, roots, and production-wiring rule. -- Restored only the three live COORDINATION seams plus the absence-locator soft edge. -- Clarified arc-close, strategy supersession, and current-authority rules. -- Added strategy-ID integrity checks. -- Added focused viewer, assignment, pagination, and `--all` tests. -- Improved malformed/missing authenticated-viewer errors. -- Updated affected documentation pointers. - -Verification: -- Formatting: passed -- ESLint: passed -- Typecheck: passed -- Full unit suite: **128/128 passed** -- Focused tests: **22/22 passed** -- Build: passed -- `git diff --check`: passed -- Live `linear:graph --all`: **82 project issues, 0 assignment mismatches, 1 external issue, no cycles** - -Remaining concern: Node emits the existing typeless-package ES-module performance warning when running the graph script. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final content/lifecycle review of current control-consolidation diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify. Verify prior findings resolved: absence locator, three live seams, no strategy loss, 650–750-word scan cost, cold-start nuance, event-watched Dora gate, arc-close no tracker churn, strategy conflict/complement/supersession/current-ID rules, live legibility pointer, no broken active links, no active COORDINATION authority. Assess whether the resulting STEERING + STRATEGY-LOG + SPEC-LEDGER split is disciplined and whether a fresh generic/current-work agent gets concise accurate orientation. Return pass/fail and only actionable findings.", - "description": "Recheck consolidated controls" -} -``` - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final code/test review of current control-consolidation diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify. Verify prior findings resolved: all-issues assignment command, viewer validation, query/page parser tests including pagination/default-open/--all/null/wrong/unassigned, read-only behavior, strategy heading count/parse equality, monotonic unique IDs, valid acyclic supersedes, STEERING only unsuperseded IDs. Inspect for overfitting or unnecessary abstractions, and confirm full test/build claims if possible from direct focused runs. Return pass/fail and only actionable findings.", - "description": "Recheck control and graph tests" -} -``` - -## User - -**Tool Result:** `TU-034CjyLK6Em9emHgFR0UuS` - -``` -**PASS — no actionable findings.** - -Verified all requested lifecycle/content concerns, including 652-word STEERING scan cost, active-link integrity, and removal of active COORDINATION authority. - -Focused verification: 22 tests passed across control surfaces, documentation links/indexing, and Linear graph behavior. -``` - -**Tool Result:** `TU-034CjyLKsYnRhyx9I1dE2J` - -``` -**FAIL — 2 actionable findings** - -1. `control-surfaces.test.ts:56` counts only `### S-*` headings. A malformed entry heading such as `### T-004` is ignored, so heading/parse equality still passes. Count all level-3 entry headings. -2. `control-surfaces.test.ts:84` checks only the “Governing strategic decisions” paragraph. Other strategy IDs in `STEERING.md` can remain superseded unnoticed. Validate every `S-\d{3}` reference in STEERING. - -All other requested findings appear resolved: assignment command, viewer/assignee validation, pagination/default-open/`--all`/null/wrong/unassigned cases, read-only querying, unique monotonic IDs, and backward acyclic supersedes. - -Verification passed: ESLint, formatting, typecheck, 128 unit tests, build, `git diff --check`, and live default/`--all` Linear graph runs. No unnecessary abstractions identified; no files modified. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT only packages/core/test/architecture/control-surfaces.test.ts in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent after reading it. Fix two review findings with minimal regex assertions:\n1. Count all level-3 headings in STRATEGY-LOG's entry area (not only valid S-* headings) and assert the count equals parsed valid entries, so malformed IDs/headings cannot disappear. Do not count the document title or non-entry explanatory prose.\n2. Collect every S-\\d{3} reference anywhere in STEERING.md, not only the governing paragraph, and assert each resolves to an existing unsuperseded strategy entry. Preserve the separate requirement that the governing list exists.\nDo not modify other files, stage, commit, or touch Linear. Run formatting, ESLint, typecheck, full unit tests, build, focused control tests, and git diff --check. Return exact assertions and results.", - "description": "Harden control lifecycle tests" -} -``` - -## User - -**Tool Result:** `TU-034Ck0mKHdCs2bbnpQLMMc` - -``` -Updated only `packages/core/test/architecture/control-surfaces.test.ts`. - -Exact new assertions: -- `expect(entryArea).not.toBeNull();` -- `expect(entries).toHaveLength(entryHeadingCount);` -- `expect(governingLine![1]).toMatch(/S-\d{3}/);` -- Every global `S-\d{3}` reference is asserted known and unsuperseded. - -Verification passed: -- Formatting -- ESLint -- Typecheck -- Unit tests: 128/128 -- Build -- Focused tests: 5/5 -- `git diff --check` - -No staging, commit, Linear activity, or other file edits. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final review of the entire current control-consolidation diff in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent after the test hardening. Do not modify. Confirm both latest test findings are resolved and no remaining blocker across content authority, lifecycle guidance, assignment invariant/audit, links, deleted COORDINATION, compact STEERING, strategy log, and tests. Return pass/fail, exact complete changed-path manifest suitable for literal staging, verification summary, and any external pointer repair still required.", - "description": "Final review control consolidation" -} -``` - -## User - -**Tool Result:** `TU-034Ck3S6mUHRivsxlJvZyw` - -``` -## Verdict: **FAIL** - -Latest test-hardening findings are resolved: - -- Assignee matching uses authenticated viewer **ID**, not display name. -- Pagination is tested, including cursor forwarding and `--all` closed-issue coverage. - -Remaining blockers: - -1. `docs/archive/migrations/hash-monorepo-import-plan.md:6` contains a broken `./COORDINATION.md` link, plus stale references at lines 138 and 177. -2. Linear issue **FE-1464** still references `COORDINATION.md`; external repair requires explicit approval. - -## Changed-path manifest - -```text -libs/@hashintel/brunch-agent/README.md -libs/@hashintel/brunch-agent/docs/INDEX.md -libs/@hashintel/brunch-agent/docs/agents/arc-close.md -libs/@hashintel/brunch-agent/docs/agents/documentation.md -libs/@hashintel/brunch-agent/docs/agents/issue-tracker.md -libs/@hashintel/brunch-agent/docs/agents/legibility.md -libs/@hashintel/brunch-agent/docs/agents/steering.md -libs/@hashintel/brunch-agent/docs/control/COORDINATION.md -libs/@hashintel/brunch-agent/docs/control/SPEC-LEDGER.md -libs/@hashintel/brunch-agent/docs/control/STEERING.md -libs/@hashintel/brunch-agent/docs/control/STRATEGY-LOG.md -libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md -libs/@hashintel/brunch-agent/packages/core/test/architecture/control-surfaces.test.ts -libs/@hashintel/brunch-agent/packages/core/test/architecture/linear-project-graph.test.ts -libs/@hashintel/brunch-agent/scripts/linear-project-graph.ts -``` - -## Verification - -- ESLint: pass -- Formatting: pass -- TypeScript: pass -- Unit tests: **128/128 pass** -- Build: pass -- `git diff --check`: pass -- Live Linear graph: pass; **0 assignee mismatches**, no cycles -- Repository search: no active `COORDINATION` references outside the archived plan -- Read-only review; no files modified or committed. -``` - -## Assistant - -The final reviewer re-raised two historical references that an earlier pointer audit had deliberately classified as evidence, not live authority. I’m adjudicating that distinction rather than rewriting history mechanically. The likely repair is only to de-link the archived broken path while retaining its historical text; FE-1464’s completed-account mention should remain untouched. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY adjudication in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent plus live Linear. Do not modify. Claims: (A) docs/archive/migrations/hash-monorepo-import-plan.md:6 is an unacceptable broken active link after COORDINATION deletion; competing explanation: it is historical evidence whose text should remain but need not be clickable. Inspect lines 1-15, 130-145, 170-182 and archive/documentation/link-test policy. Decide whether to de-link, replace, or retain. (B) FE-1464's COORDINATION.md mention needs external pointer repair; competing explanation: it is a completed historical account of commit 0629903 and changing it would falsify history. Inspect exact issue context/state and distinguish operational link from historical mention. Return claim records/evidence and verdicts/recommended smallest action. Do not infer from substring alone.", - "description": "Adjudicate historical coordination references" -} -``` - -## User - -**Tool Result:** `TU-034Ck6AR07BlPQA3fxhAfY` - -``` -## Claim A - -**Claim:** Archived import plan contains an unacceptable active broken link at line 6. -**Reliance:** Whether to de-link, replace, or retain it. -**Competing explanation:** It is preserved historical evidence, intentionally exempt from live-link validation. -**Evidence:** - -- `hash-monorepo-import-plan.md:3–6` declares the plan settles as a record. -- Lines 138 and 177–178 describe `COORDINATION.md` as it existed during cutover. -- `docs/agents/documentation.md` defines `docs/archive/` as settled material without current authority. -- `docs-index.test.ts:44–48` explicitly exempts this exact file as an immutable migration snapshot whose old paths are evidence. -- The target is currently absent, but the exemption is deliberate rather than accidental. - -**Verdict:** **WONT-FIX — retain unchanged.** The link is historical evidence, not active navigation. De-linking or redirecting it would alter the archived record. - -## Claim B - -**Claim:** FE-1464’s `COORDINATION.md` mention requires pointer repair after deletion. -**Reliance:** Whether to edit the completed Linear issue. -**Competing explanation:** It records historical chronology around commit `0629903`, rather than providing an operational pointer. -**Evidence:** - -- Live FE-1464 is **Done**, with completion evidence and 172 passing tests. -- Its exact passage says commit `0629903` removed `CONVERGENCE.md` and split responsibilities between bounded `COORDINATION.md` and the specification ledger. -- `COORDINATION.md` appears only as inline code—there is no clickable repository link. -- The surrounding section is “Already resolved threads,” documenting why a historical review finding was stale. -- Current documentation policy requires repairing external **pointers** when paths move; this is a historical mention, not a pointer or instruction to consult the file. - -**Verdict:** **WONT-FIX — retain FE-1464 unchanged.** Updating it to reflect the later deletion would falsify the issue’s completed historical account. - -**Smallest action:** None. No files or Linear records were modified. -``` - -## Assistant - -The historical-reference findings are adjudicated as intentional evidence, not live pointers: the archived migration snapshot and FE-1464 remain unchanged. The implementation now has no unresolved review findings, so I’m committing the consolidation as one unit. - -**Tool Use:** `Task` - -```json -{ - "prompt": "COMMIT the verified control-consolidation unit in /Users/lunelson/Code/hashintel/hash. Inspect status first; abort on any unexpected/foreign change. Stage exactly these literal current diff paths and no others:\n- libs/@hashintel/brunch-agent/README.md\n- libs/@hashintel/brunch-agent/docs/INDEX.md\n- libs/@hashintel/brunch-agent/docs/agents/arc-close.md\n- libs/@hashintel/brunch-agent/docs/agents/documentation.md\n- libs/@hashintel/brunch-agent/docs/agents/issue-tracker.md\n- libs/@hashintel/brunch-agent/docs/agents/legibility.md\n- libs/@hashintel/brunch-agent/docs/agents/steering.md\n- libs/@hashintel/brunch-agent/docs/control/COORDINATION.md (deletion)\n- libs/@hashintel/brunch-agent/docs/control/SPEC-LEDGER.md\n- libs/@hashintel/brunch-agent/docs/control/STEERING.md\n- libs/@hashintel/brunch-agent/docs/control/STRATEGY-LOG.md (addition)\n- libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md\n- libs/@hashintel/brunch-agent/packages/core/test/architecture/control-surfaces.test.ts\n- libs/@hashintel/brunch-agent/packages/core/test/architecture/linear-project-graph.test.ts\n- libs/@hashintel/brunch-agent/scripts/linear-project-graph.ts\nDo not use git add ./-A/wildcards. Verify staged set exactly matches and no unstaged slice changes remain. Verify COORDINATION absent, STEERING/STRATEGY log lifecycle tests passed, historical archived references are unchanged, and no unrelated staged files. Commit message: `Consolidate Brunch strategic controls`. Do not push or mutate Linear. Return SHA, stats, post-commit status.", - "description": "Commit control consolidation" -} -``` - -## User - -**Tool Result:** `TU-034Ck853UC3IPUL1nl6zB5` - -``` -Committed `921b05858b90ac4b8bd24871dd318341d7556fd9`. - -- Stats: 15 files, +554/−495 -- Tests: 15 passed -- COORDINATION deleted; STEERING/STRATEGY lifecycle verified -- Archived references unchanged -- Post-commit worktree clean -- No push or Linear mutation performed -``` - -## Assistant - -The consolidation is committed. I’m running the revised arc-close once against its own new lifecycle rules; this should either be a true no-op or expose exactly where the new discipline still fails. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the canonical arc-close procedure from /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/agents/arc-close.md after commit 921b05858b90ac4b8bd24871dd318341d7556fd9. Read-only external operations only; no further Linear mutation is approved. Scope: control consolidation, strategy log seed, assignment invariant/audit, FE-1472 assignment already completed. Verify inbox/index, all-issue assignment graph (`--all`), spec-ledger conditional, single STEERING control, strategy-log entry materiality/current IDs/supersession, exceptional roots/soft edges, stale tense/retired COORDINATION active pointers, and no-op behavior. If a durable current truth genuinely requires a repo edit, make the smallest edit, do not stage/commit, and report it; otherwise persist nothing. Run applicable focused tests/status. Return each step and final pass/conditional status.", - "description": "Validate new control lifecycle" -} -``` - -## User - -**Tool Result:** `TU-034CkADBoGZZkMM79d09g3` - -``` -Canonical arc-close completed after `921b05858b`. - -1. **Inbox/index — PASS** - - 99 documents covered; all index links resolve. - - Inbox assets remain valid transient intake. - - Focused docs-index tests: **7/7 passed**. - -2. **Linear registry — PASS** - - Ran required `linear:graph --all`: 82 open, 0 assignee mismatches, no cycles. - - All open roots are recognized maps or listed exceptional roots. - - FE-1472 is assigned to Lu (`a:self`). - - No Linear writes performed. - -3. **Spec ledger — NOT APPLICABLE** - - No milestone-one obligation or evidence changed. - -4. **Current control — PASS / NO-OP** - - Exactly one `STEERING.md`. - - Soft edges, gates, proof frontier, and exceptional roots remain current. - - S-001–S-003 are material, current, monotonically identified, and require no supersession update. - -5. **Steering pass — NOT TRIGGERED** - - No new strategic fact or proof result; no no-op control update persisted. - -6. **Tense/pointers — REPAIR REQUIRED AND MADE** - - Updated `docs/archive/migrations/hash-monorepo-import-plan.md` to settled tense and replaced retired `COORDINATION` active pointers with current `STEERING`. - - Left **unstaged and uncommitted**, as requested. - - A second scan found no remaining retired `COORDINATION` pointers outside agent protocol history. - -**Remaining external discrepancy:** FE-1476 says the demo scenario is “agreed” while also saying use-case confirmation remains pending. `STEERING` correctly treats confirmation as an active gate. Repair would require an unapproved Linear mutation, so none was made. - -**Verification:** graph audit passed; docs-index tests passed; `git diff --check` passed. Formatter excludes this Markdown path. Working tree contains only the one unstaged documentation edit above. -``` - -## Assistant - -The lifecycle check itself passed, but its close agent made one incorrect edit: it rewrote the immutable import snapshot despite our explicit historical-evidence disposition. I’m removing that edit rather than committing it. - -It also found a real external inconsistency: FE-1476 calls the scenario “agreed” while the same issue says confirmation is pending. I’ll leave Linear unchanged until you approve that separate body repair. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EDIT only docs/archive/migrations/hash-monorepo-import-plan.md in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. The immediately preceding arc-close agent incorrectly changed this immutable historical snapshot after a WONT-FIX adjudication. Compare the current unstaged diff against HEAD commit 921b05858b90ac4b8bd24871dd318341d7556fd9 and reverse every unstaged change in this file exactly, restoring the committed bytes. Do not use broad reset/checkout; apply a precise patch after reading the diff. Do not touch any other file, stage, commit, or Linear. Verify git diff for the file is empty, whole worktree is clean, docs-index tests pass, and git diff --check passes.", - "description": "Restore immutable archive snapshot" -} -``` - -## User - -**Tool Result:** `TU-034CkDsjiTBE20OYy9D4Av` - -``` -Restored the file exactly to HEAD bytes. - -Verified: -- File diff empty -- Worktree clean -- Docs-index tests: 7 passed -- `git diff --check`: passed - -No files staged or committed. -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY Linear inspection. Do not modify repo or Linear. Fetch FE-1476 exact title/state/body around every statement that says the demo scenario is agreed and every statement that says use-case confirmation is pending. Given the user's authoritative current fact that the use-case decision will remain outstanding for a while and STEERING treats it as an event-watched non-blocking gate, propose the smallest description-only textual repair that removes contradiction while preserving the issue contract and agent notes. Return exact before/after snippets and whether any other field needs change (expected no).", - "description": "Prepare FE-1476 repair" -} -``` - -## User - -**Tool Result:** `TU-034CkFUqH9UcNZxIbaNnYu` - -``` -**FE-1476** -- Title: `Prepare September demo` -- State: `Todo` - -**Current body** -```md -The agreed demo scenario is: - -1. Present a completed requirements graph (pre-built from a prior elicitation) -2. A reviewer (not the original domain expert) examines the SDCPN built from that graph -3. The reviewer explores provenance ("why was X modelled like Y?") -4. The reviewer updates a section through 3-5 turns of targeted elicitation -5. The net changes accordingly -6. Handoff to optimisation experiments (Chris/Yannis) - -Use case to be confirmed by @dm -``` - -**Smallest description-only repair** - -Before: -```md -The agreed demo scenario is: -``` - -After: -```md -The current demo scenario is: -``` - -Before: -```md -Use case to be confirmed by @dm -``` - -After: -```md -Use-case confirmation by @dm remains outstanding; per STEERING, this is an event-watched, non-blocking gate. -``` - -This preserves the scenario, issue contract, and any agent notes while removing the claim that the scenario is agreed. No other field needs changing. No repository or Linear data was modified. -``` - -## Assistant - -Implemented and committed as `921b05858b`. - -- `STEERING.md`: compact current state, 93 lines. -- Added append-only `STRATEGY-LOG.md` with S-001–S-003. -- Removed `COORDINATION.md`. -- Kept `SPEC-LEDGER.md` separate. -- Added lifecycle and assignment audit tests. -- FE-1472 assigned; 82/82 project issues now assigned to you. -- 128 tests, lint, typecheck, and build pass. -- Worktree clean; not pushed. - -One Linear repair needs separate approval: change FE-1476 from “The agreed demo scenario” to “The current demo scenario,” clarifying that use-case confirmation remains outstanding. May I apply that description-only edit? - -## User - -yes - -## Assistant - -I’ll make only that two-line description repair, preserving the rest of FE-1476 and all other fields. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the explicitly approved description-only repair on Linear issue FE-1476. Re-read the live issue first. Change exactly:\n- `The agreed demo scenario is:` -> `The current demo scenario is:`\n- `Use case to be confirmed by @dm` -> `Use-case confirmation by @dm remains outstanding; per STEERING, this is an event-watched, non-blocking gate.`\nPreserve every other description byte/Markdown section and do not change title, state, assignee, parent, project, labels, comments, or any other issue. Use a file-based description update. Treat connection failure as unknown; re-read before retry. Afterward re-read and compare before/after, verifying only those two substitutions and unchanged fields. Do not modify repository files. Return concise verification.", - "description": "Repair FE-1476 scenario wording" -} -``` - -## User - -**Tool Result:** `TU-034CkIXdafvgFj2bSSJV5y` - -``` -Updated FE-1476 via description file. - -Verified: -- Both requested substitutions are live. -- Title, state, assignee, parent, project, and labels unchanged. -- No repository files modified. - -Caveat: Linear automatically trimmed the pre-existing terminal space after `@dm`; all other content is unchanged. -``` - -## Assistant - -FE-1476 updated and verified. Only the approved wording changed; Linear also trimmed one trailing space. - -## User - -we also need to create a linear issue for all the housekeeping we've done in this branch, and mark it as in progress. Then you can use `gt rename <branch-name>` to make this branch match that issue's id and a short slug - -## Assistant - -I’ll create one outcome-oriented housekeeping issue covering the completed control/documentation migration, assign it to you, mark it In Progress, place it under the appropriate recognized root, then rename the current Graphite branch to `ln/fe-xxxx-<short-slug>`. - -**Tool Use:** `skill` - -```json -{ - "name": "cli-graphite", - "arguments": "Rename the current Brunch branch after creating its Linear issue, using `gt rename <branch-name>` and preserving the existing stack/commits." -} -``` - -## User - -**Tool Result:** `TU-034CkL0XWwy2ETsvODIyKv` - -``` -{ - "content": [ - { - "text": "<loaded_skill name=\"cli-graphite\">\n# cli-graphite Skill\n\n\n# Graphite Skill\n\n## Prerequisites\n\nBefore first use, verify the CLI is available. If the check fails, tell the\nuser what to install and **stop**.\n\n```bash\ncommand -v gt >/dev/null && echo \"ok: gt\" \\\n || echo \"MISSING: gt — install with: brew install withgraphite/tap/graphite\"\n```\n\nWork with Graphite (`gt`) for creating, navigating, and managing stacked pull requests.\n\n## Quick Reference\n\n| I want to... | Command |\n|--------------|---------|\n| Create a new branch/PR | `gt create branch-name -m \"message\"` |\n| Add a new commit to current branch | `gt commit create -m \"message\"` (or `gt cc`) |\n| Amend the last commit on current branch | `gt commit amend -m \"message\"` (or `gt ca`) |\n| Navigate up the stack | `gt up` |\n| Navigate down the stack | `gt down` |\n| Jump to top of stack | `gt top` |\n| Jump to bottom of stack | `gt bottom` |\n| View stack structure | `gt ls` |\n| Submit stack for review | `gt submit --no-interactive` |\n| Rebase stack on trunk | `gt restack` |\n| Change branch parent | `gt track --parent <branch>` |\n| Rename current branch | `gt rename <new-name>` |\n| Move branch in stack | `gt move` |\n\n---\n\n## What Makes a Good PR?\n\nIn roughly descending order of importance:\n\n- **Atomic/hermetic** - independent of other changes; will pass CI and be safe to deploy on its own\n- **Narrow semantic scope** - changes only to module X, or the same change across modules X, Y, Z\n- **Small diff** - (heuristic) small total diff line count\n\n**Do NOT worry about creating TOO MANY pull requests.** It is **always** preferable to create more pull requests than fewer.\n\n**NO CHANGE IS TOO SMALL:** tiny PRs allow for the medium/larger-sized PRs to have more clarity.\n\nAlways argue in favor of creating more PRs, as long as they independently pass build.\n\n---\n\n## Branch Naming Conventions\n\nWhen naming PRs in a stack, follow this syntax:\n\n`terse-stack-feature-name/terse-description-of-change`\n\nFor example, a 4 PR stack:\n\n```\nauth-bugfix/reorder-args\nauth-bugfix/improve-logging\nauth-bugfix/improve-documentation\nauth-bugfix/handle-401-status-codes\n```\n\n---\n\n## Creating a Stack\n\n### Basic Workflow\n\n1. Make changes to files\n2. Stage changes: `git add <files>`\n3. Create branch: `gt create branch-name -m \"commit message\"`\n4. Repeat for each PR in the stack\n5. Submit: `gt submit --no-interactive`\n\n### Handle Untracked Branches (common with worktrees)\n\nBefore creating branches, check if the current branch is tracked:\n\n```bash\ngt branch info\n```\n\nIf you see \"ERROR: Cannot perform this operation on untracked branch\":\n\n**Option A (Recommended): Track temporarily, then re-parent**\n1. Track current branch: `gt track -p main`\n2. Create your stack normally with `gt create`\n3. After creating ALL branches, re-parent your first new branch onto main:\n ```bash\n gt checkout <first-branch-of-your-stack>\n gt track -p main\n gt restack\n ```\n\n**Option B: Stash changes and start from main**\n1. `git stash`\n2. `git checkout main && git pull`\n3. Create new branch and unstash: `git checkout -b temp-working && git stash pop`\n4. Proceed with `gt track -p main` and `gt create`\n\n---\n\n## Navigating a Stack\n\n```bash\n# Move up one branch (toward top of stack)\ngt up\n\n# Move down one branch (toward trunk)\ngt down\n\n# Jump to top of stack\ngt top\n\n# Jump to bottom of stack (first branch above trunk)\ngt bottom\n\n# View the full stack structure\ngt ls\n```\n\n---\n\n## Modifying a Stack\n\n### Commit vs Amend\n\nGraphite has explicit commit commands that also auto-restack descendants:\n\n```bash\ngit add <files>\ngt commit create -m \"add validation for email field\" # New commit (preferred)\ngt commit amend -m \"updated commit message\" # Amend last commit\n# Shorthands: gt cc -m \"...\", gt ca -m \"...\"\n```\n\n**Use `gt commit create` (`gt cc`) by default** unless the user explicitly\nasks to amend or squash. `gt modify` also works (amends by default, `-c` for\nnew commit) but the `gt commit` subcommands are clearer.\n\n### Reorder Branches\n\nUse `gt move` to reorder branches in the stack. This is simpler than trying to use `gt create --insert`.\n\n### Re-parent a Stack\n\nIf you created a stack on top of a feature branch but want it based on main:\n\n```bash\n# Go to first branch of your stack\ngt checkout <first-branch>\n\n# Change its parent to main\ngt track --parent main\n\n# Rebase the entire stack\ngt restack\n```\n\n### Rename a Branch\n\n```bash\ngt rename new-branch-name\n```\n\n---\n\n## Resetting Commits to Unstaged Changes\n\nIf changes are already committed but you want to re-stack them differently:\n\n```bash\n# Reset the last commit, keeping changes unstaged\ngit reset HEAD^\n\n# Reset multiple commits (e.g., last 2 commits)\ngit reset HEAD~2\n\n# View the diff to understand what you're working with\ngit diff HEAD\n```\n\n---\n\n## Before Submitting\n\n### Verify Stack is Rooted on Main\n\nBefore running `gt submit`, verify the first PR is parented on `main`:\n\n```bash\ngt ls\n```\n\nIf the first branch has a parent other than `main`:\n```bash\ngt checkout <first-branch>\ngt track -p main\ngt restack\n```\n\n### Run Validation\n\nAfter creating each PR, run appropriate linting, building, and testing:\n\n1. Refer to the project's CLAUDE.md for specific commands\n2. If validation fails, fix the issue, stage changes, and use `gt cc -m \"fix: ...\"` (or `gt ca` to amend)\n\n---\n\n## Submitting and Updating PRs\n\n### Submit the Stack\n\n```bash\ngt submit --no-interactive\n```\n\n### Update PR Descriptions\n\nAfter submitting, use `gh pr edit` to set proper titles and descriptions.\n\n**IMPORTANT:** Never use Bash heredocs for PR descriptions - shell escaping breaks markdown tables, code blocks, etc. Instead:\n\n1. Use the `Write` tool to create `/tmp/pr-body.md` with the full markdown content\n2. Use `gh pr edit` with `--body-file`:\n\n```bash\ngh pr edit <PR_NUMBER> --title \"stack-name: description\" --body-file /tmp/pr-body.md\n```\n\nPR descriptions must include:\n- **Stack Context**: What is the bigger goal of this stack?\n- **What?** (optional for small changes): Super terse, focus on what not why\n- **Why?**: What prompted the change? Why this solution? How does it fit into the stack?\n\n**Example** (for a PR in a 3-PR stack adding a warning feature):\n\n```markdown\n## Stack Context\n\nThis stack adds a warning on the merge button when users are bypassing GitHub rulesets.\n\n## Why?\n\nUsers who can bypass rulesets (via org admin or team membership) currently see no indication\nthey're circumventing branch protection. This PR threads the bypass data from the server to\nenable the frontend warning (PR 2) to display it.\n```\n\n---\n\n## Troubleshooting\n\n| Problem | Solution |\n|---------|----------|\n| \"Cannot perform this operation on untracked branch\" | Run `gt track -p main` first |\n| Stack parented on wrong branch | Use `gt track -p main` then `gt restack` |\n| Need to reorder PRs | Use `gt move` |\n| Conflicts during restack | Resolve conflicts, then `git rebase --continue` |\n| Want to split a PR | Reset commits (`git reset HEAD^`), re-stage selectively, create new branches |\n| Need to delete a branch (non-interactive) | `gt delete <branch> -f -q` |\n| `gt restack` hitting unrelated conflicts | Use targeted `git rebase <target>` instead (see below) |\n| Rebase interrupted mid-conflict | Check if files are resolved but unstaged, then `git add` + `git rebase --continue` |\n\n---\n\n## Advanced: Surgical Rebasing in Complex Stacks\n\nIn deeply nested stacks with many sibling branches, `gt restack` can be problematic:\n- It restacks ALL branches that need it, not just your stack\n- Can hit conflicts in completely unrelated branches\n- Is all-or-nothing - hard to be surgical\n\n### When to Use `git rebase` Instead of `gt restack`\n\nUse direct `git rebase` when:\n- You only want to update specific branches in your stack\n- `gt restack` is hitting conflicts in unrelated branches\n- You need to skip obsolete commits during the rebase\n\n### Targeted Rebase Workflow\n\n```bash\n# 1. Checkout the branch you want to rebase\ngit checkout my-feature-branch\n\n# 2. Rebase onto the target (e.g., updated parent branch)\ngit rebase target-branch\n\n# 3. If you hit conflicts:\n# - Resolve the conflict in the file\n# - Stage it: git add <file>\n# - Continue: git rebase --continue\n\n# 4. If a commit is obsolete and should be skipped:\ngit rebase --skip\n\n# 5. After rebase, sync graphite's tracking (amend is correct here)\ngt modify --no-edit\n```\n\n### Recovering from Interrupted Rebase (Context Reset)\n\nIf a rebase was interrupted (e.g., Claude session ran out of context):\n\n1. **Check status:**\n ```bash\n git status\n # Look for \"interactive rebase in progress\" and \"Unmerged paths\"\n ```\n\n2. **Read the \"unmerged\" files** - they may already be resolved (no conflict markers)\n\n3. **If already resolved, just stage and continue:**\n ```bash\n git add <resolved-files>\n git rebase --continue\n ```\n\n4. **If still has conflict markers**, resolve them first, then stage and continue\n\n### Deleting Branches from a Stack\n\n```bash\n# Delete a branch (non-interactive, even if not merged)\ngt delete branch-to-delete -f -q\n\n# Also delete all children (upstack)\ngt delete branch-to-delete -f -q --upstack\n\n# Also delete all ancestors (downstack)\ngt delete branch-to-delete -f -q --downstack\n```\n\n**Flags:**\n- `-f` / `--force`: Delete even if not merged or closed\n- `-q` / `--quiet`: Implies `--no-interactive`, minimizes output\n\n**After deleting intermediate branches**, children are automatically restacked onto the parent. If you need to manually update tracking:\n```bash\ngt checkout child-branch\ngt track --parent new-parent-branch\n```\n\n\nARGUMENTS: Rename the current Brunch branch after creating its Linear issue, using `gt rename <branch-name>` and preserving the existing stack/commits.\n\nBase directory for this skill: file:///Users/lunelson/.agents/skills/cli-graphite\nRelative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.\n</loaded_skill>", - "type": "text" - } - ] -} -``` - -## Assistant - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY preparation in /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent. Do not modify repo, Linear, or branch. Read docs/agents/issue-writing.md, issue-tracker.md, triage-labels.md, git-workflow.md, current STEERING exceptional roots, and inspect current branch/status/Graphite stack plus commits since its parent/merge-base. Determine the correct recognized parent/root for one retroactive housekeeping issue covering the work actually done: strategic steering protocol; role-based docs/evaluation topology; Linear path repairs; compact STEERING + append-only strategy log; control/assignment lifecycle tests; assignment invariant. Draft an outcome-oriented Linear issue title and compact root/child-appropriate body following local contract + agent-notes patterns. The issue must be assigned to current user, project brunch-agent, and marked In Progress. Recommend exact short branch slug `ln/fe-xxxx-...` once ID exists. Flag if parent choice is genuinely ambiguous; otherwise justify one. Return current branch, stack relationship, commit list/scope, proposed parent, title, body, labels if required, and exact creation/update/rename sequence.", - "description": "Draft housekeeping issue" -} -``` - -## User - -**Tool Result:** `TU-034CkL9dS9Jb5R5PKA1paF` - -``` -## Current state - -- **Branch:** `ln/fe-xxxx-whats-next` -- **Worktree:** clean -- **Graphite parent:** `ln/fe-1437-hash-monorepo-import` -- **Merge-base with parent:** parent tip `2bbb3601d5` -- **Relationship:** one child branch, nine commits ahead of FE-1437. - -## Commit scope - -1. `d4e61bb23b` Add September steering model -2. `c3d29164ab` Register incoming SDCPN nets -3. `d38c17120e` Correct SDCPN corpus inventory -4. `397405a276` Add strategic steering protocol -5. `806714c002` Refine issue, PR, and commit-record precedent -6. `3b4a73f4a7` Organize documentation by role -7. `daf7778bdc` Separate evaluation assets from evidence -8. `28f3e3bfa8` Reconcile control surfaces -9. `921b05858b` Consolidate strategic controls - -Collectively these implement the strategic steering protocol, role-based documentation/evaluation topology, repaired Linear/document paths, compact `STEERING.md` plus append-only `STRATEGY-LOG.md`, control and assignment lifecycle tests, and the project assignment invariant. - -## Proposed parent - -**FE-1401 — Resolve the stack legibility follow-ups** - -This is not genuinely ambiguous. FE-1401 is the recognized housekeeping/remediation sweep and already owns documentation and record-quality children such as FE-1424 and FE-1451. The work informs September strategy, but it primarily repairs repository control, documentation, tracker, and verification infrastructure rather than delivering an FE-1357 demo outcome. A closed parent remains valid for this retroactive child and avoids adding another exceptional root. - -The Git branch remains stacked on FE-1437 for code-history reasons; tracker hierarchy need not mirror stack ancestry. - -## Proposed issue - -**Title** - -> Keep Brunch strategy and control records coherent - -**Body** - -```markdown -Brunch’s strategic direction, documentation roles, and Linear ownership rules were spread across overlapping records, which made current priorities and issue reachability difficult to verify. This work establishes one compact strategic control, preserves material decisions in an append-only log, and gives documentation, evaluation assets, and tracker records explicit homes. - -The implementation is already present on the current branch. The remaining work is to attach the branch to this tracker record and complete review. - -+++ 🏗️ Agent notes - -The branch: - -- adds the strategic steering protocol; -- organizes documentation by role and separates evaluation inputs from proof evidence; -- repairs moved repository paths referenced by Linear and local control records; -- reduces `docs/control/STEERING.md` to current strategy and moves durable decisions into `docs/control/STRATEGY-LOG.md`; -- adds control-surface and Linear project-graph tests for assignment and lifecycle behavior; and -- enforces that every project issue has the expected accountable human assignment while preserving explicit review for mismatches. - -It is stacked on `ln/fe-1437-hash-monorepo-import` and contains commits `d4e61bb23b` through `921b05858b`. - -+++ -``` - -## Tracker settings - -- **Team:** FE -- **Project:** `brunch-agent` -- **Parent:** FE-1401 -- **Assignee:** current user (`Lu Nelson`) -- **State:** In Progress -- **Labels:** none required. This is neither a wayfinder child nor a triage-state issue. - -## Exact creation and rename sequence - -1. Save the body above outside the repository, for example `/tmp/brunch-strategic-controls.md`. -2. Create the issue atomically: - -```sh -linear issue create \ - --team FE \ - --project brunch-agent \ - --parent FE-1401 \ - --assignee self \ - --state "In Progress" \ - --title "Keep Brunch strategy and control records coherent" \ - --description-file /tmp/brunch-strategic-controls.md \ - --no-interactive -``` - -3. Verify the returned `FE-XXXX` has parent FE-1401, project `brunch-agent`, assignee Lu Nelson, and state In Progress: - -```sh -linear issue view FE-XXXX -``` - -4. If any field was normalized incorrectly, repair only that field with `linear issue update FE-XXXX ...`, then verify again. -5. Rename the Graphite branch: - -```sh -gt branch rename ln/fe-XXXX-strategic-controls -``` - -6. Confirm parentage and cleanliness: - -```sh -gt branch info -git status --short --branch -``` - -No repository, Linear, or branch state was modified. -``` - -## Assistant - -The branch is clean, nine commits above FE-1437. The best semantic parent appears to be FE-1401, the existing housekeeping/legibility sweep, though it is already closed; I’m checking the local child-label rule and whether an open child under that closed map is an accepted precedent before creating anything. - -**Tool Use:** `Task` - -```json -{ - "prompt": "READ-ONLY final tracker-shape check. Do not modify Linear or repo. Inspect local docs/agents/issue-writing.md and issue-tracker.md plus live FE-1401, FE-1383, FE-1357 (states, parent/root role, children, issue contract), and similar recent retroactive housekeeping issues. Answer: (1) whether a new In Progress child under Done parent FE-1401 is valid local precedent or creates graph/status incoherence; (2) whether FE-1383 or FE-1357 is a better active recognized root for this control/documentation housekeeping branch; (3) whether `dogsled:unframed` or another label is mandatory for any child issue under current local guidance; (4) exact creation settings. Prefer the narrowest semantically correct parent without creating a new exceptional root.", - "description": "Validate housekeeping issue parent" -} -``` - -## User - -**Tool Result:** `TU-034CkNG46pXnmcqfsCDWFX` - -``` -## Verdict - -1. **Do not add an In Progress child under Done FE-1401.** - FE-1401 was a bounded legibility follow-up sweep and is complete; all existing children are Done. Reopening its graph with active work would create status/semantic incoherence, not useful precedent. - -2. **Use FE-1383 as parent.** - This branch consolidates repository controls, documentation topology, verification, and tracker graph enforcement—the implementation/build substrate governed by FE-1383. FE-1357 is narrower: September demo and plugin-specification work. FE-1383 is the narrowest active recognized root. - -3. **No label is mandatory.** - `dogsled:unframed` is not required or mentioned by local guidance. Labels are role-specific: - - `ready-for-agent` only when fully specified for autonomous pickup. - - Wayfinder labels only for wayfinder children. - - Other triage roles follow `triage-labels.md`. - - For this retroactive already-implemented branch, use **no label**. - -4. **Exact creation settings** - - Team: `FE` - - Project: `brunch-agent` - - Parent: `FE-1383` - - Assignee: `self` - - State: `In progress` - - Cycle: current (`Cycle 105`) - - Priority: none - - Labels: none - - Title: active verb describing the concrete consolidation - - Description: mandatory plain-prose contract, followed by optional collapsed `🏗️ Agent notes` execution record - - Create parent atomically with the issue, then verify project, parent, and assignee. - -```sh -linear issue create \ - --team FE \ - --project brunch-agent \ - --parent FE-1383 \ - --assignee self \ - --state "In progress" \ - --cycle active \ - --title "<active-verb title>" \ - --description-file <file> -``` - -No Linear or repository state was modified. -``` - -## Assistant - -FE-1401 is already a completed bounded sweep, so adding active work beneath it would reopen a closed graph. I’ll instead parent this under active build root FE-1383, with no special label, assign it to you, place it in the active cycle, and then rename the current branch with the returned issue ID. - -**Tool Use:** `Task` - -```json -{ - "prompt": "EXECUTE the explicitly approved Linear issue creation and Graphite branch rename from /Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent.\n\nPreflight:\n- Verify worktree clean, current branch is `ln/fe-xxxx-whats-next`, Graphite parent is `ln/fe-1437-hash-monorepo-import`, and `gt` exists. Stop if any differs materially.\n- Read current issue fields/cycle names as needed; do not alter existing issues.\n\nCreate one Linear issue using a description file outside the repo with these exact settings:\n- Team: FE\n- Project: brunch-agent\n- Parent: FE-1383\n- Assignee: self (Lu Nelson)\n- State: In Progress\n- Cycle: active/current cycle\n- Priority: none/default\n- Labels: none\n- Title: `Keep Brunch strategy and control records coherent`\n\nBody:\n```markdown\nBrunch’s strategic direction, documentation roles, and Linear ownership rules were spread across overlapping records, which made current priorities and issue reachability difficult to verify. This work establishes one compact strategic control, preserves material decisions in an append-only log, and gives documentation, evaluation assets, and tracker records explicit homes.\n\nThe implementation is already present on the current branch. The remaining work is to attach the branch to this tracker record and complete review.\n\n+++ 🏗️ Agent notes\n\nThe branch:\n\n- adds the strategic steering protocol;\n- organizes documentation by role and separates evaluation inputs from proof evidence;\n- repairs moved repository paths referenced by Linear and local control records;\n- reduces `docs/control/STEERING.md` to current strategy and moves durable decisions into `docs/control/STRATEGY-LOG.md`;\n- adds control-surface and Linear project-graph tests for assignment and lifecycle behavior; and\n- enforces that every project issue has the expected accountable human assignment while preserving explicit review for mismatches.\n\nIt is stacked on `ln/fe-1437-hash-monorepo-import` and contains commits `d4e61bb23b` through `921b05858b`.\n\n+++\n```\n\nUse file-based creation. On connection failure, treat outcome as unknown and query before retry. Verify the resulting issue title/body, project, parent, assignee, state, cycle, priority, and labels. Do not add a comment or alter other issues.\n\nThen derive the branch name `ln/<lowercase-issue-id>-strategic-controls` (example `ln/fe-1490-strategic-controls`) and run exactly `gt rename <new-branch-name>` from the current branch. Do not use plain git branch rename. Verify current branch name, Graphite parent/stack relationship, commits unchanged, and worktree clean. Do not submit/push or create/edit a PR. Remove the temporary description file. Return issue ID/URL/settings, old->new branch, Graphite parent, and verification.", - "description": "Create issue and rename branch" -} -``` - -## User - -**Tool Result:** `TU-034CkPZxCL4KjzaHsrBpRj` - -``` -Created Linear issue **FE-1494**: -https://linear.app/hash/issue/FE-1494/keep-brunch-strategy-and-control-records-coherent - -Verified: -- Team FE; project `brunch-agent`; parent FE-1383 -- Assignee Lu Nelson; state In Progress -- Active Cycle 105; default priority; no labels -- Exact title/body preserved (Linear normalized bullets) - -Renamed via Graphite: -- `ln/fe-xxxx-whats-next` → `ln/fe-1494-strategic-controls` -- Parent remains `ln/fe-1437-hash-monorepo-import` -- Commits and HEAD unchanged -- Worktree clean; temporary file removed -- No push, submission, PR changes, comments, or other issue edits. -``` - -## Assistant - -Created [FE-1494](https://linear.app/hash/issue/FE-1494/keep-brunch-strategy-and-control-records-coherent), assigned to you and In Progress. - -Branch renamed to `ln/fe-1494-strategic-controls`; stack parent and commits are unchanged. \ No newline at end of file diff --git a/libs/@hashintel/brunch-agent/docs/inbox/salvage/amp-session-reviewing-architecture.md b/libs/@hashintel/brunch-agent/docs/inbox/salvage/amp-session-reviewing-architecture.md new file mode 100644 index 00000000000..fea2ed31871 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/inbox/salvage/amp-session-reviewing-architecture.md @@ -0,0 +1,610 @@ +--- +title: Package architecture review +author: Lu Nelson +threadId: T-01a032f5-37ff-7328-a0d5-f8ee48678dd2 +created: 2026-08-24T08:48:46.591Z +agentMode: high +--- + +# Package architecture review + +## User + +please get oriented in this package, and then I'd like to conduct a high-level design- and planning-review session + +## Assistant + +I’ll first map the package’s declared architecture, posture, active planning artifacts, and recent work. Then I’ll frame the review around the decisions that are actually open rather than giving a generic codebase tour. + +## User + +it's actually multiple packages; I should have referred to it as a "context root" + +## Assistant + +Right — I’ll treat `libs/@hashintel/brunch-agent` as the **Brunch context root**: one authority surface for the glossary, ADRs, protocols, and planning records; four child package workspaces; plus the separately located `apps/brunch-agent` application. That distinction is architecturally important here. + +The initial read suggests the package boundaries are comparatively settled; the more consequential review target is likely the transition from the completed import/integration spine to the still-provisional elicitation semantics and milestone plan. I’m narrowing that now against the coordination ledger and current Linear state. + +## Assistant + +The coordination document’s literal `yarn turbo …` form is stale for this Yarn setup; it fails before invoking Turbo. I’ll use the repository’s installed Turbo binary directly, preserving the same read-only graph task. + +## Assistant + +The live DAG materially diverges from the 21 August coordination snapshot: seven new root-level issues, FE-1476–FE-1482, now describe the September/demo, routing, provenance, re-elicitation, requirements-graph projection, and CPS-plugin direction. Before proposing a review frame, I’m reading those issue contracts and the three established root maps to determine whether this is intentional reframing or planning drift. + +## Assistant + +I’m oriented to the **Brunch context root**: + +```diagram +┌──────────────────────────────────────────────┐ +│ Brunch context root │ +│ glossary · ADRs · protocols · planning │ +└───────────────┬──────────────────────────────┘ + │ + ┌────────────┼─────────────┬──────────────┐ + ▼ ▼ ▼ ▼ + core binding-flue transport-aisdk plugin-gherkin + harness substrate UI wire target policy + └────────────┴─────────────┴──────────────┘ + │ + ▼ + apps/brunch-agent + remote server + │ + ▼ + apps/petrinaut-website + user-facing composition +``` + +### Assessment + +- Package boundaries are coherent and mechanically guarded by the [ratified topology](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/topology.md#L10-L75). +- The durable mechanics exist: ask/reply, settlement and sweep, provenance anchoring, capture storage, Flue binding, and AI SDK transport. +- The product-defining path remains mostly design: the plugin API is intentionally skeletal, and Gherkin is only a one-proposal tracer ([plugin API](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts#L5-L14), [Gherkin tracer](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts#L43-L54)). +- The live Linear plan has drifted beyond [COORDINATION.md](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/planning/_shared/COORDINATION.md#L14-L65): FE-1476–FE-1482 are unregistered roots, FE-1477 substantially duplicates FE-1440, and several new tickets overlap earlier plugin/demo work. +- Most importantly, FE-1480 assumes an inferential requirements-graph → SDCPN projection. That conflicts with ADR-0003’s rule that projections are deterministic over register 2 and semantic inference occurs only through contestable captures ([decision](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/adr/0003-three-register-ir.md#L24-L49)). + +### Recommendation + +Next: an `arc-map` review of the September milestone—but first settle that projection boundary. Afterwards we can reconcile the demo story, dependencies, and duplicate tickets. + +Opening question: + +**Where should the non-deterministic requirements → SDCPN judgment live?** + +1. **Preserve ADR-0003:** the LLM emits explicit, provenance-bearing formalization captures; folding and SDCPN projection remain pure. +2. **Amend ADR-0003:** projection itself may infer, requiring an auditable and reproducible projection-run model. + +My initial recommendation is **1**. Is that still your intended architecture? + +## User + +To your points: + +- yes, there are new tickets, drafted by my PM Dora, which we must adopt and fold-in to our graph +- package boundaries are in place yes; but implementation boundaries and paths are only traced so far, and this needs serious attention +- and yes FE-1480 will require discussion (as will other issues) + +Before we address those things, let me give you the high-level concerns that are on my mind, as other entrypoints: + +- the integration with the petrinaut demo website is only the thinnest of tracer bullets so far; completing the full contract layers here will bloom in to much larger scopes +- we haven't even begun `plugin-cps`, which is the actual thing we need to build a critical demo around, very soon (2 weeks in human time) +- the current design for the elicitor architecture seems sound; but its rigor may be at cross-purposes with establishing an e2e working model as fast as we can +- IMO the current issue topology/graph likely does not inherently express the goals we need to be driving at + +## Assistant + +Those concerns point to one underlying problem: the current plan is organized around **contract completion**, while the urgent objective is a **demo-capable vertical system**. That makes locally sensible work compete with global time-to-proof. + +I think the review should separate what must stay rigorous from what may remain deliberately narrow for two weeks, then rebuild the dependency graph around the demonstrated outcome. + +## Assistant + +Your diagnosis matches mine. The tension is less **rigor versus speed** than **generality versus speed**. + +We should retain rigor at irreversible boundaries—provenance, capture correction, real application wiring, valid SDCPN output—and defer broad genericity: complete plugin SDK, full CPS ontology, every affordance, multi-target validation, and contract freeze. + +I’d reshape the plan around vertical proofs: + +```diagram +Fixture captures ─▶ CPS model ─▶ valid SDCPN + │ +Live correction ─▶ supersession ─▶ changed SDCPN + │ +Net element ─▶ model slot ─▶ capture ─▶ user evidence + │ + real Petrinaut demo website +``` + +Existing capability tickets become constraints and inputs, not the primary execution sequence. FE-1476 should likely own the demo outcome, with FE-1477–FE-1482 folded beneath it and reconciled against FE-1393/1402/1403/1438/1440/1442. + +The first decisive question: + +**Is FE-1476 now the authoritative demo contract, replacing cold-start comprehensive elicitation with the prebuilt-model → provenance → targeted revision story?** + +My recommendation: **yes**. Otherwise we are planning two demos in two weeks. This would remove full cold-start elicitation and comprehensive completion accounting from the critical path unless they are still explicitly required. + +## User + +This essential line of reasoning is correct; but may still be working on too-thin signals. The core concern that exhibits the tension between the priorities of correct/complete vs "works effective e2e" is the actual elicitor architecture, and I think we should work through a fuller evaluation of this, to understand what's really on the critical path and what's not + +## Assistant + +Agreed. We should not infer the critical path from ticket titles or the demo script yet. We need to evaluate the elicitor as a system: trace each runtime responsibility from user turn to durable assertion, model, projection, and UI effect; then distinguish **missing load-bearing behavior** from merely incomplete contract breadth. + +I’ll assess the traced layers for role completeness, contract coverage, and fidelity to the proposed architecture before we resume prioritization. + +## Assistant + +One concrete issue is already emerging: the implemented loop can interview and append provenance-safe captures, but it cannot yet *reason over the target-document it has built*. The binding gives the model generic ask/sweep instructions; the private sweep sees conversation text and a proposal schema, but there is no register-2 model, demand state, projection, or plugin guidance feeding the next turn. + +I’m checking the correction and projection seams now, because the new demo requires the loop to revise existing knowledge—not merely capture new statements. + +### Readiness Map: Elicitor architecture + +**Verdict:** the substrate and capture foundation is strong, but the system is only tracer-complete. The implemented path currently terminates at captures: + +```diagram +Implemented +─────────── +User ─▶ agent ─▶ ask/reply ─▶ settlement ─▶ sweep ─▶ durable captures + │ + └── dead end + +Designed but absent +─────────────────── +captures ─▶ fold ─▶ elicited model ─┬─▶ completion/gaps ─▶ next-question policy + ├─▶ SDCPN projection ─▶ Petrinaut + └─▶ provenance map ─▶ targeted revision +``` + +| Layer | Readiness | Critical-path judgment | +| ------------------------------------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------- | +| Ask/suspend/transport | Mechanically substantial | Foundation ready; broader affordances are deferrable | +| Sweep/capture/provenance | Mechanically substantial | Foundation ready, but not yet usable for revision | +| Model-aware elicitation loop | Absent | **Critical** | +| Register-2 fold/model | Design only | **Critical**, in a deliberately narrow CPS form | +| CPS plugin | Absent | **Critical** | +| SDCPN projection | Design only and contested | **Critical design decision** | +| Petrinaut document application | Ask-only local tracer | **Critical**, but its correct mechanism is unresolved | +| Existing-document/reviewer session | Domain primitives exist; host collapses document to conversation | **Critical** for FE-1476 | +| Artifact provenance and scoped re-elicitation | Store primitives only | **Critical** for FE-1478/1479 | +| Generic plugin authoring, completion, loss reporting, full catalog | Design only | Mostly deferrable unless selected by the demo story | +| Public durability/security/deployment | Partial | Critical only after the functional loop exists | + +#### Important architectural findings + +1. **The elicitor does not yet consult what it has elicited.** + Sweep extraction receives conversation text and proposal names, not active captures, issues, or a derived model ([binding](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts#L138-L227)). Consequently, no model state can steer the next question. + +2. **Targeted correction is not reachable.** + The store rigorously supports `supersedes`, but extraction cannot see the active capture IDs it must reference. The current Gherkin schema cannot emit `supersedes` either. + +3. **The planned projection and client-tool paths potentially duplicate semantic authority.** + The plugin design says register 2 projects to a canonical SDCPN. FE-1438 instead has the agent manipulate Petrinaut through browser tools. If both perform modelling judgments, provenance and reproducibility break. We must separate: + - semantic projection: model → SDCPN; + - application: SDCPN/diff → live document; + - optional repair: diagnostics → revised projection. + +4. **The reviewer workflow needs a real target-document boundary.** + The domain model supports multiple sessions against one document, but the current host maps `targetDocumentId` directly from `conversationId` ([current mapping](file:///Users/lunelson/Code/hashintel/hash/apps/brunch-agent/src/petrinaut-chat.ts#L31-L45)). FE-1476 requires a reviewer session reopening an existing elicitation. + +5. **The generic plugin architecture may be premature, but its fundamental registers are not.** + We probably need captures → model → projection for provenance and revision. We probably do **not** need the complete declarative meta-schema, generic fold algebra, Gherkin-first SDK, all CPS proposal kinds, and contract freeze before proving one CPS path. + +My resulting hypothesis is: + +> Preserve the three-register architecture, but let the first CPS implementation discover the executable plugin boundary instead of completing the generic plugin framework first. + +The next unresolved design area is therefore the **semantic materialization boundary**: concrete CPS fold, projection ownership, provenance mapping, and how the resulting artifact enters Petrinaut. + +**Routing**: where should we go next? + +1. **Explore module shape (Recommended)** — design two competing executable shapes for that semantic boundary. +2. **Design verification** — first define the minimum end-to-end proof and its oracles. +3. **Back to triage** — continue auditing other architectural layers before choosing a boundary. + +Reply **1–3**, or redirect the review. + +## User + +Those routing options are good; but let's keep discussing for now. + +FWIW, your observations match my intuition and my suspicions. + +I must say, that tracer discpline has been a real double-edged sword: it's been a very common for the agent to take the tracer as the "definition of done" and for the actual implementation to be essentially left not only incomplete but barely started. + +I think your essential prioritization and plan-shape is correct; but we're going to need a new protocol for representing and driving that kind of plan or it's going to evaporate after this session: the `COORDINATION.md` doc is nice, but right now only tracks the issue-graph topology with a bias toward _what is mechanically next_ i.e. unblocked, while not having any way to track an actual strategic execution plan, which should rather be objective-driven, epistemically-tracked and with the issue-graph as a sequencing and trade-off surface + +I want to offer the mental model I have of the elicitor architecture so far, which I had skethed out before starting this thread, and which you can also therefore analyze as both a proposal and and an inventory, and help me determine where I have gaps or weak layers. So far, I think of it in roughly four parts, which belong to two essential layers (there are more layers than these two but these are the two with real architectural design pressure and which are currently hollow) + +1. __harness layer: elicitation agentics__. the prompting and skilling parts, general to all elicitations: + - orientations, motivations, licenses + - interviewing/question-formulation guidance +2. __harness layer: capture mechanics and agentics__. the capture mechanics so far; but lacking further build-out, as you've observed + - evidence sweeping and logging mechanism; with annotations re: supersession, conflicts etc. + - next-question ledger and loop? + - mapping plugin-defined types from utterances: entities (nodes), relations (edges), quantities, conditionalities +3. __plugin layer: typologies, critical points and motifs, ...?__. the things for the capture parts to be aware of + - what entities are we after + - what shapes and relations do they have +4. __plugin: runbook (objectives, trajectories, boundaries)__. this is the least sketched part so far IMO, and so "runbook" is a proposal at this point, about how the rest of the plugin manifest could actually be shaped + - the job + - what 'done' looks like + - how to check + - ... (what else?) + +## Assistant + +Your model is sound. I would make two refinements: + +1. Separate the **elicitation controller** from capture mechanics. +2. Separate the plugin’s static **runbook** from the dynamic **engagement brief** it produces for one target-document. + +```diagram + HARNESS +┌─────────────────────────────────────────────┐ +│ Strategy repertoire │ +│ orientations · techniques · licenses │ +└───────────────────┬─────────────────────────┘ + ▼ +┌─────────────────────────────────────────────┐ +│ Elicitation controller │ +│ assess situation · choose move · stop/replan│ +└───────▲───────────────────────────┬─────────┘ + │ ▼ +┌───────┴───────────────┐ ask · propose · validate +│ Evidence engine │ +│ sweep · capture · │ +│ supersede · resolve │ +└──────────┬────────────┘ + │ assertions + ▼ + PLUGIN +┌─────────────────────────────────────────────┐ +│ Domain contract │ +│ proposals · model · fold · diagnostics · │ +│ projection · provenance │ +└──────────▲──────────────────────────────────┘ + │ model state and gaps +┌──────────┴──────────────────────────────────┐ +│ Job runbook │ +│ objectives · trajectory · checks · stopping │ +└─────────────────────────────────────────────┘ +``` + +### 1. Harness: strategy repertoire + +Your contents fit, with one qualification: + +- **Orientations**: generic role and epistemic posture. +- **Licenses**: re-ask, challenge, propose for correction, expose assumptions. +- **Techniques**: contrastive questions, incident reconstruction, quantile elicitation. +- **Question formulation guidance**: generic forms only. + +The harness should define these capabilities, but not decide when domain-specific questions matter. Prompting and Flue skills are their delivery mechanism—not the architectural concepts themselves. + +**Current weakness:** the generic quiver is named but not designed. More importantly, there is no module composing its strategies into a coherent engagement. + +### 2. Harness: evidence engine + +This should own: + +- conversation archive and evidence classification; +- settlement and sweep execution; +- capture envelope and provenance; +- atomic application; +- issues, conflicts, supersession and retraction; +- invocation of plugin-defined proposal extraction. + +But two items in your list sit elsewhere: + +- **“Next-question ledger and loop” belongs to the controller.** +- **Entities, relations and conditionalities belong to plugin vocabulary.** The harness executes schema-constrained extraction; the plugin defines what can be extracted. Quantities may come from a shared stated-form library, but should not become universal harness ontology. + +A useful decomposition is: + +```diagram +Model demand ─▶ knowledge gap ─▶ candidate move ─▶ chosen move ─▶ concrete ask + derived derived derived session state transcript +``` + +The “ledger” should mostly be derived, not persisted. Persist the selected trajectory or active commitment only when continuity requires it; otherwise stale agendas will compete with the current model. + +**Current weakness:** the evidence engine writes captures but provides no read path back into an elicitation controller. It is an append-capable substrate, not yet a closed loop. + +### 3. Plugin: domain contract + +This is broader than “what entities are we after.” It owns: + +- model node kinds, slots and relations; +- utterance-shaped proposal catalog; +- fold and identity semantics; +- grade and conflict semantics; +- domain validators and diagnostics; +- projection into artifacts; +- artifact-element → model-slot → capture provenance mapping. + +I would place your concepts as follows: + +- **Typologies** → model/proposal schemas. +- **Critical points** → derived diagnostics and question triggers. +- **Motifs** → runbook hypotheses or questioning scaffolds, not model facts unless the user confirms them. + +The existing “two schemas, two tables” design covers much of this, but is probably overcommitted to a generic authoring representation before one real CPS model works. + +### 4. Plugin: runbook + +“Runbook” is a good provisional name because it adds time, direction and judgment to the current `ElicitationPack`, which is otherwise mostly a bag of cards and checks ([current contract](file:///Users/lunelson/Code/hashintel/hash/libs/@hashintel/brunch-agent/docs/spec.md#L531-L550)). + +A runbook likely needs: + +- **Supported job**: build, review, explain, or revise. +- **Kickoff contract**: objective, intended use, scope, audience, available effort. +- **Trajectory**: orient → skeleton → deepen → cross-examine → validate → hand off. +- **Priority policy**: objective relevance, information gain, dependency, user effort and risk. +- **Demand/completion rules**: what knowledge is required and to what grade. +- **Challenge checks**: contradictions, ungrounded assumptions, missing boundaries, invalid projections. +- **Stopping outcomes**: sufficient, useful-partial, stalled, budget-exhausted, user-stopped. +- **Default/inference policy**: what may be proposed, defaulted, inferred or must be asked. +- **Revision policy**: how to scope targeted re-elicitation and determine affected projections. +- **Output/handoff contract**: artifacts, provenance, visible gaps and validation results. +- **Recovery policy**: re-entry after another session or changed target-document. + +However, “the job” is not wholly static plugin content. The plugin offers a runbook template; kickoff creates an **engagement brief** containing the actual objective, boundaries, appetite and selected trajectory. + +### The largest missing architectural role + +The current design contains pieces of the controller—`demandRunner`, `affordanceCuer`, `firesWhen`, completion—but no interface unifying them. The controller must read: + +```text +engagement brief ++ current model and open issues ++ runbook ++ available generic strategies ++ recent conversation state +``` + +and produce a bounded next move: + +```text +ask | propose | contrast | validate | project | explain | stop +``` + +That is the hollow center between “we captured facts” and “we conducted an effective elicitation.” + +There is a parallel planning insight here: **the issue graph is analogous to the capture store**—durable inventory and dependencies, but not an execution strategy. `COORDINATION.md` is a projection over that inventory. A separate strategic plan must act like the controller/runbook: objective, current model of reality, confidence, current bet, proof spine, issue mapping and replanning triggers. + +The next architectural question I would examine is: + +> Does a plugin have one runbook, or a family of job-specific runbooks sharing one domain contract? + +My recommendation is the latter. For CPS, at least **cold-start construction** and **review-and-revise** have different kickoff, trajectory and stopping conditions. The September demo can then implement one honest `review-and-revise` runbook without pretending the complete CPS elicitor already exists. + +## User + +I agree a plugin could define multiple runbooks, that makes sense. And yes the planning insight echoes the gaps we're seeing in teh implementation. The planning itself needs a kind of "runbook" as well, in effect. The routines we've instituted so far here around the COORDINATION.md doc are just one part of that surface, and that surface should be the central sense-making tool for running the project + +## Assistant + +Exactly. **Unblocked is a property of the work graph, not a reason to perform work.** The missing surface must decide what matters next from objectives, evidence, risk and time. + +```diagram +Code/tests/docs/Linear + │ evidence + ▼ +┌──────────────────────────┐ +│ Strategic execution model│ +│ objective · current truth│ +│ bets · proof spine · risk│ +└────────────┬─────────────┘ + │ selects and scopes + ▼ + Issue graph / work + │ + └──── new evidence ────┘ +``` + +The existing artifacts each retain a narrower authority: + +- **Linear:** issue state and hard dependency truth. +- **COORDINATION.md:** topology, soft edges, seams and registry integrity. +- **SPEC-LEDGER.md:** implementation against settled obligations. +- **ADRs/specs:** durable design truth. +- **New strategic surface:** current objective, execution strategy, priority and confidence. + +### What the central surface should contain + +#### 1. Milestone contract + +- Outcome being pursued. +- Observable proof. +- Deadline and audience. +- Explicitly excluded outcomes. +- Which product claims must be honest versus merely demonstrated narrowly. + +#### 2. Current system model + +Not “tickets completed,” but: + +- what genuinely works end-to-end; +- which layers are tracer-only; +- which responsibilities are absent; +- external facts and constraints; +- assumptions with confidence and supporting evidence. + +#### 3. Current strategic bet + +A concise theory such as: + +> Preserve the three-register architecture, prove one CPS review-and-revise runbook concretely, and allow that implementation to determine the generic plugin interface. + +It should state why this bet beats alternatives and what evidence would reverse it. + +#### 4. Proof spine + +Proof obligations, not issues: + +```text +P1 CPS captures derive a model and valid SDCPN with provenance +P2 A reviewer session opens the existing target-document +P3 One scoped correction supersedes evidence and changes the model +P4 Reprojection changes the live Petrinaut net +P5 A provenance question reaches net → model → capture → utterance +P6 The complete path runs through the deployed demo surface +``` + +Each proof carries: + +- certainty; +- missing knowledge; +- acceptance oracle; +- mapped issues; +- dependencies—functional and epistemic; +- replan trigger. + +#### 5. Strategic frontiers + +“Now / next” chosen from the proof spine, with a reason. An issue being unblocked only makes it *eligible*. + +A frontier entry should answer: + +- Why now? +- What uncertainty or capability does it retire? +- What becomes possible after it? +- What is the smallest honest proof? +- Which adjacent work remains deliberately deferred? + +#### 6. Issue projection + +Map issues onto proofs many-to-many. This is where Dora’s tickets are adopted without allowing their initial decomposition to dictate implementation architecture. + +It should expose: + +- duplicate or overlapping tickets; +- tickets that contribute to no current proof; +- proofs with no owned work; +- scope that should move between tickets; +- trade-offs created by sequencing. + +#### 7. Learning and replanning + +Record only consequential shifts: + +- evidence observed; +- assumption promoted, weakened or falsified; +- confidence change; +- resulting strategy or sequence change. + +### Operating protocol + +1. **Orient:** read the strategic surface, then query live issue state. +2. **Select:** choose the frontier with the highest closure or information gain—not the lowest issue number. +3. **Scope:** define the smallest proof-producing slice. +4. **Execute and verify.** +5. **Reconcile:** update evidence, confidence, proof status and issue topology. +6. **Replan when a named trigger fires.** + +I would not design this protocol abstractly first. The September demo should be its first worked instance. We can construct the real strategic surface around the elicitor architecture, use it through at least one replan, and only then extract a reusable schema or skill. + +## User + +Agreed. I don't know yet what this is called; but it's not a roadmap, and not a blueprint, and not the spec either (we can have multiple specs anyway, at different levels) + +It's the thing that tracks objectives, evidence, risk and time -- but strategically, which is to say it continually evaluates the former against the latter: achieving objectives/obligations _UNDER_ constraints, limitations, pressures, unknowns. It consumes specs as one input and uses issues as a planning surface. + +We can model this protocol as we use it. A combination of document structure (perhaps templated?) and perhaps local agent-skills might be necessary, as we go + +## Assistant + +I think the core concept is **steering**. + +- **Steering model** — the current strategic understanding of objectives, position, constraints, evidence, risks and bets. +- **Steering surface** — the document plus tools through which people and agents inspect and update that model. +- **Steering protocol** — the recurring orient → choose → execute → reconcile → replan loop. + +`STEERING.md` would be a plausible provisional name. Unlike a roadmap, steering is explicitly feedback-driven: it continually adjusts the route against observed conditions. + +### Proposed structure + +```md +## Project steering + +### Mission +Outcome, obligations, proof, audience, deadline. + +### Current position +What is proved, tracer-only, absent, contradicted or unknown. + +### Pressures and constraints +Deadline, capacity, external dependencies, non-negotiable invariants. + +### Current strategy +The active bet, why it is preferred, and alternatives rejected for now. + +### Proof spine +The ordered capabilities or claims that must be demonstrated. + +### Active frontiers +The next proof-producing moves, why now, and what they unlock. + +### Risks, assumptions and unknowns +Confidence, evidence, consequence, retirement test and fallback. + +### Commitments and cut lines +Must / should / stretch / defer, with dates or evidence that trigger cuts. + +### Issue projection +Issues mapped onto objectives and proofs; overlaps, gaps and trade-offs. +Links to the live coordination graph rather than mirroring its state. + +### Replan triggers +Events that require reconsidering the strategy. + +### Recent confidence shifts +Only consequential learning; stable truth graduates elsewhere. +``` + +The key vocabulary could be: + +- **Objective:** desired outcome. +- **Obligation:** condition that must remain true. +- **Constraint:** unavailable or prohibited option. +- **Pressure:** fact that changes trade-offs, such as time. +- **Unknown:** material unanswered question. +- **Risk:** uncertain adverse outcome. +- **Bet:** chosen strategy under uncertainty. +- **Proof:** evidence that supports an objective or retires risk. +- **Frontier:** strategically selected next proof—not merely unblocked work. +- **Cut line:** condition under which scope is reduced or fallback selected. + +### Relationship to existing artifacts + +```diagram +Specs / ADRs ───────▶ obligations and design truth +Code / tests ───────▶ implementation evidence +Linear ─────────────▶ work inventory and hard dependencies +COORDINATION.md ────▶ issue topology, seams and registry + │ + ▼ + STEERING.md + strategy and next action +``` + +I would keep `COORDINATION.md` narrow rather than expanding it indefinitely. The steering surface consumes its graph projection while owning priority and rationale. + +### Adoption approach + +Start with the document and run it manually for the September effort. Avoid building a family of skills before the operating loop has been exercised. + +After a few real updates, stable routines could become local skills: + +1. **Orient** — assemble current evidence and identify drift. +2. **Steer** — select or revise the strategic frontier. +3. **Reconcile** — incorporate completed work and confidence shifts. +4. **Pressure-test** — challenge the proof spine, assumptions and cut lines. + +“Steering” is my strongest candidate, but I would treat the name as provisional until we use the model on the current elicitor plan. diff --git a/libs/@hashintel/brunch-agent/docs/inbox/salvage/design-questions-re-provenance.md b/libs/@hashintel/brunch-agent/docs/inbox/salvage/design-questions-re-provenance.md new file mode 100644 index 00000000000..61516e8360f --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/inbox/salvage/design-questions-re-provenance.md @@ -0,0 +1,462 @@ +# provenance questions + +## Example situation + +Suppose the prior elicitation established: + +> “There is one washdown crew shared by both lines. If both lines need washing, the crew normally handles the order with the earlier due date first.” + +The projected SDCPN contains: + +- a place or resource representation for the washdown crew; +- capacity one; +- transitions that reserve and release it; +- contention logic representing the practiced priority rule. + +During the demo, the reviewer selects that area and asks: + +> “Why is there only one washdown crew, and why does Line 1 get priority here?” + +The system needs to traverse backwards from those net elements to the basis for those decisions. + +Then the reviewer says: + +> “That changed in July. We now have a second contract crew on night shifts, but daytime still has one crew.” + +The system asks several targeted questions, updates its understanding, and changes only the relevant net region. + +## Option A: provenance directly on model fields + +The smallest design is to attach source references directly to fields in the semantic workpiece. + +```yaml +resources: + - id: washdown-crew + name: Washdown crew + capacity: + value: 1 + applies_when: daytime + epistemic_status: explicit + support: + - conversation_id: initial-elicitation + turn_id: user-12 + quote: We only have one washdown crew during the day. + contention_rule: + value: earliest-due-order-first + epistemic_status: explicit + support: + - conversation_id: initial-elicitation + turn_id: user-15 + quote: We normally send them to whichever order is due first. +``` + +The projection manifest then says: + +```yaml +net_elements: + - id: washdown-crew-available + produced_from: + model_item: washdown-crew + fields: + - capacity + - id: reserve-washdown-crew + produced_from: + model_item: washdown-crew + fields: + - capacity + - contention_rule +``` + +The provenance query is straightforward: + +```text +washdown-crew-available +→ washdown-crew.capacity +→ initial-elicitation / user-12 +``` + +### What happens during revision + +The new review turns modify the resource: + +```yaml +capacity: + daytime: 1 + night: 2 +``` + +Each value receives its own source reference. The projector produces a new desired net, and a structural diff patches the existing net. + +### Advantages + +- Fewest moving parts. +- No separate capture assertion store. +- Easy to explain. +- Enough for many “why?” questions. +- Source references stay beside the meaning they support. + +### Weaknesses + +It becomes awkward when: + +- several statements jointly support one field; +- one statement supports several model items; +- two people disagree; +- a statement is corrected rather than merely refined; +- the reviewer adds contextual truth rather than replacing the original account; +- we need to preserve what the model believed in version 1. + +For example, “one crew” was not actually false—it remained true during the day. A simple overwrite risks treating contextual refinement as correction. + +This design works best if FE-1476 only needs a narrow, clean revision with little disagreement. + +--- + +## Option B: first-class assertions inside the semantic workpiece + +The middle design gives claims their own stable identities but keeps them inside the same semantic workpiece. There is no generic capture-to-model fold subsystem. + +```yaml +assertions: + - id: assertion-washdown-day-capacity + subject: washdown-crew + predicate: available-count + value: 1 + applies_when: + shift: day + epistemic_status: explicit + lifecycle_status: active + support: + - conversation_id: initial-elicitation + turn_id: user-12 + quote: We only have one washdown crew during the day. + + - id: assertion-washdown-priority + subject: washdown-crew + predicate: practiced-contention-rule + value: earliest-due-order-first + epistemic_status: explicit + lifecycle_status: active + support: + - conversation_id: initial-elicitation + turn_id: user-15 + quote: We normally send them to whichever order is due first. + +model: + resources: + - id: washdown-crew + capacity_by_shift: + day: + value: 1 + derived_from: + - assertion-washdown-day-capacity + contention_rule: + value: earliest-due-order-first + derived_from: + - assertion-washdown-priority +``` + +This creates a three-stage provenance path: + +```text +net element +→ semantic model field +→ assertion +→ conversation evidence +``` + +The assertion is logically separate from the model field, but it does not need a separate storage system or generic plugin architecture. + +### What happens during revision + +The reviewer’s first statement creates a tentative assertion: + +```yaml +- id: assertion-washdown-night-capacity-review + subject: washdown-crew + predicate: available-count + value: 2 + applies_when: + shift: night + effective_from: 2026-07 + epistemic_status: tentative + asserted_by: + role: reviewer + support: + - conversation_id: review-session + turn_id: user-4 +``` + +The agent might then ask: + +1. Does daytime capacity remain one? +2. Is the contractor available every night or only on request? +3. What happens if both crews are already committed? +4. Does this replace the previous account, or add a night-shift exception? + +After those answers, the assertion can become explicit and active. The original daytime assertion remains active because it was not corrected. + +If the reviewer instead said: + +> “The six-hour dark-to-light washdown was the old procedure. It is four hours now.” + +That is a real supersession: + +```yaml +- id: assertion-dark-to-light-four-hours + subject: dark-to-light-washdown + predicate: typical-duration + value: PT4H + lifecycle_status: active + supersedes: + - assertion-dark-to-light-six-hours +``` + +The old assertion remains visible for historical explanation, but it no longer drives the current model. + +### Advantages + +- Handles correction, refinement, contextual truth, and disagreement cleanly. +- Gives provenance a stable unit smaller than an entire model object. +- Allows one semantic item to depend on several assertions. +- Allows one assertion to support several semantic items. +- Makes reviewer authorship explicit. +- Supports versions without requiring a graph database. +- Fits the phrase “captured assertion” honestly. + +### Weaknesses + +- Requires us to define an assertion contract. +- Requires lifecycle decisions: active, superseded, tentative, conflict. +- Requires a small interpretation step from assertions into the current model. +- Can grow into the retired typed kernel if we type everything indiscriminately. + +The restraint would be: + +> Assertions only need enough shape to support provenance, correction, and the selected projection—not the old universal kind/slot/completion system. + +This is my current recommendation for FE-1476. + +--- + +## Option C: separate capture ledger and folded model + +The fullest design makes assertions independent durable capture records: + +```text +Flue conversation +→ extraction/sweep +→ capture assertion ledger +→ fold +→ semantic model +→ SDCPN projection +``` + +An assertion might look similar to Option B, but it is written into a capture store independently of the workpiece. The semantic model is then derived entirely by folding active assertions. + +Revision becomes: + +```text +new reviewer turns +→ new captures and supersession +→ fold model again +→ project desired net +→ diff +→ patch +``` + +### Advantages + +- Strongest separation between evidence and interpretation. +- Full correction history. +- Potentially supports many sessions and many projections. +- The model can be regenerated from assertions. +- Closest to the original “requirements graph” idea. + +### Weaknesses + +This is where the large machinery returns: + +- extraction must decide assertion boundaries; +- captures require semantic types; +- correction and conflict semantics must be defined; +- fold behavior must be deterministic enough to trust; +- capture granularity becomes consequential; +- in-loop extraction risks returning to Condition 5 latency; +- the fold and model must agree under evidence reordering; +- the live revision crosses more independently failing boundaries. + +It could be the long-term architecture. It is a risky assumption to make the two-week demo depend on it. + +--- + +# Why I prefer Option B + +Option B takes the minimum useful property from the requirements-graph design—**first-class, source-linked, revisable assertions**—without requiring the full capture/fold architecture. + +Conceptually: + +```text +semantic workpiece +├── assertions: what people said, with source and lifecycle +└── model: what currently drives projection, with assertion references +``` + +It can be one JSON/YAML artifact or one document with a machine-readable region. “Graph” describes the relationships, not the storage technology. + +The resulting durable package could be: + +```text +review-artifact/ +├── workpiece.json +│ ├── assertions +│ └── current semantic model +├── net.json +└── projection-manifest.json +``` + +The transcript remains durable in Flue history. For export and optimisation handoff, quoted excerpts and conversation/turn IDs can also be embedded in the workpiece so the package does not become meaningless if the live Flue store is unavailable. + +## Full six-beat behavior under Option B + +### 1. Show the completed workpiece + +The “requirements graph” UI could initially be modest: + +- objective and boundary; +- process spine; +- activities and resources; +- assertions and unresolved assumptions; +- links between assertions and model items. + +It need not be a graph visualization. Inspectable JSON plus a human-readable view may be enough for the first proof. + +### 2. Examine the SDCPN + +The SDCPN is projected from the `model` region, not composed from transcript prose. + +Stable semantic IDs determine stable net IDs: + +```text +resource:washdown-crew +→ place:resource:washdown-crew:available +``` + +### 3. Ask why + +The reviewer selects or names a net element. + +The system reads the projection manifest: + +```text +place:resource:washdown-crew:available +→ model resource washdown-crew / capacity_by_shift +→ assertions A17 and A23 +→ quoted turns user-12 and user-19 +``` + +The model can explain in prose, but it is not inventing the chain. + +### 4. Targeted re-elicitation + +The selected element establishes scope. The agent receives: + +- the relevant model item; +- its supporting assertions; +- neighboring constraints; +- the reviewer’s question. + +It conducts 3–5 focused turns rather than reopening the entire interview. + +### 5. Change the net + +The settled turns produce new or superseding assertions. The current semantic model is updated. + +Then: + +```text +project whole small model deterministically +→ diff by stable IDs +→ reject unrelated churn +→ apply only changed mutations +``` + +We can explicitly test that IDs outside the selected impact set remain byte-for-byte unchanged. + +### 6. Optimisation handoff + +Chris and Yannis receive: + +- revised `net.json`; +- scenario/parameter inputs; +- projection manifest; +- relevant assumptions and unresolved gaps; +- optionally the complete review artifact. + +They do not need to inspect a Flue transcript to understand where the model came from. + +# The reviewer-authority problem + +The fact that the reviewer is **not the original expert** matters more than it first appears. + +Suppose the original expert said: + +> “Dark-to-light washdown takes six hours.” + +The reviewer says: + +> “I think it is four now.” + +There are three possible products: + +1. **Authoritative editing:** the reviewer may supersede the original assertion. +2. **Proposed revision:** the reviewer creates a candidate assertion requiring confirmation. +3. **Contextual alternative:** both claims remain active under different conditions. + +A source-linked Markdown field can record the latest answer, but first-class assertions make these outcomes explicit. The net should probably change automatically only for an accepted authoritative correction or a clearly contextual refinement. A tentative disagreement might produce a preview or named conflict instead. + +That authority policy is part of the demo semantics, not merely UI wording. + +# Parallel work enabled by this boundary + +Once the assertion, semantic item, stable-ID, and projection-manifest contracts are pinned, several tracks can proceed in separate worktrees: + +1. **Baseline/evaluation track** + Run and grade the frozen elicitation baseline. + +2. **Semantic workpiece track** + Build Option B from an existing Mission 3 IR and transcript. + +3. **Projection/diff track** + Use a fixture workpiece to produce a stable net, manifest, and scoped diff. + +4. **Provenance interaction track** + Build “why?” against a fixed projection manifest before live projection exists. + +5. **Petrinaut mutation track** + Prove the minimal live client-tool patch path using a predetermined diff. + +6. **Targeted re-elicitation track** + Rehearse 3–5-turn scoped revision against a fake workpiece adapter. + +7. **Optimisation-handoff track** + Confirm the exact net/scenario package Chris and Yannis can consume. + +These can be asynchronous because they meet at explicit artifacts. The contracts must be fixed first; otherwise parallel agents will each invent a different meaning of assertion, model item, and impact set. + +## My present recommendation + +For FE-1476: + +- use **first-class assertions inside the semantic workpiece**; +- point assertions directly to durable conversation evidence; +- derive the current projectable model from those assertions without building a generic fold engine; +- generate a projection manifest alongside the SDCPN; +- reproject and diff rather than building a general incremental projector; +- defer a separate capture assertion ledger until the integrated slice reveals that the workpiece cannot carry the necessary evidence lifecycle. + +The next question I would settle is: + +> **Does the reviewer have authority to commit a correction directly, or are their changes proposals that require confirmation from an original domain expert or another named authority?** + +That answer determines the assertion lifecycle and whether the live demo changes the canonical net immediately or first shows a proposed revision. diff --git a/libs/@hashintel/brunch-agent/docs/inbox/claude-dafny-lean.md b/libs/@hashintel/brunch-agent/docs/inbox/salvage/more-plugin-inputs/claude-dafny-lean.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/claude-dafny-lean.md rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/more-plugin-inputs/claude-dafny-lean.md diff --git a/libs/@hashintel/brunch-agent/docs/inbox/kleppmanns-geolog-design-rationale-2026-04-18T12-57-01Z.md b/libs/@hashintel/brunch-agent/docs/inbox/salvage/more-plugin-inputs/kleppmanns-geolog-design-rationale-2026-04-18T12-57-01Z.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/kleppmanns-geolog-design-rationale-2026-04-18T12-57-01Z.md rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/more-plugin-inputs/kleppmanns-geolog-design-rationale-2026-04-18T12-57-01Z.md diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-1-pn-consumption-trigger-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-1-pn-consumption-trigger-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-1-pn-consumption-trigger-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-1-pn-consumption-trigger-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-1-pn-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-1-pn-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-1-pn-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-1-pn-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-2-spn-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-2-spn-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-2-spn-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-2-spn-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-3-cpn-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-3-cpn-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-3-cpn-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-3-cpn-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-4-dcpn-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-4-dcpn-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/gases-4-dcpn-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/gases-4-dcpn-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/semiconductor-fab-drift-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/semiconductor-fab-drift-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/semiconductor-fab-drift-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/semiconductor-fab-drift-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/truck-fleet-predictive-maintenance-layout.json b/libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/truck-fleet-predictive-maintenance-layout.json similarity index 100% rename from libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/truck-fleet-predictive-maintenance-layout.json rename to libs/@hashintel/brunch-agent/docs/inbox/salvage/sdcpn-examples-to-validate/truck-fleet-predictive-maintenance-layout.json diff --git a/libs/@hashintel/brunch-agent/docs/mission-archive/4-core-plugin-elicitation-proof-of-life.md b/libs/@hashintel/brunch-agent/docs/mission-archive/4-core-plugin-elicitation-proof-of-life.md new file mode 100644 index 00000000000..f69eb0b4335 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-archive/4-core-plugin-elicitation-proof-of-life.md @@ -0,0 +1,134 @@ +# Mission 4 — prove the Brunch core/plugin elicitation architecture is alive + +## Status + +**Closed by owner adjudication on 2026-09-03** for [FE-1563](https://linear.app/hash/issue/FE-1563/redesign-the-elicitation-runbook-and-workpiece-against-the-frozen). The owner accepts the implemented independent core `elicitation` capability and core/plugin/app responsibility pattern on the narrower observed evidence below. The technically valid S4 review-to-elicitation failure remains a frozen failure but is a non-blocking nice-to-have at this mission boundary; no full-run workpiece candidate exists. See [`mission-4-closure-and-deferral-2026-09-03.md`](../evidence/decisions/mission-4-closure-and-deferral-2026-09-03.md). + +Current state: the agreed topology baseline is implemented on the current Graphite ancestry at `baba973269ce7ecf1a47de8749c751033b2ce471` (historical pre-restack implementation `93eb211dd3d7fa07bc5b1ff69ddb402b45b07cf9`), with the owner-accepted pre-freeze inlining repair recorded in [`mission-4-inline-universal-elicitation-2026-09-03.md`](../evidence/decisions/mission-4-inline-universal-elicitation-2026-09-03.md). Core mounts an independently activatable `elicitation` capability skill; plugin-sdcpn is a contribution bundle whose job skill activates it; plugin-gherkin and a stubbed plugin-dafny hold their proposed homes and are not composed. The YAML plugin machinery is removed; suspended code is isolated under `src/_suspended/`. The persona harness, six prospective case families, and client-tool hosts are landed evaluation infrastructure. The owner-frozen [`mission-4-proof-of-life-v1`](../../evaluations/protocols/mission-4-proof-of-life-v1/protocol.md) stopped after both Vestera attempts exposed an instrument defect: its isolated persona was asked to apply an undefined evaluator-owned semantic stop category. V1 remains immutable evidence and no later v1 slot may run. The owner authorized preparation of [`mission-4-proof-of-life-v2`](../../evaluations/protocols/mission-4-proof-of-life-v2/protocol.md), which replaces only that semantic stop with a fixed three-submission probe under fresh ids. The owner accepted v2's exact 35-file manifest at `d9ca2fe1498f6484746b2abaaf18973e7abcbeaa` and authorized execution; currency gating was suspended while usage reporting and every logical ceiling remained binding. V2 admitted four attempts: both interactive probes and S3 passed, then the technically valid S4 run failed item 4e because it identified the knowledge gap without first activating `elicitation`. The frozen serial rule stopped execution before Industrial Gas, so the `3/3` floor and workpiece candidate were not completed and the bounded proof-of-life claim is not established. See the [campaign adjudication](../evidence/evaluations/mission-4-proof-of-life-v2/final-adjudication.md), [freeze acceptance](../evidence/decisions/mission-4-proof-of-life-v2-freeze-acceptance-2026-09-03.md), and [repair decision](../evidence/decisions/mission-4-retire-v1-and-cut-v2-2026-09-03.md). + +This recut removes the topology-neutral case portfolio, broad workpiece-quality campaign, Mission 3 comparative adjudication, Petrinaut browser witness, and comprehensive close-out sweep from FE-1563's blocking proof. The proposed [`mission-4-topology-neutral-case-matrix.md`](../../evaluations/cases/mission-4-topology-neutral-case-matrix.md) remains unaccepted future input to be allocated by the successor addendum and the first missions that make its individual cases load-bearing. Mission 4 will retain one exact conversation/workpiece/manifest bundle as a downstream handoff candidate, but will make no workpiece-quality, reusable-fixture, database-seed, or product-parity claim about it. + +### Architecture kernel (owner-accepted) + +1. **Topology.** Exactly this, per package; directories are homes, not mandatory slots, and `tools/` exists only where an executable capability is earned: + + ```text + packages/core/src/ + ├── prompts/SYSTEM.md always-on universal invariants + ├── skills/elicitation/{SKILL.md, skill.ts} + ├── skills/skill-markdown.ts SKILL.md + files → defineSkill + └── flue.ts useBrunchAgent(): model, elicitation skill, prompt + packages/plugin-<pairing>/src/ + ├── prompts/APPEND_SYSTEM.md optional always-on plugin policy + ├── skills/<job>/{SKILL.md, references/, templates/, skill.ts} + ├── tools/ only when a real capability exists + └── flue.ts use<Pairing>Plugin(): selected mounting + apps/brunch-agent registration, transport, diagnostics, app tools + ``` + + Authored paths equal the packaged paths the model reads. This follows Flue's native skill-directory convention (`skills/<name>/SKILL.md` with frontmatter and supporting files; name equals directory). +2. **Responsibility test.** A prompt carries invariants that must hold for the whole mounted lifetime of a contribution. A skill carries the procedure **and judgment** for a recognizable job or capability. A tool contract carries the semantics and constraints of one executable operation. Package authority (core, plugin, app, binding) and primitive type are independent axes; core-owned does not imply always-on. +3. **Core capability.** `elicitation` is core's one capability skill: adaptive human-knowledge acquisition and epistemic correction. It excludes target review, target mutation, construction, and tool execution. Job skills activate it when progress requires knowledge that cannot be responsibly inferred from existing evidence. This is the owner's topology; it is not a hypothesis under test by routing experiments. +4. **Plugin cardinality.** A plugin is a contribution bundle, not a symmetric inventory. It contributes the smallest set of independently activatable job skills its real user jobs earn. `sdcpn-modelling` and `gherkin-specification` are one job each; Dafny's specification/verification split is an open pressure-test question, not a commitment. +5. **Content basis.** Ampcode is the conceptual basis ([design evidence](../evidence/design/mission-4-prompt-skill-tool-architecture.md)); Five-Register supplies the domain-primary workpiece template and the evidence-level checks. No third synthesis. Registers classify what guidance does; they are not phases, question order, schemas, or file topology. +6. **Question dosage.** The accepted wording is the Ampcode text now in production: do not open with a battery of independent questions; deepen one answerable thread at a time; group questions only when they share one frame. "Exactly one question", "one interrogative sentence", and "at most one `?`" were narrower operationalizations introduced downstream and are withdrawn. The proof-of-life campaign gates only the opening prohibition and reports later dosage without making it acceptance-determining. +7. **Scope of experimental authority.** Behavioral evidence may falsify an implementation, a prompt wording, or a proof claim. It may not select or replace the topology in items 1, 3, or 4; a topology change is presented to the owner with the observed strain and a proposed smallest mitigation. +8. **Source-to-production manifest.** The current-ancestry selected sources are the Ampcode (`A`) and Five-Register (`F`) drafts at `ca57b45729260cc657f89b718fc505997a4e1b3c:libs/@hashintel/brunch-agent/packages/core/_drafts/`, byte-identical on those trees to historical recovery commit `e087f570d77507c12a4862604a30c6fcd640aa2f`; the Gherkin paper instrument (`G`) is `A`'s `plugin-gherkin/` copied unchanged to `evaluations/protocols/gherkin-shape-c-paper-v1/instrument/`. Current-ancestry topology baseline: `baba973269ce7ecf1a47de8749c751033b2ce471`; the inlining repair is the one additional accepted production delta before campaign freeze. Proof item 1 is run against this table and the repair record; any other difference is a stop condition. + + | Production file | Source | Permitted delta | + | --- | --- | --- | + | `core/src/prompts/SYSTEM.md` | `A core/SYSTEM.md` | none | + | `core/src/skills/elicitation/SKILL.md` | accepted capability wrapper plus `A core/universal-elicitation.md` | the universal file's reference-only heading and preamble are removed; its operative guidance is otherwise inlined unchanged after the accepted wrapper | + | `core/src/skills/skill-markdown.ts`, `core/src/flue.ts`, each `skill.ts`, each `flue.ts` | none; mounting code | must package each `SKILL.md` and its files at the authored relative paths | + | `plugin-sdcpn/src/prompts/APPEND_SYSTEM.md` | `A plugin-sdcpn/APPEND_SYSTEM.md` | none | + | `plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md` | `A .../sdcpn-modelling/SKILL.md` | "Activate the `elicitation` skill" replaces reading the packaged universal reference; `references/` and `templates/` path prefixes; the exact `/.flue/packaged-skills/...` resource-URI sentence | + | `plugin-sdcpn/.../references/profile.md` | `A .../sdcpn-elicitation.md` | filename only | + | `plugin-sdcpn/.../references/pn-construction.md` | `A .../pn-construction.md` | none | + | `plugin-sdcpn/.../references/checks.md` | `F .../checks.md` | none | + | `plugin-sdcpn/.../templates/workpiece.md` | `F .../workpiece-template.md` | filename only | + | `plugin-gherkin/src/prompts/APPEND_SYSTEM.md` and `skills/gherkin-specification/{references/*, templates/workpiece.md}` | `G` | filenames and path prefixes only | + | `plugin-gherkin/src/skills/gherkin-specification/SKILL.md` | `G SKILL.md` | the same three adaptations as the SDCPN skill | + | `plugin-dafny/**` | none; stub homes authored 2026-09-02, marked "Stub" in every file | placeholder only; no procedure, no mounting by any app | + | removed: core `plugin/`, `schema/`, `teaching/`, `interpretation/`, `testing/`, `prompts.ts`; both `plugin.yaml`; binding `useElicitation` | retired YAML/typed-plugin machinery | removal is part of the accepted implementation; last present on current ancestry at `f7f77544dab022be667f535ca73181ddc57535e0`, byte-identical on the named retired paths to historical `924be780ce6a5e7ebbbc0e43b72042ceb93c8387` | + +## Imperative + +Prove that the implemented core/plugin architecture is alive on the production Flue `ChatAgent` path: SDCPN work that needs human operational knowledge activates core's independently mounted `elicitation` capability before substantive questioning, required judgment is disclosed before reliance, and named construct-only and resolvable-review paths refrain from elicitation. Retain one exact full-run conversation/workpiece bundle as a downstream handoff candidate without promoting it to accepted workpiece, reusable fixture, database seed, or Petrinaut product evidence. + +## Throughline + +```text +accepted topology baseline at baba973269 + accepted universal-guidance inlining repair +→ production ChatAgent mounting and disclosure proof +→ canonical raw-history snapshot + mechanically derived ordered trace +→ persona-driven full conversation plus two cross-case activation probes +→ controlled resolvable-review and knowledge-gap-review checks +→ one exact conversation/workpiece/manifest handoff candidate +→ owner adjudication of the bounded proof-of-life claim +→ FE-1563 PR close report and successor handoff +``` + +The exercised production door is the persona harness's direct Flue client → long-running production `ChatAgent` → provider. This crosses the real agent, skill, tool, settlement, and conversation-storage boundary. It does not cross Petrinaut's `/api/chat` AI SDK adapter, browser `useChat`/`onToolCall`, live editor document, browser persistence, deployment replacement, or remote infrastructure boundary. + +## Proof + +This proof establishes bounded cross-case activation and restraint through the production agent and retains one attributable downstream candidate. It does not establish general reliability, full workpiece quality, the topology-neutral case portfolio, comparative superiority to Mission 3, fixture or seed validity, Petrinaut browser parity, deployment, or comprehensive mission-family closure. + +1. **Topology and translation fidelity.** Production matches the architecture kernel and source manifest with only the permitted deltas. Oracle: exact protected-surface comparisons from [`mission-4-restacked-authority-sha-audit-2026-09-03.md`](../evidence/decisions/mission-4-restacked-authority-sha-audit-2026-09-03.md), rerun at freeze; `packages/core/test/architecture/boundaries.test.ts`; and the SDCPN, Gherkin, and Dafny skill packaging tests. +2. **Production mounting and disclosure.** The built `ChatAgent` presents the core prompt, SDCPN append, and a catalog containing `elicitation` and `sdcpn-modelling`; scripted activation exposes exact packaged resources. Oracle: `apps/brunch-agent/test/build-artifact.test.ts` and `apps/brunch-agent/test/petrinaut-chat.test.ts` driving the production `ChatAgent` integration without treating the test UI projection as campaign evidence. +3. **Canonical evidence mechanism.** Every proof run can retain the raw settled `history()` snapshot and a mechanically derived ordered trace of user turns, skill activations with names/outcomes, conditional resource reads, other tools/executors, and workpiece-bearing text. Construct-only results record activated skill names. Oracle: focused unit tests for trace derivation, raw snapshot writing, canonical event order, and the extended `runbook-headless` result, followed by inspection that each frozen run directory contains snapshot, transcript, trace, adjudication, manifest, and SHA-256 values. +4. **Interactive activation proof of life.** Per elicitor model, the frozen campaign obtains three valid 4a-gradable runs over three distinct current persona case families: one full 6–10-turn conversation that emits a recoverable workpiece and two fixed three-submission probes whose first Substantive text is located after settlement by the independent adjudicator. All three must show successful `sdcpn-modelling` then `elicitation` activation and the conditional SDCPN profile read before the first Substantive text. Orientation may precede activation. Oracle: candidate [`mission-4-activation-and-restraint-ruler-v2.md`](../../evaluations/oracles/mission-4-activation-and-restraint-ruler-v2.md), canonical traces, quoted independent adjudication, and exact `3/3`; invalid or no-Substantive members remain separately visible and do not satisfy the floor. +5. **Controlled restraint and complement.** Construct-only execution does not activate elicitation; exact S3 resolvable review identifies the supplied target defect without elicitation; exact S4 knowledge-gap review activates elicitation and asks for the missing operational rule without inventing it. Oracle: the frozen v2 ruler, extended `runbook-headless` test, and one retained canonical run each for the exact S3/S4 prompt strings and hashes recorded by the ruler. These controlled cues do not prove uncued review-routing robustness. +6. **Opening and resource restraint.** No interactive run's first Substantive text is a Battery; activated universal guidance and the conditional profile precede reliance; construction resources are absent from ordinary interviewing; if the full run emits a workpiece, the template read precedes the first workpiece text in canonical order in the same turn. Later-turn dosage is classified and reported but does not determine proof-of-life acceptance. Oracle: frozen v2 ruler and retained trace/adjudication. +7. **Downstream handoff candidate.** The full run retains one recoverable Markdown workpiece beside its exact raw Flue conversation, trace, elicitor/persona model manifest, case identity, source/frozen commit, validity and adjudication records, and SHA-256 values. The manifest labels it `evaluation-run` and `handoff-candidate`, never accepted workpiece, reusable fixture, database seed, product conversation, or Petrinaut witness. Oracle: mechanical hash verification plus a check that every named file exists and the workpiece's recorded source message id resolves in the raw snapshot. +8. **Integrity and bounded close.** Core, binding, transport, three plugins, and app build/type/lint/unit checks pass at the frozen commit; Mission 3 evidence remains unchanged from current-ancestry close commit `4c11c7a6c4e1df26c9d76cec30e32af8f013042d`; the PR close report states the bounded claim, invalid members, candidate status, and every deferral; the mission receives external owner adjudication before archival. Oracle: focused Turbo checks, an empty diff over `libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1` from `4c11c7a6c4...` to the frozen commit, artifact inspection, and owner decision. + +## Constraints + +- The owner retains candidate text changes, instrument freeze, paid ceiling, proof adjudication, candidate promotion, and closure. These are external actions an agent waits for and records only afterwards. +- No production prompt, skill, resource, or topology edit occurs without a repair record naming observed failure, responsible disclosure layer, smallest change, and regression risk, followed by owner acceptance and one focused commit. +- The frozen campaign ruler may falsify behavior but may not redefine topology or interaction policy. No question-count, sentence-count, punctuation-count, response-length, or packaged-topology proxy enters production text or acceptance. +- Persona infrastructure is not campaign authority. A protocol becomes authoritative only when exact case allocation, models, budgets, validity, retry/stop rules, retention layout, and hashes are frozen in a focused commit. +- No paid call occurs before a clean frozen instrument and explicit owner acceptance of its stop rules covering elicitor, persona, review checks, invalid replacements, and adjudication. Record any active currency gate or its owner-authorized suspension separately. +- Every admitted run receives a unique run id; invalid and failed members are retained; no user utterance is silently resent after admission. +- Canonical Flue history is evidence. Pi rendering, persona summaries, browser/debug projections, and evaluation-side tool details are corroboration only. +- The handoff candidate is not a fixture or seed. No Flue database rows, generated submission/incarnation ids, capture ids, browser principal/conversation mapping, or headless document state are copied and presented as authentic product state. +- Preserve the immutable Mission 3 campaign, its recorded source commit `b738aa1be1a62a9f9cdde89ced78558f04293a77`, and its ruler unchanged. The restacked patch-equivalent `57b8900a04c56aa9e0d833fcbab8d290ab9756eb` improves current-ancestry resolvability but never rewrites historical run provenance. +- Keep one model-facing agent, Flue-native `useInstruction`/`useSkill`/`useTool`, progressive disclosure, the `ChatAgent` door, and skill directories packaged through `defineSkill`. No loader, workflow engine, second elicitor, TUI, YAML plugin definition, or repertoire runtime. +- Construction stays outside ordinary elicitation. The real-headless client host is an in-memory Petrinaut-core callback executor, not browser execution, product persistence, or parity proof. +- Read-only audits may run in parallel; no concurrent writers in the shared worktree. + +## Fog-line + +Do not design past these until the frozen proof or a successor owner settles them: + +- Whether the v2 fixed-turn probes reach a first Substantive text and satisfy the accepted activation/read ordering without any persona-owned semantic stop decision. +- Whether simulator refusal can be bounded by a content-neutral retry rule without biasing valid members; the protocol must diagnose and pre-register its handling before paid execution. +- Whether the accepted independent activation succeeds `3/3`; any miss is strain to adjudicate, not permission to switch topology. +- Whether the full conversation emits a recoverable candidate within its selected 6–10-turn budget and what limitations remain visible in that workpiece. +- Whether that candidate is eligible for Mission 5 promotion after proof of life; Mission 4 records no semantic-quality acceptance. +- Whether direct-Flue persona evidence can later be promoted into a deterministic fixture or product/database seed without fabricating identity, settlement, tool-execution, capture, or browser-document provenance. +- Which topology-neutral cases become load-bearing in the successor addendum, Mission 5 provenance work, Mission 6 projection, Mission 7 revision, or a later readiness sweep. + +## Stop or reorient + +Stop and surface the evidence if: + +- the raw snapshot and derived trace disagree, required ordering cannot be mechanically recovered, or a retained artifact cannot be bound by hash to its source run; +- any of the three valid interactive members fails activation or required-read ordering, or the exact S3/S4/construct-only checks violate their expected activation boundary; +- a first Substantive text is an opening Battery, the conditional profile is missing or late, or a template/construction resource is used before its accepted branch; +- simulator, provider, transport, or client-tool invalidity is counted as a behavioral pass/fail, silently replaced, or rerun under the same id; +- an oracle or repair changes topology or introduces a stricter interaction rule without a separate owner decision; +- a candidate run touches the immutable Mission 3 control or historical run records; +- the direct Flue path is described as `/api/chat`, Petrinaut browser execution, populated product state, deployment, or remote proof; +- the handoff candidate is called accepted, fixture-ready, seeded, or product-authentic without the successor promotion contract; +- paid execution begins without a clean freeze and explicit owner acceptance of the active logical and currency-gate state; +- closure is recorded before external owner adjudication of the bounded proof-of-life claim. + +## Deferred + +A separate issue, branch, PR, and mission authority may own the Mission 4 close-out addendum: the observed S4 report-versus-immediate-ask transition; broader reliability/hardening if warranted; Petrinaut `/api/chat` and browser parity; source/workpiece preparation and fixture/seed promotion contracts; topology-neutral case allocation; contract/readiness sweeps; authority/archive subtraction; and reconciliation with the landed but not remotely proved Mission 8 application contract. Re-enter S4 only when a real review must continue immediately or repeated gap-only reports create visible friction. Gate A remains proposed input, not an accepted whole-suite obligation. + +Mission 5 cannot inspect a Mission 4 full-run candidate because none was produced. It or the predecessor addendum must select an explicitly eligible retained source—such as immutable Mission 3 evidence—or commission a new run, then establish and owner-gate the minimum workpiece eligibility, identity mapping, capture provenance, and fixture/seed promotion its visible claim consumes. Voice integration is a parallel parent/reconciliation concern under [`mission-4-voice-integration-handoff.md`](../evidence/implementations/mission-4-voice-integration-handoff.md); it does not reopen Mission 4 or make S4 blocking. + +Mission 6 owns automatic traceable projection into one meaningful live SDCPN region. Mission 7 owns bounded authorized reviewer revision and scoped net patching. Mission 8's existing deployment branch stopped after local application proof and still lacks remote infrastructure proof. Mission 9 owns the optimisation handoff after its consumer contract exists. Broad observer, compaction, voice, structured-question, remote-release, Gherkin/Dafny production-route, and complete topology-neutral regression work remain future planning rather than FE-1563 authority. diff --git a/libs/@hashintel/brunch-agent/docs/mission-archive/4-owner-led-runbook-and-workpiece-redesign.md b/libs/@hashintel/brunch-agent/docs/mission-archive/4-owner-led-runbook-and-workpiece-redesign.md new file mode 100644 index 00000000000..404fbfd15e1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-archive/4-owner-led-runbook-and-workpiece-redesign.md @@ -0,0 +1,113 @@ +# Mission 4 — owner-led runbook and workpiece redesign + +## Status + +Closed for mission transition on 2026-09-01 by owner direction before Mission 8 was cut. This archive preserves the execution contract for [FE-1563](https://linear.app/hash/issue/FE-1563/redesign-the-elicitation-runbook-and-workpiece-against-the-frozen); the deployment cut makes no independent claim that every proof item below passed. Mission 3's frozen prospective campaign is the immutable control: one invalid runtime member, two valid independently graded workpieces, and an adjudicated range recorded in [`docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md`](../evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md). Its observed artifacts and source revision `b738aa1be1a62a9f9cdde89ced78558f04293a77` remain the exact instrument of record; current source may be relocated, but no relocated branch-tip file may be represented as the frozen v1 instrument. + +Later projection, provenance, bounded revision, observer, host-continuity, and optimisation concerns remain in [`MISSION.next.md`](../../MISSION.next.md). They are not implementation authority here. + +### Accepted design decision — Markdown prompt authority and core source topology + +**Observed strain:** a growing generic system prompt would be awkward as a TypeScript literal, while the core package's flat `src/` mixed active evidence contracts, suspended conversation/interpretation/teaching machinery, and plugin SDK declarations. **Local obligation:** keep one authoritative production prompt in `src/SYSTEM.md`, keep temporary prompt-shaping material outside `src/`, and group implementation by the authority that causes it to change without changing public package exports or runtime behavior. **Owner acceptance:** the owner approved the Markdown prompt migration, temporary browseable workbench, and authority-based core topology before implementation. The workbench is non-authoritative and must be deleted or moved into a versioned evaluation protocol when shaping is complete. + +### Accepted design decision — Flue-native package ownership + +**Observed strain:** the app-local `ChatAgent` mixed the core Brunch prompt, SDCPN steering and runbook resources, SDCPN construction tools, Petrinaut host tools, and transport conventions, making none of their package authorities visible. **Local obligation:** follow Flue's native distinction between the agent's returned core instructions, `useInstruction` contributions, progressively disclosed `useSkill` resources, and `useTool` capabilities while exposing core and SDCPN ownership directly. **Alternatives:** preserving a substrate-neutral core would have kept agent composition in `binding-flue`, contrary to the intended split; making core and plugin Flue-native keeps the app as the required directive-marked registration and host composition point. **Owner acceptance:** the owner explicitly selected the Flue-native core/plugin alternative before implementation, then clarified that core must remain deployable in any context and for any output formalism: SDCPN and Petrinaut model-facing identity, guidance, and tools belong to plugin-sdcpn, while browser execution and transport remain app-owned. This decision relocates the existing instrument without changing its runbook semantics and does not reactivate the generalized repertoire/`useElicitation()` runtime. + +### Accepted design decision — plugins pair a domain typology with a target formalism + +**Observed strain:** the formalism-only plugin definition left the reusable operational-process typology with no honest owner, even though SDCPN Recognition, Coverage, Operations, and Verification depend on both that subject-matter typology and the target representation. **Local obligation:** make each plugin carry one reusable domain typology / target formalism pairing while preserving the boundary between reusable typology and concrete user domain. **Alternatives:** keeping plugins formalism-only would scatter domain-typology guidance into core or the app; making plugins per concrete domain would bake scenario nouns into reusable teaching. **Owner acceptance:** the owner selected the paired plugin model. `plugin-sdcpn` carries operational processes plus SDCPN; `plugin-gherkin` carries software behavior plus Gherkin. Concrete operations, organizations, situations, and scenarios remain conversation/workpiece content, never plugin units. + +## Imperative + +Manually reshape the technically viable Mission 3 elicitation runbook and Markdown workpiece into a stronger, research-informed candidate that the owner understands and endorses one consequential decision at a time. + +Use the completed research synthesis, historical workpieces, owner-supplied edge cases, and frozen prospective baseline as evidence rather than treating any one source as a specification. The mission should expose where the current package's teaching order, question dosage, epistemic treatment, phase boundaries, or workpiece structure strain under real modelling, make the least structural change that answers each observed strain, and establish the gains, regressions, and remaining uncertainty of the resulting candidate against the frozen control. + +This is not an autonomous rewrite and not a statistical claim of universal superiority. The design conversation and manual walkthroughs are part of the work: the owner expects remodeling itself to reveal consequential corner cases that an agent-generated replacement would miss. + +## Throughline + +One stepwise redesign through the production core-agent and SDCPN-plugin package seam: + +```text +research synthesis + historical workpieces + current authored resources +→ owner and agent inspect one observed strain or edge case +→ agree the local obligation and smallest structural or teaching change +→ manually revise the one-skill runbook/workpiece package +→ walk known and owner-supplied cases through that revision +→ repeat only at the new fog-line +→ freeze a versioned candidate instrument +→ run it through the production Flue ChatAgent +→ grade the recovered workpiece with the frozen ruler +→ compare candidate evidence with Mission 3's frozen control +``` + +The semantic edit surface is the SDCPN plugin's existing modelling skill: `SKILL.md`, `elicitation.md`, `ir-template.md`, and only the construction/check material whose phase ownership is directly implicated. Brunch core owns the stable Flue agent prompt; plugin-sdcpn owns its additional prompt material, runbook skill, and target-specific tools; the app owns only the directive-marked registration point and Petrinaut host composition. Amend the structural-typing specification only when an accepted redesign decision contradicts it. Keep changes reviewable in small semantic steps rather than replacing the package wholesale. + +The baseline is available as a comparison input, not a design prescription. Its observed range and dispositions must be considered before the candidate instrument is frozen or any improvement claim is made. Candidate runs use a new versioned protocol/output location and preserve their own instrument manifest; they never write into or modify the baseline campaign. + +## Proof + +This proof establishes that the owner-led redesign produces a coherent candidate runbook/workpiece package with inspectable evidence of its effects. It does not establish projection, provenance, observer consolidation, scoped net revision, comprehensive domain semantics, or final demo readiness. + +Observe all of the following: + +1. Each consequential redesign has a recorded observed strain or edge case, the local obligation it answers, the alternatives considered where material, and owner acceptance before implementation. +2. The package remains one real Flue skill with progressive disclosure. Ordinary elicitation stays in operational vocabulary; IR headings, SDCPN structures, and construction schemas do not become an opening questionnaire. +3. The workpiece gives an authoritative, cold-readable home to the objective and process spine; expert evidence; agent inference; assumptions with reason and how-to-check; unknown, unasked, declined, deferred, and conflicting material where present; corrections or contextual coexistence; and construction-opened losses without laundering authorship. +4. Concrete walkthroughs exercise 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. Owner-supplied cases may deepen this set. +5. One frozen candidate version runs through the production Flue `ChatAgent` and emits a recoverable workpiece. Its run artifacts record the exact candidate instrument and remain separate from the Mission 3 baseline. +6. Independent omniscient and cold review with the frozen ruler identifies gains, regressions, disagreements, and new mistakes relative to the control. A human adjudication states what the evidence supports without collapsing baseline variation to one mean score. +7. The resulting candidate is sufficient to prepare the prebuilt FE-1476 workpiece or explicitly names the smallest remaining workpiece gap. No projection success is inferred from workpiece quality. + +Prefer one coherent candidate and a discriminating comparison over many lightly reasoned variants. A fluent interview or attractive template by itself is not proof. + +## Constraints + +- Preserve the Mission 3 prospective baseline artifacts unchanged and treat source revision `b738aa1be1a62a9f9cdde89ced78558f04293a77` as the committed v1 instrument. Current source may move without compatibility wrappers; candidate runs must record the relocated files under a new versioned campaign and may not write into the v1 output location. +- Treat the authoritative elicitation research synthesis as evidence and decision support, not a backlog to implement wholesale. Shared-source repetition is not independent corroboration. +- The owner leads semantic and editorial choices. The agent presents alternatives, traces consequences, edits accepted decisions, and runs probes; it does not infer approval or silently choose the final package. +- Work one observed strain or owner-supplied edge at a time. Prefer subtraction, relocation, and clearer authority before adding another catalog, abstraction, or artifact. +- Keep universal elicitation, SDCPN investigation, workpiece structure, and PN construction distinct enough that each can change without turning questions into schema slots. +- Preserve one model-facing agent, one runbook skill, Flue's native returned instructions, `useInstruction`, `useSkill`, `useTool`, resource disclosure, and the production `ChatAgent` door. Do not add a loader, workflow engine, second agent, or TUI. +- Do not add a comprehensive ontology, closed claim kinds, typed completion algebra, generalized plugin/repertoire runtime, observer fold, projection engine, capture-store join, or live Petrinaut mutation path. The statically composed Flue-native core and SDCPN package contributions are the production path, not a reactivation of generalized `useElicitation()`. +- Preserve exact expert evidence and honest authorship. Normalized prose, agent assumptions, unasked material, and explicit user unknowns remain distinguishable. +- Do not tune reusable guidance only to Vestera. Scenario facts and owner examples may test the package but do not enter reusable teaching as universal facts. +- Keep construction outside ordinary elicitation. Move construction-owned material only when the redesign can observe the effect; do not broaden into Mission 5's provider-schema problem. +- Update affected Petrinaut user-facing documentation only if this mission changes user-visible behavior in Petrinaut packages; internal runbook resource edits alone do not create that obligation. + +## Fog-line + +Do not design the whole candidate before resolving these questions with the owner at the affected resource: + +- Whether the first useful change is teaching order/dosage, workpiece structure, authorship/epistemic treatment, or a smaller combination. +- Whether broad headings survive, collapse into a case/process spine plus epistemic ledger, or become objective slices with supporting cases. Versioned assertion clusters are not the default. +- Whether question batching guidance belongs in always-on routing, the skill body, or elicitation resources, and whether the prospective control replicates historical opening overload. +- How much `Transform to PN` guidance should leave elicitation and whether projection losses should open only during construction. +- How to represent correction, contextual coexistence, declined/deferred material, and directional/context-dependent values without mandatory per-statement semantic typing. +- Which duplicate summaries can be removed only after one authoritative home is demonstrated. +- The smallest candidate campaign that can reveal regression given baseline variance without pretending one scenario proves universal superiority. +- Whether a manually discovered edge belongs in reusable guidance, a probe/grader catalog, the workpiece shape, or mission evidence only. + +Resolve each at the smallest real or paper boundary and record the decision before continuing. A longer fog-line after a walkthrough is calibration, not failure. + +## Stop or reorient + +Stop and surface the evidence if: + +- the redesign edits or overwrites the frozen baseline artifacts, case, graders, ruler, or source revision, or presents relocated current source as the v1 instrument; +- the agent produces a wholesale replacement before the owner works through its consequential choices; +- headings or typologies become a scripted intake form or dictate the interview's opening order; +- uncertainty is handled by inventing more mandatory fields rather than preserving it honestly; +- normalized agent prose is presented as verbatim expert evidence or never-asked material becomes a user-declared unknown; +- concrete Vestera or owner-supplied facts leak into reusable teaching; +- construction, projection, provenance, observer scheduling, capture folding, or live net mutation expands into this mission because a later mission may need it; +- a candidate is called better from fluency, parser shape, aesthetics, or one favorable anecdote without the frozen comparison ruler and explicit regressions; +- the redesign requires restoring Condition 5's foreground `brunch_ask`, typed extraction/fold, completion accounting, or minute-scale ordinary turns. + +A need for one stronger structural distinction is not permission to rebuild the retired typed kernel. Name the exact ambiguity and test the least mechanism that could resolve it. + +## Deferred + +Mission 5 owns the first workpiece-to-live-SDCPN projection and evidence-backed provenance answer. Mission 6 owns bounded reviewer re-elicitation and scoped net patching. Mission 7 owns the complete six-beat rehearsal and optimisation handoff. The inferential observer remains an optional spike rather than this mission's workpiece mechanism. Host/session continuity, compaction, voice, broad observability, simulated-conversation viewing, and remote release remain in `MISSION.next.md` unless this mission's real throughline exposes a direct blocker. diff --git a/libs/@hashintel/brunch-agent/docs/mission-archive/README.md b/libs/@hashintel/brunch-agent/docs/mission-archive/README.md index abdf8c67d2f..b2935cae472 100644 --- a/libs/@hashintel/brunch-agent/docs/mission-archive/README.md +++ b/libs/@hashintel/brunch-agent/docs/mission-archive/README.md @@ -1,10 +1,9 @@ # Closed missions -Accepted `MISSION.md` files, moved here on close. Evidence of what was proven, not execution -authority. The next-concerns draft and cut rule live in the context-root -[`AGENTS.md`](../../AGENTS.md). +Closed `MISSION.md` files, moved here on close or explicit owner-directed branch transition. Each file's status states what was accepted, falsified, or left unadjudicated. These are historical contracts and evidence records, not execution authority. The next-concerns draft and cut rule live in the context-root [`AGENTS.md`](../../AGENTS.md). -- [`1-bare-petrinaut-flue-chat.md`](1-bare-petrinaut-flue-chat.md) — Mission 1, accepted - 2026-08-27. -- [`2-mechanical-capture-sweep.md`](2-mechanical-capture-sweep.md) — Mission 2, accepted - 2026-08-27. +- [`1-bare-petrinaut-flue-chat.md`](1-bare-petrinaut-flue-chat.md) — Mission 1, accepted 2026-08-27. +- [`2-mechanical-capture-sweep.md`](2-mechanical-capture-sweep.md) — Mission 2, accepted 2026-08-27. +- [`3-structurally-typed-runbook-to-headless-pn.md`](3-structurally-typed-runbook-to-headless-pn.md) — Mission 3, closed 2026-08-31 with the runbook/workpiece path accepted and real-model construction falsified on the exercised route. +- [`4-owner-led-runbook-and-workpiece-redesign.md`](4-owner-led-runbook-and-workpiece-redesign.md) — Mission 4's interim 2026-09-01 branch-transition archive, later superseded when the owner reopened the mission; retained as historical contract evidence. +- [`4-core-plugin-elicitation-proof-of-life.md`](4-core-plugin-elicitation-proof-of-life.md) — Mission 4's final 2026-09-03 closure: core/plugin implementation accepted on narrower evidence, S4 review-to-elicitation transition deferred, and no full-run workpiece candidate produced. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/10-bounded-reviewer-revision.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/10-bounded-reviewer-revision.md new file mode 100644 index 00000000000..7c20b04c3a3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/10-bounded-reviewer-revision.md @@ -0,0 +1,249 @@ +# Draft Mission 10 — Bounded reviewer revision and scoped patch + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1394](https://linear.app/hash/issue/FE-1394/revise-one-traceable-net-region-through-targeted-reviewer-elicitation) — tracker projection for this future branch mission; the eventual branch `MISSION.md` remains execution authority. + +A fresh builder must read these sources before cutting or implementing this cluster: + +- [`MISSION.md`](../../MISSION.md) — closure pointer for Mission 4. Mission 4 produced no full-run workpiece candidate; consume only the source/workpiece pair explicitly selected and promoted by Mission 7 or a predecessor addendum. +- [`MISSION.next.md`](../../MISSION.next.md) — compact shared frame, standing locks, and current mission joins. +- [`README.md`](README.md) — durable draft authority, lifecycle, conversion, and oracle-gap rules. +- [`docs/mission-archive/2-mechanical-capture-sweep.md`](../mission-archive/2-mechanical-capture-sweep.md) — exact-evidence capture, idempotency, Flue-history authority, and model-free scheduling. +- [`docs/mission-archive/3-structurally-typed-runbook-to-headless-pn.md`](../mission-archive/3-structurally-typed-runbook-to-headless-pn.md) — accepted workpiece leg and falsified provider-schema construction leg. +- [`evaluations/oracles/ir-quality-ruler-v1.md`](../../evaluations/oracles/ir-quality-ruler-v1.md) and [`evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md`](../../evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md) — current conservation, grounding, conflict-collapse, and cold-reading criteria; neither is yet a successive-revision oracle. +- [`docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md`](../evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md) — healthy foreground-turn range, costly whole-workpiece synthesis, and observed correction handling. +- [`packages/core/src/prompts/SYSTEM.md`](../../packages/core/src/prompts/SYSTEM.md), [`packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md`](../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md), and [`packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md`](../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md) — current foreground lifecycle and workpiece correction behavior. +- [`apps/brunch-agent/test/petrinaut-chat.test.ts`](../../../../../apps/brunch-agent/test/petrinaut-chat.test.ts), [`apps/brunch-agent/test/headless-petrinaut-client.test.ts`](../../../../../apps/brunch-agent/test/headless-petrinaut-client.test.ts), and [`packages/plugin-sdcpn/src/tools/petrinaut-construction.ts`](../../packages/plugin-sdcpn/src/tools/petrinaut-construction.ts) — current real door, bounded mutation subset, and its limits. +- [`7-capture-backed-review.md`](7-capture-backed-review.md) and [`9-traceable-projection.md`](9-traceable-projection.md) — provisional inherited artifacts, provenance seam, projection contract, and stable-identity obligations. Re-resolve these joins against accepted close evidence at cut time rather than assuming draft hypotheses landed. +- Commit `157730cc5a214dd9c543e8d95c7193a219c48aef` on deployment branch `ln/fe-1569-brunch-agent-deployment`, especially `libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md` — local application contract and the still-open infrastructure proof that any deployed durability claim must consume. + +## Visible product advance + +**Release note:** a second person corrects the model in conversation and only the relevant part of the net changes. + +**Demo script (no engineer present):** open the demo net in the Petrinaut Brunch panel, on the deployment posture named at cut time. As a reviewer who was not the original expert, challenge one modelled fact in a few focused turns. Watch that region of the net update, with the correction attributed to you and the original expert's evidence still visible in the why answer. Confirm the rest of the net did not move. Then ask for a second change you are not entitled to make, or that the evidence does not support, and watch Brunch decline or qualify rather than comply. + +**Previously impossible:** changing the net meant regenerating it or hand-editing it, and no one could tell afterwards who had said what. + +Through the Petrinaut Brunch panel, a scenario-authorized reviewer selects one operational meaning, challenges or refines it in 3–5 focused turns, inspects the attributed prior and current meaning plus a semantic change account, and sees either: + +- a linked SDCPN-region patch with unrelated identities and behavior unchanged; +- an explicitly justified widening of the impact boundary; or +- a visible refusal because authority, evidence, consistency, staleness, or canonical validation does not permit the change. + +The updated why answer retains the original expert evidence, adds the reviewer evidence with attribution, and explains the disposition of prior meaning. Recency by itself never grants overwrite authority. + +The broader demonstration portfolio is not yet enumerated. The accepted floor here is the selected FE-1476 review/revise case plus the revision classes named below, not every operational process or every possible correction. + +**Completion:** the mission is done when both the accepted correction and a visible refusal or qualification appear in the same demo at the readiness gate below. One successful correction alone is the throughline tracer inside the mission. + +## Contract stratum + +The named stratum is **bounded reviewer-authority, workpiece-revision, and linked-patch locality for one selected projected region**. + +Scenario-declared authority must name the reviewer, the selected workpiece meaning and linked net region, the kinds of change the reviewer may settle, and any required confirmation or owner boundary. Authority outside that declaration is absent, not inferred. + +The currently accepted peer classes are: + +1. **Correction** — the authorized reviewer establishes that prior canonical meaning was wrong; the prior revision and evidence remain retained while the new revision names supersession. +2. **Qualification** — new evidence narrows, conditions, or hedges prior meaning without erasing the supported core. +3. **Contextual coexistence** — both accounts remain valid under distinguishable conditions; the workpiece and projection preserve the split rather than selecting the newest. +4. **Unresolved conflict** — accounts disagree and authority/evidence does not resolve them; canonical state does not silently advance. +5. **Refusal / rejected or unsupported change** — the request exceeds declared authority, lacks evidence, targets stale state, or cannot pass impact or canonical validation. + +Stale-base revision and legitimate impact widening are cross-class failure/extent cases. Any additional class must be explicitly accepted when the live mission is cut. + +## Boundary crossings and current throughline hypothesis + +```text +scenario declares reviewer authority + selected region + base revisions +→ reviewer enters the deployed Petrinaut assistant panel +→ AI SDK /api/chat transport resumes the owning Flue conversation +→ foreground Brunch agent conducts 3–5 focused operational-language turns +→ Flue history retains the canonical conversation +→ harness-owned mechanical sweep durably captures the settled reviewer range +→ one bounded foreground phase-boundary synthesis reads: + prior workpiece revision + current derivation/region + newly captured evidence +→ synthesis classifies correction | qualification | coexistence | conflict | refusal +→ attributed next workpiece revision + semantic diff + impact declaration +→ authority, base-revision, evidence, and impact gates admit or refuse commit +→ SDCPN plugin applies the bounded patch through Petrinaut-owned canonical mutations +→ Petrinaut validates the current net and selected behavior +→ panel shows revised meaning, disposition, patch/refusal, and updated why answer +``` + +The foreground phase-boundary synthesis is the default. It is one explicit semantic operation after the focused review, not extraction/fold work on every turn. The patch consumes the committed current workpiece and current net; neither the transcript nor an observer queue is an unbounded fallback. + +## Throughline proof floor + +One scenario-declared, consequential operational distinction must cross the real deployed path and produce: + +- 3–5 focused reviewer turns in operational vocabulary; +- mechanically retained, attributed reviewer evidence; +- an inspectable prior/current workpiece pair and semantic diff; +- the correct class disposition; +- a bounded patch or explicit refusal; +- updated element → derivation → workpiece revision → original and reviewer evidence provenance; and +- unchanged ids and behavior outside the declared impact, except where a visible, justified widening is accepted. + +The default tracer should be a correction because it proves canonical change. It must be selected so a mistaken overwrite, qualification, coexistence, and conflict treatment would be observably different. One successful correction is only throughline proof and the first internal milestone; it does not close the class stratum and it is not mission completion, which additionally requires the demo's visible refusal or qualification. + +## Readiness ratchet + +### Inherited stratum closure + +This cluster may start only after the prior missions have supplied and accepted: + +- Mission 7's honest prebuilt pair, durable exact-evidence provenance, broken-link behavior, and element/workpiece/evidence identity seam; +- Mission 9's meaningful automatically projected live region, derivation coverage, canonical provider-visible mutation path, repeated-projection identity behavior, and explicit partial/unsupported failure; +- the current workpiece revision and exact source Flue conversation selected at the prior handoff; +- a deployment boundary that actually persists every state this path consumes across the replacement behavior it claims. + +Draft links are not evidence. If Mission 7 or Mission 9 ships a different representation, Mission 10 must consume that actual contract or return here for re-cutting. + +### Readiness gate after the new throughline + +Before this visible capability ships, assess the whole accepted class set: correction, qualification, contextual coexistence, unresolved conflict, refusal/unsupported change, stale revision, and impact widening. + +For each, record: + +- declared authority and the reason canonical state may change or must not change; +- preservation, supersession, qualification, contextual split, conflict retention, or rejection of prior meaning; +- semantic diff quality and attributed evidence support; +- workpiece, derivation, and net-link churn; +- base-revision and stale-state behavior; +- patch scope, unrelated identity stability, and behavior preservation; +- latency, token/usage cost, foreground blocking, and visible failure; +- transcript fallback behavior; and +- compaction/recovery behavior if the exercised deployed path crosses those boundaries. + +These peer obligations close in Mission 10 because Mission 11 consumes a trustworthy final revision rather than becoming the first owner of reviewer-authority semantics. The next owner is Mission 11 only for the selected complete artifact and accepted optimisation handoff. Re-entry gate: Chris/Yannis' accepted consumer contract reveals another revision class or makes a currently deferred durability/identity property load-bearing. Oracle: the new class must be added to the revision oracle and exercised through the deployed panel before Mission 11 may rely on it. + +Breadth beyond the named classes and accepted scenario portfolio remains unearned. + +## Candidate evidence and oracles + +- `apps/brunch-agent/test/petrinaut-chat.test.ts`, test **“the committed /api/chat door streams a plain Flue agent through server and client tools”**, currently proves the production AI SDK/Flue door, client-tool correlation, history recovery, ownership refusal, exact capture excerpts, idempotent recapture, and absence of sweep/construction tools on the interviewer. It does not prove reviewer revision or deployed infrastructure. +- `apps/brunch-agent/test/headless-petrinaut-client.test.ts`, tests **“constructs a parser-accepted document through the bounded callbacks”** and **“refuses tools outside the side-quest subset”**, currently prove only the six-tool construct subset and parser acceptance. They are evidence for bounded capability/refusal, not a scoped update patch or semantic fidelity. +- `evaluations/oracles/ir-quality-ruler-v1.md` supplies stable `CONFLICT-COLLAPSE`, `CONS-MISS`, `CONS-DISTORT`, `INVENT`, `HARDEN`, `SCOPE`, and `GAP-MISCLASS` judgments. Its own scope excludes successive revision and PN construction, so it may seed but cannot settle the revision claim. +- `docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md` records one correction preserved in a workpiece and healthy ordinary-turn timing. It does not prove authorization, successive revision, or patch locality. +- Canonical action/schema/parser tests under `libs/@hashintel/petrinaut-core/src/actions.test.ts`, `src/file-format/parse-sdcpn-file.test.ts`, and `src/file-format/serialize-sdcpn.test.ts` are exact inner oracles for accepted payloads and round-trip document validity, not meaning or locality. +- **ORACLE GAP — successive semantic revision:** no current oracle compares prior workpiece + newly captured evidence against the next revision across all five classes. Before cut, freeze a reviewed fixture set and adjudication rubric that detects lost supported meaning, incorrect authority, unsupported strengthening, conflict collapse, and incorrect disposition. +- **ORACLE GAP — patch locality and behavior:** no current oracle proves that a semantic revision changes the intended linked region while preserving unrelated ids and behavior. Before cut, define the selected region, explicit allowed impact set, before/after id inventory, semantic expectations, and—where discriminating—a Petrinaut simulation comparison. +- **ORACLE GAP — outer path:** no current test or artifact witnesses the 3–5-turn scenario portfolio through a remotely deployed Petrinaut/Brunch path. Before claiming the visible advance, record a human witness against the accepted deployment, exact scenario/base revisions, transcript, workpiece diff, mutation trace, before/after net, and refusal output. +- **ORACLE GAP — durable capture join:** the deployment handoff explicitly leaves the JSON capture store inactive and non-durable. Before this path consumes capture remotely, Mission 7 or this cut must identify and test the durable implementation across the claimed task-replacement boundary. + +## Verification approach + +- **Inner mechanism:** deterministic tests for authority checks, base-revision refusal, exact evidence references, semantic-diff representation, class disposition, idempotent commit, impact calculation, and canonical mutation validation. Use the frozen class fixtures and revision oracle; parser success cannot substitute for semantic review. +- **Middle integration/contract:** drive the production `ChatAgent`/AI SDK path from the selected Mission 9 artifact, perform the mechanical settled-range capture and foreground synthesis, apply the patch through the actual browser client-tool callbacks, and compare persisted before/after workpiece, derivation, and net artifacts. Exercise a stale-base attempt and one explicit refusal. +- **Outer deployed/user-visible:** a named human witness performs each accepted peer class through the deployed panel, including the 3–5-turn correction tracer, and verifies visible attribution, semantic diff, changed region, stable unrelated ids/behavior, updated why answer, and comprehensible refusal/failure. The live mission owns this outer proof; it cannot be delegated to Mission 11. + +## Inputs and joins + +- Upstream source exit: the frozen workpiece, exact source Flue conversation, instrument manifest, and evaluation/adjudication explicitly selected by Mission 7 or a predecessor addendum; Mission 4 itself supplies no full-run candidate. +- Mission 7: prebuilt workpiece/net pair, current workpiece revision and references, capture evidence references, net-element ids, projection rationale, durability disposition, and the why interaction. +- Mission 9: selected meaningful region, canonical mutation surface, derivation records, repeat-projection identity evidence, and accepted unsupported/partial behavior. +- Mission 8: consume the actual application contract—fail-closed Postgres Flue state, verified TLS, IAM/static-password paths, content-free OTel, restricted routes, liveness, singleton ownership policy—but do not imply it is deployed. The infrastructure handoff, real RDS/Anthropic/collector/replacement/rollback proof, and owner acceptance remain required before an outer deployed claim. +- Mission 11: receives only an accepted final workpiece/net/evidence/derivation revision package and the six-beat real-path evidence; its consumer contract may not weaken Mission 10's revision-integrity closure. + +## Risks and assumptions + +- **ASSUMPTION:** bounded foreground synthesis can incorporate 3–5 turns without losing prior meaning or blocking ordinary turns. **Impact if false:** the default revision mechanism is not trustworthy or usable. **Cheapest validation:** run the five frozen class fixtures against prior revision + exact captures and measure phase-boundary latency separately from foreground turns. +- **ASSUMPTION:** scenario-declared authority is sufficient for the selected review. **Impact if false:** a reviewer may make an unauthorized canonical change or every change may require another owner. **Cheapest validation:** have the scenario owner adjudicate one allowed correction and one cross-boundary refusal before implementation. +- **ASSUMPTION:** Mission 9's stable ids and derivation neighborhood are sufficient to calculate a bounded impact. **Impact if false:** local revision can cause unrelated churn or require broader context. **Cheapest validation:** dry-run the selected semantic change against the frozen Mission 9 before/after artifact and enumerate the minimal connected impact. +- **RISK:** semantic diff reports textual edits while hiding a changed operational claim. **Impact:** a reviewer cannot understand what changed. **Cheapest validation:** cold human comparison against the class fixture's expected preserved/changed meaning. +- **RISK:** compaction removes the recoverable workpiece or evidence needed by synthesis. **Impact:** stale or transcript-dependent revision. **Cheapest validation:** if the real path crosses compaction, reconstruct the same current revision and evidence references after that boundary; otherwise label the limitation and keep it outside the shipped durability claim. +- **RISK:** the capture store remains task-local JSON while the service claims replacement durability. **Impact:** reviewer evidence may disappear after acceptance. **Cheapest validation:** inspect the consumed Mission 7/Mission 8 storage contract before cut and refuse remote revision until capture durability is observed. + +## Accepted constraints and guarded invariants + +- **STOP-THE-LINE — bounded authority:** canonical state changes only under the scenario's declared reviewer authority. Guard: authority fixture plus allowed/refused outer witness. +- **STOP-THE-LINE — evidence retention:** original and reviewer evidence stay exact, attributed, immutable, and reachable; model prose is never presented as quotation. Guard: capture identity/excerpt assertions and provenance inspection. +- **STOP-THE-LINE — no recency overwrite:** prior supported meaning survives unless explicitly corrected, qualified, context-split, or retired under authority. Guard: successive-revision oracle across every accepted class. +- **STOP-THE-LINE — patch locality:** unrelated ids and behavior remain stable, and necessary expansion is declared before commit. Guard: before/after id inventory, accepted impact set, and semantic/simulation check where applicable. +- Flue history remains the canonical conversation log; the capture ledger is not a second transcript. +- Mechanical capture remains domain-opaque and harness-owned. The foreground Markdown workpiece owns semantic synthesis. +- The foreground model neither receives nor schedules a sweep tool. Ordinary turns do not block on extraction, fold, completion, or projection. +- Petrinaut owns canonical SDCPN schemas and mutations. Brunch imports or mechanically consumes them and does not copy field shapes. +- Brunch remains a second assistant; preserve stock-assistant operation and distinct histories. Keep the panel on AI SDK `useChat` / `onToolCall`. +- No comprehensive ontology, closed claim kinds, typed completion algebra, generic assertion fold, generalized runtime, TUI, second agent, or second server is earned. + +## Cross-cutting obligations + +- Workpiece sufficiency: a cold reader can understand both revisions and the disposition without transcript archaeology. +- Projection fidelity and evidence provenance: the patch follows the committed workpiece change, and every changed consequential element reaches projection rationale and both generations of evidence. +- Revision integrity and patch locality: class handling, semantic diff, retained meaning, stable ids, and explicit impact widening remain inspectable as separate claims. +- Petrinaut semantic acceptance: canonical validation, non-empty result, and selected behavior are checked; tool-call or parser success alone is insufficient. +- Interaction quality and visible failure: focused foreground turns remain in a healthy latency class, and authority conflict, staleness, unsupported change, schema failure, or locality failure cannot silently advance state. +- If Petrinaut user-visible behavior changes, update the relevant pages under `libs/@hashintel/petrinaut/docs/` in the same change and prompt the owner to replace any screenshots made stale. + +## Expected touched paths + +Tentative until the Mission 7/9 joins and live UI boundary are inspected: + +```text +libs/@hashintel/brunch-agent/ +├── packages/core/src/SYSTEM.md ~ revision/authority conduct if needed +├── packages/core/src/evidence/ ? generic retained-evidence/revision mechanics only if earned +├── packages/plugin-sdcpn/src/skills/sdcpn-modelling/ ~ foreground review and workpiece revision guidance +├── packages/plugin-sdcpn/src/tools/ ~ canonical update/patch capability selected by Mission 9 +├── evaluations/oracles/ + successive-revision oracle +├── evaluations/protocols/ + frozen class fixtures/protocol +└── docs/evidence/evaluations/ + observed revision campaign/adjudication +apps/brunch-agent/ +├── src/agents/chat-agent/ ~ compose only accepted capabilities +├── src/capture/ ~ settled-range durable join, not semantic fold +├── src/conversation/ ? explicit phase-boundary operation if this is the earned home +├── src/http/ ? only if the existing real door needs generic transport support +└── test/ ~ production-path revision, refusal, persistence, and locality coverage +libs/@hashintel/petrinaut-core/ +├── src/action-schemas.ts ~ only if a canonical mutation gap is proven upstream +└── src/simulation/ ? only if selected behavior comparison is discriminating +libs/@hashintel/petrinaut/ +├── src/ui/views/Editor/panels/ai-assistant-panel* ? visible diff/impact/refusal surface if chat is insufficient +└── docs/ ~ when user-visible behavior changes +``` + +No path is permission to edit before the cluster is cut. Prefer existing generic host extension points over Brunch-specific Petrinaut library logic. + +## Fog-line + +- The exact accepted scenario portfolio beyond the selected correction tracer. +- The exact authority declaration and whether any class requires original-expert or owner confirmation. +- The semantic-diff representation that is understandable without imposing per-statement semantic typing. +- The smallest linked neighborhood sufficient for synthesis and impact analysis. +- What counts as unchanged behavior outside the region and when simulation is a useful discriminator. +- How legitimate impact widening is previewed, authorized, and either committed or refused. +- The exact durable storage/transaction boundary across captures, workpiece revision, derivation links, and net commit. +- Whether the short path crosses Flue compaction; if not, which limitation must remain visible in the handoff. +- Whether the chat answer alone makes prior/current meaning and patch impact inspectable or a generic linked detail surface is required. +- Exact latency and usage ceilings must come from the accepted scenario and deployment budget, not invention. + +## Stop or reorient + +- Stop if reviewer authority cannot be named per scenario or a tentative proposal can silently become canonical truth. +- Stop if a correction, qualification, coexistence, conflict, or refusal cannot preserve and explain prior meaning. +- Stop if phase-boundary synthesis blocks each ordinary turn, repeatedly loses supported meaning, reads stale state, is unrecoverable, or unavoidably depends on unbounded history. +- Stop if reviewer evidence is not durable before canonical workpiece or net state changes. +- Stop if the patch rereads the transcript as its primary model, mutates from an uncommitted workpiece, or silently rebuilds unrelated regions. +- Stop if unrelated ids or behavior churn without an explicit, authorized impact widening. +- Stop if canonical Petrinaut schemas must be copied into Brunch or parser/tool success is presented as semantic success. +- Stop if deployment is claimed from the local Mission 8 image evidence without the remote infrastructure proof. +- Stop if implementation grows an ontology, deterministic capture-to-workpiece reducer, typed fold, completion algebra, or generalized revision platform to handle the selected stratum. +- Stop and reassess observer re-entry only under the named strain below; do not add it by momentum. + +## Carried evidence and rejected alternatives + +- **Default retained:** one bounded foreground phase-boundary synthesis over the prior workpiece revision and newly mechanically captured reviewer evidence. Whole-workpiece synthesis already has a distinct, higher latency class than ordinary turns; measure it at the boundary rather than moving semantic work into every turn. +- **Observer/fold rejected by default:** no observer exists on the production path, and the current IR ruler has not tested successive observer revisions. The canonical promotion mechanics and extraction ladder live in [`MISSION.next.md`](../../MISSION.next.md#foreground-revision-and-observer-re-entry). Mission 10 is the decisive strain gate: re-entry is considered only after repeated consequential foreground blocking, loss of prior supported meaning, stale state, unrecoverability, or unavoidable unbounded-history dependence. An admitted observer still may not mutate the net, and its likely short-review barrier is a forced tail sweep/queue flush because the token threshold alone may never fire. +- **Recency overwrite rejected:** newer testimony may correct, qualify, coexist with, or conflict with earlier testimony. Time order alone is not authority. +- **Append-only journal rejected:** it preserves history but leaves excessive cold-reading and canonical-meaning burden; the observed workpiece intent is a maintained current account with retained revision history. +- **One artifact rejected:** immutable captures and editable semantic workpiece revisions have different lifecycles. +- **Deterministic typed fold rejected:** Condition 5's typed mapping plus in-loop model judgment caused minute-scale ordinary turns. Re-entry requires evidence that foreground synthesis cannot reliably preserve or classify meaning and that the narrower mechanism fixes the observed failure. +- **Full regeneration rejected as a visible patch claim:** an implementation may reconsider broader context internally only if the applied diff remains bounded and unrelated identities/behavior are proven stable; otherwise it must report widening or refuse. +- Mission 3 evidence establishes an accepted, cold-usable workpiece leg and one observed correction, but its construction leg failed 0-for-9 on nested provider-visible shape and produced a vacuous empty net. Mission 9 must retire that risk before this cluster treats patching as available. +- The v3 Flue composition comparison found packaged universal guidance more reliable for opening elicitation but exposed shared review routing as weak. Mission 4 owns the selected one-job-skill content repair; Mission 10 must consume its actual accepted review route rather than reopen topology by momentum. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/11-optimisation-handoff.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/11-optimisation-handoff.md new file mode 100644 index 00000000000..f94547cf6bc --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/11-optimisation-handoff.md @@ -0,0 +1,187 @@ +# Draft Mission 11 — Accepted optimisation handoff + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1503](https://linear.app/hash/issue/FE-1503/hand-one-accepted-sdcpn-to-an-optimisation-experiment) — tracker projection for this future branch mission; the eventual branch `MISSION.md` remains execution authority. + +A fresh builder must read these durable sources before deepening this cluster: + +- [`../../MISSION.md`](../../MISSION.md) — current closure pointer. Mission 4 is closed; later accepted mission archives and an owner-authorized live cut become inherited authority before this draft can execute. +- [`../../MISSION.next.md`](../../MISSION.next.md) and [`README.md`](README.md) — shared frame, standing locks, draft authority, and lifecycle. +- [`10-bounded-reviewer-revision.md`](10-bounded-reviewer-revision.md) and the eventual accepted Missions 7, 9, and 10 close evidence — inherited real-path artifacts and proof. Draft promises are not join evidence. +- [`../mission-archive/3-structurally-typed-runbook-to-headless-pn.md`](../mission-archive/3-structurally-typed-runbook-to-headless-pn.md) — accepted workpiece leg, falsified real-model construction, and the parser-valid-empty warning. +- [`../../../petrinaut-core/src/file-format/serialize-sdcpn.ts`](../../../petrinaut-core/src/file-format/serialize-sdcpn.ts), [`../../../petrinaut-core/src/optimization.ts`](../../../petrinaut-core/src/optimization.ts), and [`../../../petrinaut/docs/optimization.md`](../../../petrinaut/docs/optimization.md) — existing Petrinaut terrain to inspect with the consumers, not a preselected handoff boundary. +- Commit `157730cc5a214dd9c543e8d95c7193a219c48aef` on deployment branch `ln/fe-1569-brunch-agent-deployment`, especially `libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md` — locally verified application contract and explicit application-to-infrastructure stop. +- The written Chris/Yannis consumer contract and accepted fixture, once they exist. Their absence is the fog-line, not permission to infer topology from current source. + +## Visible product advance + +**Release note:** Chris and Yannis start an optimisation experiment on a model that came out of Brunch, without asking anyone to reconstruct it. + +**Demo script (no engineer present):** the handoff package opens in the form the consumers accepted, carries the scenario, parameters, assumptions, omissions, losses, and limits, and the agreed experiment begins. This mission passes the product-manager litmus by construction because its outcome is an external consumer acting on Brunch output. + +**Previously impossible:** no Brunch output had an external consumer. + +Mission 11 broadens the proven bounded projection/revision path only across the scenario Chris and Yannis accept for one optimisation question. That same traceable path produces one selected complete SDCPN and a visible/exportable package from which they can begin the agreed experiment. + +The known semantic artifact floor is: + +- revised workpiece; +- final selected SDCPN; +- capture-backed evidence and projection/revision derivation; +- selected scenario and parameters; +- assumptions; +- omissions; +- construction/projection losses; and +- unresolved limits. + +The package representation, export topology, execution contract, and returned result are unknown. This draft does not choose them. + +The six-beat FE-1476 rehearsal proves that the handoff came through the real deployed elicitation, review, revision, and scoped-patch path. Rehearsal is evidence, not the sole product outcome. + +## Contract stratum + +The provisional stratum is **one consumer-accepted handoff for one selected complete SDCPN and one agreed optimisation experiment**. + +Before this cluster can be cut, Chris and Yannis must accept: + +1. the input artifacts; +2. one concrete optimisation question; +3. the required scenario and parameter representation; +4. the execution boundary; +5. the expected returned result; and +6. the minimum credibility checks. + +Only that acceptance can define “complete,” “exportable,” “can begin,” the relevant peers, and the contract closure required here. Do not broaden from one experiment to a generic optimisation integration or universal package. + +Mission 11 owns the breadth needed to move from Mission 10's accepted bounded region to the selected complete model. “Complete” means complete for the one consumer-accepted question and scenario, not exhaustive of every workpiece fact or operational process. + +## Boundary crossings and current throughline hypothesis + +Only the evidence-backed prefix and consumer-visible outcome are currently earned: + +```text +accepted Mission 10 bounded projection/revision path +→ Chris and Yannis define “complete” for one optimisation question +→ broaden the proven traceable path only across that accepted scenario +→ selected complete SDCPN + revised workpiece + evidence/derivation +→ scenario/parameters + assumptions/omissions/losses/limits +→ ORACLE GAP: consumer-accepted handoff boundary +→ Chris and Yannis can begin one agreed experiment +``` + +Do not fill the oracle gap with Petrinaut's current manifest, host capability, UI, archive shape, service, notebook, or another plausible transport before the consumers accept one. + +## Throughline proof floor + +One selected complete SDCPN and the known semantic artifact floor: + +- are produced by broadening the witnessed projection/revision path across only the accepted scenario, rather than substituting a hand-curated final model; +- are handed to Chris and Yannis in the form they accepted; +- carry the accepted scenario and parameters with honest assumptions, omissions, losses, and limits; and +- let them begin the one agreed optimisation experiment at the accepted boundary. + +This proves one accepted handoff and is also the completion bar, since the consumer's acceptance is the readiness decision. It does not prove a generic export package, optimizer integration, result model, repeated-experiment protocol, public release, or broad scenario portfolio. + +## Readiness ratchet + +### Inherited stratum closure + +Mission 11 consumes rather than repairs: + +- Mission 7's durable capture-backed provenance and visible why behavior; +- Mission 9's meaningful automatic projection, stable identities/derivations, and closed projection stratum for the selected region; +- Mission 10's accepted reviewer-authority classes, retained evidence, semantic revision, scoped patch/refusal, and stable unrelated behavior; and +- an actual deployment threshold sufficient for the consumers to use the path, with each claimed identity, durability, telemetry, access, and recovery property observed rather than inferred from the local image. + +If inherited closure is missing, return the defect to its owning mission. Mission 11 must not script around it for rehearsal. + +### Readiness gate after the new throughline + +The lateral obligations are intentionally not enumerated beyond the six consumer decisions. Once the first accepted handoff works, enumerate only the package, transfer, execution, result, credibility, repeatability, access, or retention obligations that the actual boundary exposes. Close those required to trust and begin the selected experiment. Carry broader obligations only with a named successor, re-entry gate, and oracle. + +Until consumer acceptance, any more detailed readiness list would plan past the fog-line. + +## Candidate evidence and oracles + +- Existing Petrinaut serialization, scenario, optimization, and UI contracts are terrain to inspect with Chris and Yannis. Their tests establish only what the current product can represent or execute; they do not establish acceptance, package shape, or experiment credibility. +- Accepted Missions 7, 9, and 10 artifacts must provide the exact conversation, bounded workpiece revisions, captures, derivations, mutation trace, revised region, revision disposition, and deployed witness from which Mission 11 broadens. Their eventual archive/evidence paths replace these draft joins. +- The pinned Mission 8 handoff proves a local application artifact only. Remote infrastructure, replacement, real provider/collector behavior, rollback, and acceptance remain open. +- **ORACLE GAP — consumer contract:** record Chris and Yannis' acceptance of all six decisions and one concrete fixture before this draft is cut. +- **ORACLE GAP — selected complete model:** the consumer question must expose what completeness and credibility mean for this SDCPN; name the exact human or executable oracle only after that question exists. +- **ORACLE GAP — outer handoff:** the accepted contract must name the witnessed action and observation that distinguish “can begin the experiment” from receipt of an unusable artifact. + +## Verification approach + +- **Before consumer acceptance:** inspect the existing terrain with Chris and Yannis, obtain the six decisions and one fixture, and stop. No package, execution, result, or UI implementation verification is earned. +- **At mission cut:** derive inner artifact checks, middle handoff/execution checks, and outer consumer witness from the accepted contract. Bind every final leaf to an exact command, fixture, artifact inspection, or named witness; do not inherit the generic possibilities in this draft as requirements. +- **Real-path provenance:** whatever boundary is accepted must demonstrate that Mission 11 broadened the proven Missions 7, 9, and 10 path across the accepted scenario and that the handed-off artifacts were not hand-curated substitutes. + +## Inputs and joins + +- Selected upstream source: frozen workpiece, exact source Flue conversation, instrument manifest, and adjudication chosen by Mission 7 or a predecessor addendum; Mission 4 itself closed without a full-run candidate. +- Missions 7, 9, and 10: accepted bounded workpiece/net/evidence/derivation chain, revision and patch-locality evidence, and the witnessed real path Mission 11 must broaden. +- Mission 8 actual contract: locally verified application artifact plus still-open infrastructure handoff; no remote deployment is assumed. +- Chris/Yannis: written acceptance of the six consumer decisions and one fixture. +- Petrinaut: current serialization, scenario, optimization, and host capabilities are inspected as existing terrain and used only where the consumer contract accepts them. + +## Risks and assumptions + +- **ASSUMPTION:** one selected complete SDCPN can support one useful agreed experiment. **Impact if false:** the mission's visible floor or scenario portfolio changes. **Cheapest validation:** obtain the concrete question and ask the consumers what minimum model meaning it requires. +- **RISK:** Mission 10's bounded result is mistaken for the complete selected model. **Impact:** required scenario breadth is skipped or silently hand-built at handoff. **Cheapest validation:** after consumer acceptance, inventory the accepted scenario against the bounded region and name only the missing projection/revision breadth Mission 11 must traverse. +- **RISK:** package design precedes consumer acceptance. **Impact:** speculative topology becomes accidental infrastructure. **Cheapest validation:** make the six decisions and fixture the hard re-entry gate. +- **RISK:** “complete” degrades into non-empty or parser-valid. **Impact:** the experiment may be meaningless. **Cheapest validation:** derive completeness and credibility from the accepted question before cutting the mission. +- **RISK:** rehearsal substitutes curated artifacts for the real path. **Impact:** it proves staging rather than the product. **Cheapest validation:** inspect stable artifact identities and derivations across the witnessed six beats. +- **RISK:** local Mission 8 evidence is mistaken for deployment. **Impact:** the outer handoff cannot honestly run. **Cheapest validation:** inspect the remote proof matrix and owner acceptance before scheduling the witness. + +## Accepted constraints and guarded invariants + +- **STOP-THE-LINE — consumer acceptance:** do not choose or implement package topology, export UI, execution/result contracts, or touched paths before the six decisions and fixture are accepted. +- **STOP-THE-LINE — real-path origin:** the handoff must contain outputs of the witnessed deployed review/revise path. +- **STOP-THE-LINE — earned broadening:** complete the accepted scenario by extending the proven traceable path; do not substitute a separately prepared full model. +- **STOP-THE-LINE — semantic credibility:** non-empty and parser-valid are not synonyms for complete or optimisation-ready. +- Preserve the known semantic artifact floor and honest assumptions, omissions, losses, and limits. +- Use Petrinaut's current contracts only where accepted; do not copy their shapes into Brunch or couple directly to an optimizer speculatively. +- Keep Brunch as a second assistant with separate stock behavior and histories. +- Do not represent Mission 8's local application evidence as remote deployment. +- Do not invent a generic export platform, graph database, ontology, regeneration engine, TUI, or generalized runtime. + +## Cross-cutting obligations + +- Workpiece sufficiency, projection fidelity, provenance, revision integrity, and patch locality remain inspectable without transcript archaeology. +- Canonical Petrinaut acceptance and consumer-specific credibility remain separate claims. +- Failures and unsupported limits remain visible at whatever boundary the consumers accept. +- Any user-facing Petrinaut change later requires same-change user documentation and a prompt to replace stale screenshots. + +## Fog-line + +- All six consumer decisions. +- What makes the selected SDCPN complete for the agreed question. +- Whether current Petrinaut serialization, scenario, optimization, or host contracts are accepted, revised, or irrelevant. +- Package representation, export/transfer interaction, access and retention. +- Execution boundary, expected result, result correlation, repeatability, and credibility checks. +- The deployment/access threshold required by the consumers. +- Exact owning repositories and touched paths. + +## Stop or reorient + +- Stop until Chris and Yannis accept the concrete consumer contract and fixture. +- Stop if work starts by selecting an export bundle, manifest, UI, execution API, result type, touched paths, access/retention contract, or verification topology from current source rather than consumer acceptance. +- Stop if rehearsal becomes the sole output or curated staging replaces real-path artifacts. +- Stop if Mission 10's bounded region is relabelled complete without consumer-defined breadth, or if missing breadth is filled outside the proven projection/revision path. +- Stop if the selected net is called complete because it is non-empty, renders, or parses. +- Stop if consumers must reconstruct intent, evidence, assumptions, or derivation from the original transcript. +- Stop if a defect in Missions 7, 9, and 10 is scripted around rather than returned to its owner. +- Stop if current Petrinaut contracts are copied into Brunch or direct optimizer coupling is introduced speculatively. +- Stop if local application evidence is called deployment or the chosen handoff bypasses applicable identity, durability, telemetry, access, and recovery gates. + +## Carried evidence and rejected alternatives + +- The six FE-1476 beats remain the minimum integrated rehearsal and provenance witness, not the product ceiling. +- The broader scenario portfolio remains unenumerated; only the accepted experiment can name the relevant scenario and contract classes. +- The known semantic artifact floor is ratified and retained. Its package representation is not. +- Mission 11 owns broadening the bounded path to the consumer-defined complete scenario; this ownership does not select an export, UI, API, optimizer, execution, or repository topology. +- Existing Petrinaut serialization/optimization capabilities are terrain evidence, not a selected boundary. +- Invented export topology, execution/result contract, touched-path manifest, generic optimizer integration, rehearsal-only completion, transcript handoff, parser-only acceptance, every-fact completion, and remote-deployment assumption remain rejected until consumer acceptance supplies contrary evidence. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/5-direct-voice-flue-transport.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/5-direct-voice-flue-transport.md new file mode 100644 index 00000000000..35e5ba40d2c --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/5-direct-voice-flue-transport.md @@ -0,0 +1,129 @@ +# Draft Mission 5 — Direct Voice over canonical Flue transport + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1574](https://linear.app/hash/issue/FE-1574/let-voice-speak-through-canonical-brunch-conversations) — tracker projection for this future branch mission; the eventual branch `MISSION.md` remains execution authority. +- [`../../MISSION.md`](../../MISSION.md) — Mission 4 closure pointer; no live Brunch mission exists. +- [`../../MISSION.next.md`](../../MISSION.next.md) — shared contracts, parallel-track rules, and current sequencing. +- [`../evidence/implementations/mission-4-voice-integration-handoff.md`](../evidence/implementations/mission-4-voice-integration-handoff.md) — observed Voice stack, conflict surfaces, and the package-composition invariant. +- [`../../packages/core/src/flue.ts`](../../packages/core/src/flue.ts) and [`../../../../../apps/brunch-agent/src/app.ts`](../../../../../apps/brunch-agent/src/app.ts) — accepted `useBrunchAgent()` + `useSdcpnPlugin()` agent composition and mounted Flue route. +- [`../../../../../apps/brunch-agent/src/http/petrinaut-chat.ts`](../../../../../apps/brunch-agent/src/http/petrinaut-chat.ts), [`../../../../../apps/brunch-agent/src/conversation/ui-stream.ts`](../../../../../apps/brunch-agent/src/conversation/ui-stream.ts), and [`../../packages/transport-aisdk/src/index.ts`](../../packages/transport-aisdk/src/index.ts) — current AI SDK adapter and Flue-to-UI projection; these are terrain, not a required Voice path. +- Flue [`FlueClient`](https://flueframework.com/docs/sdk/flue-client/) and React client documentation — supported `send`, `read`, `observe`, `history`, `abort`, offsets, submission correlation, and conversation incarnation semantics. +- Voice PRs #9496, #9507, and #9512 at their current accepted tips; do not reconstruct their behavior from this draft. + +## Visible product advance + +A person speaks one finalized answer in the Voice surface and hears the canonical Brunch reply begin through TTS while the same answer and reply appear exactly once in the owning Flue conversation. Voice connects through Flue's supported conversation protocol rather than submitting through the Petrinaut AI SDK chat composer, and no secondary model rewrites Brunch's reply before speech. + +The existing Petrinaut AI assistant may remain temporarily present during integration, but it is not the conversation authority or required transport for this proof. Removal of obsolete assistant UI and deletion of `transport-aisdk` are consequences only after dependency inspection proves they have no surviving consumer. + +## Contract stratum + +Close the **one-turn direct Voice/Flue transport stratum** for finalized input, streamed canonical output, cancellation, and conversation resumption. + +The accepted objects are one stable logical conversation id, one admitted submission id, one finalized user message, one canonical assistant response, and one Voice playback lifecycle. `conversationId` is the durable logical reference; `submissionId` correlates one admitted turn and supports reattachment; stream offsets are opaque Flue cursors; `uid` identifies one current Flue incarnation and must not become the durable demo/session id. + +## Boundary crossings and current throughline hypothesis + +```text +microphone → provisional STT (ephemeral) +→ one finalized transcript +→ supported FlueClient send to the owning Brunch conversation +→ accepted useBrunchAgent() + useSdcpnPlugin() composition +→ canonical Flue response chunks and settlement +→ exact canonical text projected to Voice +→ ordinary TTS playback +→ history/observe rehydration of the same conversation after reopen +``` + +Authentication, principal ownership, CORS or a same-origin protocol-preserving proxy remain host obligations. “Direct Flue” means use of `@flue/sdk`/`@flue/react`, not handwritten SSE parsing or an unauthenticated public agent route. + +## Throughline proof floor + +From the real Voice surface, one finalized spoken answer produces exactly one visible user message in canonical Flue history; one canonical Brunch response streams to both visible text and TTS without a generative simplification pass; interruption stops local playback and the selected durable abort action has its documented effect; reopening the conversation reconstructs the same settled turn without duplicate submission or playback. + +The retained proof artifact is the canonical Flue snapshot plus the Voice event ledger for STT finalization, admission/submission id, text/TTS projection, cancellation, settlement, and reopen. This does not prove client-side Petrinaut mutations, workpiece viability, broad Voice UX, remote deployment, or that the AI SDK adapter is removable. + +## Readiness ratchet + +### Inherited stratum closure + +- Preserve Mission 4 package composition; never restore the deleted app-local stub `ChatAgent` to resolve Voice conflicts. +- Preserve canonical Flue history as the sole conversation authority and exact finalized-answer correlation from the Voice work. +- Preserve principal/ownership semantics even if the AI SDK adapter is bypassed. + +### Readiness gate after the new throughline + +Before this one-turn capability is accepted, close provisional-versus-final transcript deduplication, submission correlation, replay/reopen behavior, TTS cancellation, durable abort races, visible failed/aborted settlement, authentication/origin handling at the claimed host boundary, and exact canonical spoken/visible correspondence. Carry only broader speech ergonomics, multi-turn barge-in tuning, and obsolete-adapter/UI deletion, each after observed strain or dependency proof. + +## Candidate evidence and oracles + +| Claim leaf | Candidate oracle | +| --- | --- | +| Finalized speech enters one canonical conversation once | Snapshot inspection shows one user message with the expected text and one admission/submission id; provisional STT never appears in history. | +| Voice bypasses AI SDK UI-message transport | Network/source inspection shows the supported Flue conversation protocol and no Voice request to the AI SDK chat route. | +| Spoken output is canonical | Captured TTS input equals the canonical response text selected by the documented deterministic policy; no secondary generation call occurs. | +| Cancellation and abort remain distinct | Voice event ledger plus Flue settlement/history distinguish local playback cancellation, local observation cancellation, and durable conversation abort. | +| Reopen resumes rather than duplicates | A second surface rehydrates the same conversation and settled submission from `history()`/`observe()` without a new user message or automatic replay. | +| Accepted agent architecture survives reconciliation | Composition/dependency test and code inspection retain `useBrunchAgent()` + `useSdcpnPlugin()` and exclude the older stub agent. | + +## Verification approach + +- **Inner:** deterministic tests for finalized-transcript deduplication, canonical text selection, TTS cancellation, submission correlation, and rehydration. +- **Middle:** run the real Brunch agent behind Flue and drive one Voice turn through `send` plus `observe` or `read`, retaining canonical history and the Voice ledger. +- **Outer:** a human speaks, hears the response begin, interrupts once, reopens the same conversation, and confirms visible/spoken/history agreement. Browser-only mocks or a server-only Flue call do not establish the Voice advance. + +## Inputs and joins + +- This mission may cut from Mission 4 independently of the fixture/workpiece mission; neither mission is a prerequisite for the other's first tracer. +- The Voice branch supplies STT, TTS, playback, and answer-correlation behavior. Mission 4 supplies the current Brunch composition and canonical conversation runtime. +- A later integration mission may reuse this direct client to service Petrinaut client tools, but this mission does not need tool mutation to prove transport. + +## Risks and assumptions + +- If canonical Mission 4 replies remain too long for speech, first try deterministic question-focused presentation or Brunch-owned spoken-mode instruction; re-admit secondary generative preparation only after measured failure and with visible canonical/spoken distinction. +- If browser-to-Flue auth/CORS cannot be made safe directly, use the thinnest same-origin proxy that preserves Flue semantics rather than translating into AI SDK messages. +- If `transport-aisdk` or the existing assistant UI has another live consumer, retain it; this mission establishes that Voice does not require it, not that the repository does not. + +## Accepted constraints and guarded invariants + +- One canonical Flue history; no Voice-side transcript authority. +- One finalized answer submission; provisional speech remains ephemeral. +- No lossy generative simplification in the tracer. +- Voice owns audio interaction and playback; Brunch owns canonical response content. +- Use supported Flue client APIs; do not hand-roll offset, retry, or stream reduction. +- Preserve ownership/authentication and make failures visible. +- Do not restore the old app-local Brunch agent or splice Voice into the stock assistant's history. + +## Cross-cutting obligations + +Record latency to admission, first canonical text, first audio, and settlement; distinguish local cancellation from durable abort; keep content out of ordinary telemetry; and update Voice/Petrinaut user documentation if the visible interaction or assistant surface changes. + +## Expected touched paths + +```text +Voice-stack application paths ~ direct Flue client host, STT/TTS projection, cancellation +apps/brunch-agent/src/app.ts ? protocol/auth mounting only if the existing route is insufficient +apps/brunch-agent/src/agents/chat-agent/ ~ preserve current package composition during reconciliation +apps/brunch-agent/src/conversation/ ? only shared identity/tool-result mechanics actually reused +libs/@hashintel/brunch-agent/packages/transport-aisdk/ ? retain or remove only after consumer inspection +libs/@hashintel/petrinaut/src/ui/ ? remove/replace obsolete assistant surface only if separately admitted +``` + +## Fog-line + +- The exact Voice-stack source after its PRs settle and the selected integration order. +- Whether `FlueClient.read` or a maintained `observe({ live: "sse" })` store best fits the existing Voice state machine. +- The authenticated production URL/proxy and origin policy. +- The deterministic policy for which canonical text is spoken if a response contains multiple text blocks or interactive tool parts. +- Whether any non-Voice consumer still needs `transport-aisdk` or the current assistant UI. + +## Stop or reorient + +Stop if integration creates a second conversation authority, submits provisional STT, rewrites canonical output through another model without an observed need, restores the old stub agent, exposes an unauthenticated Flue route, hand-rolls stream recovery, or claims adapter/UI removal before consumer inspection. Stop at a crisp host/auth blocker rather than rebuilding the AI SDK adapter under a new name. + +## Carried evidence and rejected alternatives + +The existing AI SDK transport remains a valid adapter for an AI SDK chat consumer; it is rejected only as an inherent Voice dependency. The older Voice simplifier was a workaround for pre-Mission-4 response shape, not permanent authority. Direct Flue preserves durable submission correlation, history, observation, and abort semantics while avoiding a second projection protocol. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md new file mode 100644 index 00000000000..26a3163fc76 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/6-resumable-workpiece-petrinaut-fixture.md @@ -0,0 +1,164 @@ +# Draft Mission 6 — Resumable workpiece-to-Petrinaut fixture tracer + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1575](https://linear.app/hash/issue/FE-1575/resume-one-brunch-workpiece-and-petrinaut-document-across-tabs) — tracker projection for this future branch mission; the eventual branch `MISSION.md` remains execution authority. +- [`../../MISSION.md`](../../MISSION.md) — Mission 4 closure pointer and explicit absence of a full-run candidate. +- [`../../MISSION.next.md`](../../MISSION.next.md) — shared workpiece, projection, evidence, and product constraints. +- [`../mission-archive/3-structurally-typed-runbook-to-headless-pn.md`](../mission-archive/3-structurally-typed-runbook-to-headless-pn.md) and [`../evidence/implementations/fe-1525-headless-runbook-pn.md`](../evidence/implementations/fe-1525-headless-runbook-pn.md) — accepted workpiece viability, hermetic callback route, and failed real-model nested-schema carrier. +- [`../mission-archive/4-core-plugin-elicitation-proof-of-life.md`](../mission-archive/4-core-plugin-elicitation-proof-of-life.md) — accepted core/plugin architecture and exact proof exclusions. +- [`../../packages/plugin-sdcpn/src/flue.ts`](../../packages/plugin-sdcpn/src/flue.ts), [`../../packages/plugin-sdcpn/src/tools/petrinaut-construction.ts`](../../packages/plugin-sdcpn/src/tools/petrinaut-construction.ts), and Petrinaut's canonical AI/action schemas — current read and bounded construction capabilities. +- [`../../../../../apps/brunch-agent/src/conversation/client-tools.ts`](../../../../../apps/brunch-agent/src/conversation/client-tools.ts), [`../../../../../apps/brunch-agent/src/conversation/ui-stream.ts`](../../../../../apps/brunch-agent/src/conversation/ui-stream.ts), and the current browser host — existing client-tool carriage and result correlation; reuse semantics without requiring the old chat UI. +- Flue `FlueClient` documentation for stable conversation history, observation, submission reattachment, and tool-part materialization. + +## Visible product advance + +**Release note:** Brunch edits the Petrinaut net you are looking at from the conversation, and your work survives closing the tab. + +**Demo script (no engineer present):** open the stable demo fixture; the canonical Brunch conversation, current Markdown workpiece, and associated Petrinaut document come back together. Tell Brunch one new realistic thing about the process. Watch the workpiece update and a meaningful change appear in the live net. Save. Open the same fixture in a second tab and continue the conversation from the saved state. + +**Previously impossible:** Brunch only produced off-canvas net JSON for manual load; nothing it did touched the live document or survived a reload. + +The fixture may be deliberately prepared. It need not be a complete persona-produced Mission 4 artifact, a promoted quality baseline, or proof of comprehensive provenance. Prepared status, authored material, limitations, and any model-produced updates remain explicit, and the demo script says so out loud. + +**Completion:** the mission is done when a product manager can run the demo script end to end at the readiness gate below, not when the first browser mutation lands. + +## Contract stratum + +Close the **single-fixture viability stratum** for these two transformations and their resumable product boundary: + +```text +canonical conversation evidence → maintained Markdown workpiece +maintained Markdown workpiece → meaningful Petrinaut read/write change +``` + +The fixture has separate stable identities for the demo case, conversation, current workpiece revision, and Petrinaut document revision. One id must not impersonate all four lifecycles. A small manifest records their relationships, exact prepared inputs, current coherent bundle revision, and hashes or revision tokens needed to detect stale state. + +The minimum fixture contains one process spine, one shared or constrained resource, one decision/policy, one contextual quantity, one explicit unknown, and enough meaning to change a small non-empty net region. It does not require a comprehensive typed domain IR, assertion-card ontology, graph database, or full provenance ledger. + +## Boundary crossings and current throughline hypothesis + +```text +stable demo fixture id +→ resolve conversation id + workpiece revision + Petrinaut document id/revision +→ hydrate canonical Flue history and current Markdown workpiece +→ one realistic evidence turn updates the workpiece at an explicit phase boundary +→ Brunch reads current Petrinaut state through a browser-owned client tool +→ SDCPN skill interprets the current workpiece, not the transcript as projection IR +→ Brunch requests the least canonical Petrinaut mutation(s) +→ browser validates and executes against the bound document +→ correlated client-tool result resumes the same Flue conversation +→ verify meaningful non-empty state +→ publish a new coherent fixture revision only after workpiece and document saves succeed +→ second tab resolves and resumes that revision +``` + +A partial save remains visible and does not advance the fixture's current coherent revision. The first tracer does not require simultaneous multi-tab collaboration or a distributed transaction service. + +## Throughline proof floor + +For one deliberately prepared fixture: + +1. a cold reader can reconstruct the selected operational spine and distinguish supplied evidence, agent inference/assumption, and the explicit unknown in the current Markdown workpiece; +2. one new realistic conversation turn produces an inspectable workpiece revision without erasing the unknown or unsupported meaning; +3. through the real browser client-tool boundary, Brunch reads the associated Petrinaut document and applies one meaningful supported change derived from the current workpiece; +4. canonical Petrinaut state is non-empty and visibly corresponds to the selected meaning; and +5. after save, a second tab opens the same fixture id, observes the same settled conversation/workpiece/document revision, and successfully continues or reads it without duplicate submission or identity drift. + +One pass through those five steps is the first internal milestone, not mission completion. The retained oracles are the stable demo URL or fixture selector plus the before/after fixture manifest, exact Flue snapshot, Markdown workpiece revisions, and canonical Petrinaut document revisions; they are evidence for the builder, not the visible advance. This proves viability, not automatic full-net projection, selected-pair provenance breadth, remote replacement durability, concurrent editing, or Mission 3/4 quality superiority. + +## Readiness ratchet + +### Inherited stratum closure + +- Consume Mission 4's accepted `useBrunchAgent()` + `useSdcpnPlugin()` architecture and no broader quality claim. +- Consume Petrinaut-owned schemas/mutations mechanically; parser acceptance alone remains vacuous. +- Treat Flue history as canonical and client-tool results as correlated execution evidence. +- Preserve the Mission 3 failure: provider-visible nested mutation shapes are unproved and may force a smaller first mutation or a crisp upstream blocker. + +### Readiness gate after the new throughline + +This gate is the mission's completion bar: the demo script above must work for the named fixture. Before accepting this single-fixture capability, close stale fixture/workpiece/document revision refusal, duplicate tool delivery, read/write failure visibility, unsupported meaning, no-op mutation honesty, partial-save behavior, second-tab rehydration, separate identity integrity, and one negative mutation case. Do not close every consequential-element provenance link, remote task replacement, broad scenario coverage, or repeated automatic projection here; those become Mission 7 or Mission 9 obligations only after this tracer exposes a finite peer set and load-bearing seams. + +## Candidate evidence and oracles + +| Claim leaf | Candidate oracle | +| --- | --- | +| Prepared fixture is honest and minimally sufficient | Frozen manifest plus cold-reader adjudication identifies the spine, resource, policy, quantity, unknown, authorship, and preparation route. | +| Conversation evidence can maintain Markdown | Before/after workpiece inspection against the exact Flue snapshot detects invention, hardening, lost prior meaning, and lost unknowns. | +| Browser executes real Petrinaut read/write tools | Production-boundary integration records tool call ids, canonical parsed inputs, execution outcomes, correlated result signals, and current document state. | +| Change is meaningful | Human comparison binds one workpiece meaning to a visible canonical type/parameter/place/transition/arc change appropriate to the fixture; non-empty/parser-valid alone fails. | +| Save is coherent | Injected workpiece-save or document-save failure leaves the prior current bundle revision selected and exposes the partial result for recovery. | +| Second tab resumes stable state | Open the same stable fixture selector after save and compare conversation id/history, workpiece revision/hash, document id/revision, and canonical definition before continuing. | +| No typed domain IR was smuggled in | Public-schema and dependency inspection finds only fixture identity/revision links and canonical Petrinaut payloads, not a closed process ontology or typed capture-to-workpiece model. | + +## Verification approach + +- **Inner:** fixture-manifest parse/version/stale checks, explicit identity separation, coherent-revision publication, idempotent client-tool result handling, and canonical Petrinaut mutation tests. +- **Middle:** drive the production Brunch agent through Flue, update the Markdown artifact, execute actual browser callbacks against the fixture-bound Petrinaut instance, and retain before/after artifacts plus one injected failure. +- **Outer:** from the real demo route or equivalent product selector, perform the update/save in Tab A and reopen/continue from the same fixture in Tab B. A headless callback alone does not establish this mission. +- **Semantic:** a cold human judges whether the workpiece remained honest and the changed net region corresponds to it. + +## Inputs and joins + +- This mission may cut directly from Mission 4 and run independently of the direct Voice mission. Typed text is sufficient for its first tracer; Voice can later become another input modality to the same canonical conversation. +- An owner selects one deliberately prepared fixture and records its non-claims. No full Mission 4 candidate or new persona campaign is prerequisite. +- Mission 7 inherits this fixture only if it needs to close capture-backed why/provenance breadth. Mission 9 inherits the browser mutation and semantic-projection seam only if the tracer proves them viable. +- Mission 8's local deployment artifact is terrain, not a prerequisite for a local two-tab viability proof and not evidence of remote durability. + +## Risks and assumptions + +- If a realistic prepared conversation/workpiece cannot support one meaningful mutation without richer typed structure, record the exact lookup, identity, or ambiguity strain before adding any schema. +- If existing per-action provider schemas cannot carry the required nested mutation, reduce to the smallest meaningful supported action only if semantic correspondence survives; otherwise stop with the crisp provider/Flue schema blocker. +- If coherent save cannot span existing workpiece/document stores, the least fixture-scoped commit marker may publish only after both writes; do not invent distributed transactions before a failure demonstrates the need. +- If direct browser tool servicing needs a transport abstraction, extract only tool-call/result correlation from the existing AI SDK adapter; do not require the chat UI or duplicate Flue observation. + +## Accepted constraints and guarded invariants + +- Separate demo, conversation, workpiece, and document identities with explicit links. +- One canonical Flue history; fixture log projections do not become another authority. +- Markdown is the semantic workpiece; no comprehensive typed domain IR. +- Projection consumes the current workpiece, not the transcript as primary IR. +- Petrinaut owns canonical schemas and mutation execution. +- Client tools execute in the browser against the bound document and return the original tool call id. +- Current coherent revision advances only after all required saves succeed. +- Prepared fixture status and unsupported meaning remain visible. +- No claim of remote replacement durability, automatic full projection, provenance breadth, or concurrent collaboration. + +## Cross-cutting obligations + +Preserve exact evidence attribution, visible failure, stable identity, workpiece sufficiency, semantic correspondence, stock-assistant isolation if it remains present, and same-change user documentation for the demo selector/save/resume behavior. + +## Expected touched paths + +```text +libs/@hashintel/brunch-agent/ +├── evaluations or docs/evidence fixture area + one prepared fixture and adjudication +├── packages/core/ ? only minimal workpiece/fixture contracts with a real second consumer +└── packages/plugin-sdcpn/ ~ least read/write capability and guidance needed by the tracer +apps/brunch-agent/ +├── src/agents/chat-agent/ ~ mount accepted fixture read/write path +├── src/conversation/ ~ client-tool correlation without UI-message authority +└── test/ + real Flue/browser-tool integration +libs/@hashintel/petrinaut-core/ ~ canonical contracts only if a source defect is found +libs/@hashintel/petrinaut/src/ ~ stable fixture resolution, save/resume, browser tool execution +``` + +## Fog-line + +- The exact prepared scenario and smallest meaningful document mutation. +- Where the fixture manifest and current coherent-revision marker belong. +- The current Petrinaut document persistence/revision API and whether the demo route already has a stable selector. +- Whether workpiece Markdown is a file, Flue data part, or product document for this tracer; choose the least real persistence boundary that supports two-tab reopen. +- The least browser host for direct Flue client-tool servicing after removal or bypass of the old assistant UI. +- The exact negative save/mutation case and acceptable local-only durability claim. + +## Stop or reorient + +Stop if the tracer requires pretending a Mission 4 candidate exists, makes a prepared fixture look model-produced, conflates all ids, treats parser validity as meaning, submits client-tool results without original correlation, advances the current bundle after a partial save, uses transcript text as the projection IR, or introduces a closed domain ontology before observed strain. Stop at provider-schema or host-persistence blockers rather than widening into Mission 7/9 readiness work. + +## Carried evidence and rejected alternatives + +Mission 3 showed that a Markdown workpiece can be useful and that a hermetic callback can build canonical non-empty state, while falsifying the exercised real-model nested-schema carrier. Mission 4 established capability composition but produced no full-run candidate. This mission intentionally joins those facts with a prepared fixture to test viability before requiring promoted-source quality, complete provenance, or a generalized semantic model. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/7-capture-backed-review.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/7-capture-backed-review.md new file mode 100644 index 00000000000..8d930f60581 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/7-capture-backed-review.md @@ -0,0 +1,294 @@ +# Draft Mission 7 — Capture-backed review of an honest prebuilt pair + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1573](https://linear.app/hash/issue/FE-1573/explain-one-prepared-petrinaut-net-from-exact-conversation-evidence) — tracker projection for this future branch mission; it advances stakeholder outcome [FE-1478](https://linear.app/hash/issue/FE-1478/provide-provenance-from-a-generated-net-back-to-the-requirements-graph) without rewriting that record. + +A fresh builder must resolve the current repository and the deployment handoff rather than treating this draft as a specification: + +- [`../../MISSION.md`](../../MISSION.md) — closure pointer; Mission 4 produced no full-run conversation/workpiece candidate and this draft must not imply otherwise. +- [`6-resumable-workpiece-petrinaut-fixture.md`](6-resumable-workpiece-petrinaut-fixture.md) — independent viability predecessor. If accepted, its deliberately prepared and honestly labelled fixture may become this mission's selected pair after a separate provenance-suitability decision; a complete persona workpiece is not intrinsically required. +- [`../../MISSION.next.md`](../../MISSION.next.md) — compact future spine, FE-1476 product frame, shared proof obligations, standing locks, and any later evidence admitted after this draft was written. +- [`../mission-archive/2-mechanical-capture-sweep.md`](../mission-archive/2-mechanical-capture-sweep.md) — accepted mechanical capture throughline, exact close evidence, empty-payload boundary, conversation identity, and carried flags. +- [`../mission-archive/3-structurally-typed-runbook-to-headless-pn.md`](../mission-archive/3-structurally-typed-runbook-to-headless-pn.md) and [`../evidence/implementations/fe-1525-headless-runbook-pn.md`](../evidence/implementations/fe-1525-headless-runbook-pn.md) — accepted runbook/workpiece leg, falsified real-model construction leg, and the distinction between a hermetic non-empty fixture and vacuous empty-net parser success. +- [`../evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md`](../evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md) — frozen Mission 3 control and cold-reader evidence; do not substitute current source for its instrument revision. +- [`apps/brunch-agent/test/petrinaut-chat.test.ts`](../../../../../apps/brunch-agent/test/petrinaut-chat.test.ts), [`apps/brunch-agent/test/petrinaut-chat.integration.ts`](../../../../../apps/brunch-agent/test/petrinaut-chat.integration.ts), and [`apps/brunch-agent/src/capture/apply-sweep.ts`](../../../../../apps/brunch-agent/src/capture/apply-sweep.ts) — current production chat door and explicit harness-owned sweep. +- [`../../packages/core/src/evidence/capture-store.ts`](../../packages/core/src/evidence/capture-store.ts), [`../../packages/core/test/capture-store.test.ts`](../../packages/core/test/capture-store.test.ts), [`../../packages/binding-flue/src/local-capture-store.ts`](../../packages/binding-flue/src/local-capture-store.ts), and [`../../packages/binding-flue/test/local-capture-store.test.ts`](../../packages/binding-flue/test/local-capture-store.test.ts) — capture envelope, archived evidence, ownership, atomicity, parse, and local durability contracts. Their richer historical types are not permission to expose a typed capture ontology in this mission. +- [`apps/brunch-agent/src/http/petrinaut-chat.ts`](../../../../../apps/brunch-agent/src/http/petrinaut-chat.ts), [`apps/brunch-agent/src/conversation/client-tools.ts`](../../../../../apps/brunch-agent/src/conversation/client-tools.ts), and [`../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx`](../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx) — AI SDK transport, Flue client-tool suspension/resume, and the existing `useChat` / `onToolCall` browser execution boundary. +- Commit `157730cc5a214dd9c543e8d95c7193a219c48aef` on deployment branch `ln/fe-1569-brunch-agent-deployment`, read with `git show`, especially `libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md`, plus its `MISSION.md`, `MISSION.next.md`, Mission 4 archive, and archive README. That commit proves a local application artifact and stops at the application-to-infrastructure handoff; it does not prove remote deployment. +- On that branch, inspect `apps/brunch-agent/src/db.ts`, `src/postgres.ts`, `src/database-config.ts`, `test/postgres.test.ts`, `test/database-config.test.ts`, and `test/container-smoke.integration.ts`. Mission 7 activates capture as product data and therefore must use a durable implementation at the claimed replacement boundary; task-local JSON is not an admissible deployed capture store. +- [`../../../petrinaut/docs/ai-assistant.md`](../../../petrinaut/docs/ai-assistant.md) and the other Petrinaut user-guide pages that mention the affected panel flow. Any user-visible change requires same-change documentation and a prompt to replace screenshots if they become stale. + +Mission 4 closed without admitting its full run and therefore retained no source conversation/workpiece handoff candidate. Mission 6 may establish an honestly prepared conversation/workpiece/document fixture without changing that fact. Mission 7 must select either that accepted prepared fixture, another explicitly eligible retained source, or a newly authorized run, then record why it is sufficient for the narrower capture-backed provenance claim. Selection is not retrospective persona acceptance or a general quality claim. + +## Visible product advance + +**Release note:** ask Brunch why any element in the demo net exists and get back what the expert actually said. + +**Demo script (no engineer present):** open the honestly prebuilt demo net in the Petrinaut Brunch panel, on the deployment posture named at cut time (a locally run panel is acceptable; remote durability stays in the readiness gate). Pick any consequential element and type its visible name or id. Read the current workpiece passage that supports it, the prebuilder's rationale including any assumption, default, omission, or loss, and the exact quoted excerpts from the source conversation. Pick the element the demo marks as unsupported and watch Brunch decline rather than improvise. + +**Previously impossible:** nothing connected a net element to the conversation evidence behind it; a reviewer had to trust the modeller. + +Each answer contains: + +1. the current selected Markdown workpiece passage that supports the modelled meaning; +2. the prebuilder's explicit projection rationale, including consequential assumption, uncertainty, omission, default, or representational loss; and +3. exact mechanically retained excerpts from the source Flue conversation. + +The response visibly identifies both the workpiece and SDCPN as prebuilt. It makes no claim that Brunch automatically projected the net, inferred a complete provenance graph, or observed and consolidated the conversation in the background. A deliberately broken provenance link returns a visible unsupported/unavailable result rather than a plausible reconstructed explanation. + +This is FE-1476 beats 1–3 over one honest pair. The six-beat story is the integrated floor, not the product or demo ceiling; the broader scenario portfolio remains unenumerated and must be named when this draft is cut. + +Typing a visible element name or id is the accepted first interaction. Click-to-chat and automatic canvas-selection context are deferred unless textual identification proves ambiguous or burdensome in observed review use. + +**Completion:** the mission is done when every consequential element in the demo net either resolves or visibly declines, at the readiness gate below. One resolving element is the throughline tracer inside the mission, not the mission. + +**Scope decision, 2026-09-03:** the earlier one-element cut was judged too small under the product-manager litmus. The owner expanded Mission 7 to the whole demo net rather than consolidating it. Folding it into Mission 6 was rejected because it would load the viability tracer with capture durability and delay it. Folding it into Mission 9 was rejected because it would tie explainability, a stakeholder outcome in its own right (FE-1478), to the unproved provider-schema projection route. Expansion is cheap: capture durability and the why route cost the same for one element or all of them, and the extra work is preparing provenance for the rest of the pair. Reverse toward consolidation with Mission 9 only if preparing whole-net provenance by hand proves to be the dominant cost with no mechanism strain, in which case the why route should ride on Mission 9's generated derivations instead. + +## Contract stratum + +Close the **capture-backed provenance stratum for the selected prebuilt workpiece/SDCPN pair**. + +The accepted objects and minimum seam are: + +- one current Markdown workpiece revision promoted from an explicitly named retained source only after eligibility inspection and owner selection; +- one exact source Flue conversation and explicit settled range; +- immutable mechanical capture envelopes containing exact evidence and source pointers; +- stable references from consequential workpiece passages to capture evidence; +- one honestly prebuilt non-empty SDCPN with stable element ids; +- a minimal derivation fixture from consequential net elements to current workpiece passages, projection rationale, and relevant uncertainty/assumption/loss; and +- one reviewer-facing resolution operation from a typed visible element name/id to that chain. + +This stratum is narrower than automatic projection and broader than one green lookup. Every consequential element in the selected pair must have either resolvable provenance or an explicit unsupported/unlinked disposition before the visible pair can be trusted as capture-backed. The contract also covers duplicate, ambiguous, and stale identity; missing evidence; replay/idempotency; capture durability; cross-owner refusal; visible failure; and recovery at the replacement boundary the deployed product claims. + +Capture remains domain-opaque evidence. The foreground Markdown workpiece owns semantic synthesis. The derivation fixture records what the human or explicitly identified prebuilder decided. No capture payload becomes the canonical workpiece, assertion card, SDCPN proposal, or semantic intermediate representation. + +## Boundary crossings and current throughline hypothesis + +```text +owner-selected eligible retained conversation/workpiece source + → harness explicitly names the settled source range + → Mission 2 mechanical sweep reads Flue history + → durable capture store archives exact user evidence and immutable envelopes + → prepared Markdown workpiece references the relevant capture ids/spans + → identified prebuilder creates a non-empty SDCPN and minimal derivation fixture + → durable product state preserves workpiece, derivation, captures, and owner binding + → deployed Petrinaut reviewer sees the selected prebuilt net + → reviewer types a visible element name or id in the existing AI panel + → AI SDK transport sends the turn to the Brunch Flue ChatAgent + → provenance capability resolves element → derivation → current workpiece → capture archive + → client/server response returns exact excerpts plus attributed rationale + → panel renders an evidence-grounded why answer or visible unsupported/unavailable refusal +``` + +Actor and authority crossings: + +- **Flue conversation → Brunch harness:** history is the canonical conversation log; an explicit harness fact, never the interviewer model, schedules the sweep. +- **Harness → capture store:** only mechanical capture projection and evidence archiving occur. The store validates ownership, source pointers, idempotency, atomicity, and durable format. +- **Capture store → workpiece preparation:** a person or explicit preparation step writes narrow references. There is no automatic capture-to-workpiece reducer. +- **Workpiece → prebuilder:** the prebuilder interprets the selected Markdown meaning and records its rationale. The net is not represented as model-generated. +- **Brunch server → Petrinaut browser:** the Flue agent emits a client-tool request or uses another application-level host extension; the existing `useChat` / `onToolCall` path executes it. Petrinaut library code must not gain Brunch-specific business logic. +- **Element id/name → evidence:** deterministic stored links select the derivation and evidence. The model may explain linked material but may not invent links or substitute a transcript reread. +- **Application → deployment substrate:** active Flue and capture/workpiece/derivation state cross the actual Mission 8 persistence boundary. The application artifact exists on the deployment branch; infrastructure deployment and replacement proof remain open. + +## Throughline proof floor + +At the real product boundary named at cut time: + +1. one consequential visible element in the honestly prebuilt SDCPN resolves from its stable id or unambiguous visible name to the current workpiece passage, prebuilder projection rationale, exact source excerpts, and at least one relevant uncertainty, assumption, omission, default, or loss; and +2. one deliberately broken or stale link visibly returns unsupported/unavailable without fabricated rationale or evidence. + +This floor is the first internal milestone: a real capture-backed why route for one element and its honest negative control. It is not mission completion, which is the readiness gate over every consequential element. It does not close the selected pair's whole provenance stratum, prove automatic projection, establish an observer, type capture semantics, or enumerate the broader scenario portfolio. + +## Readiness ratchet + +```text +Mission 2 mechanical-capture throughline ++ one retained source selected and promoted through an explicit eligibility and identity decision ++ Mission 8 landed application contract and still-open infrastructure handoff +→ inherited capture/workpiece/deployment closure required here +→ one deployed capture-backed why answer plus broken-link refusal +→ readiness gate +├─ close provenance breadth and durability for the selected prebuilt pair before Mission 7 ships +├─ admit the stable workpiece/derivation/element seam into Mission 9 automatic projection +└─ leave typed capture semantics, automatic observation, broad scenario coverage, and autonomous projection unearned +``` + +### Inherited stratum closure + +Mission 7 consumes, but must not overstate: + +- **Mission 2 throughline:** explicit settled Flue history ranges produce idempotent, source-linked envelopes with payload `{}` and no extraction model. Before use as product data, replay must preserve exact source evidence, owner refusal, format validity, and atomic all-or-nothing behavior. +- **Mission 3/4 evidence boundary:** Mission 4 accepted the core/plugin implementation on partial activation/restraint evidence but retained no full-run workpiece candidate. Before preparing the pair, this mission or its predecessor addendum must select a retained source, establish that its workpiece is cold-readable and sufficiently epistemically honest for the named review slice, freeze it with the exact source conversation and instrument, and record every accepted limitation. It may use only the topology-neutral rows made load-bearing by that slice and may not hide a known gap in the derivation. +- **Prebuilt-pair honesty:** all net content used by the visible path must have an identified preparation route, stable ids, and explicit projection rationale. Parser validity alone and the Mission 3 empty paid document are ineligible. +- **Deployment application contract:** the pinned Mission 8 handoff has locally verified fail-closed Postgres Flue storage, TLS, image, health, content-free telemetry, and container smoke, but no remote infrastructure proof. Because this mission activates capture, its durable storage and replacement behavior become application and infrastructure obligations. Its inactive local JSON capture store is not inherited closure. +- **Product door:** panel → AI SDK → Flue `ChatAgent` and client-tool resume already work. The panel remains the real entrypoint and the stock assistant remains independent. + +If any inherited item is unavailable at cut time, name it as open inherited closure with an owner and oracle; do not call the throughline a dependable base. + +### Readiness gate after the new throughline + +This gate is Mission 7's completion bar. Before Mission 7 can ship its selected-pair claim, assess every consequential element in the demo net and close: + +- resolvable current provenance or an explicit unsupported/unlinked disposition; +- duplicate ids/names, ambiguous name lookup, stale workpiece revision, stale derivation, deleted/renamed element, missing capture, and mismatched owner; +- exact-evidence replay, sweep idempotency, capture/workpiece/derivation durability, atomic update/refusal, format/version refusal, and replacement recovery; +- cross-owner read/write refusal and absence of evidence leakage in errors, telemetry, or rendered answers; +- visible negative states for unsupported, unavailable, ambiguous, stale, and temporarily failed resolution; +- deterministic context source: stored current workpiece and derivation, never transcript fallback disguised as provenance; +- stock-assistant coexistence and a Brunch selection/routing posture sufficient for the selected deployed path; +- representative latency, model/tool usage, timeout, and failure behavior without reintroducing minute-scale foreground extraction/fold work; and +- compaction/stale-state risk: either cross a real compaction boundary and preserve the resolution chain, or state and visibly guard the accepted non-compaction limit. + +Mission 9 may inherit the stable seam only after these leaves are enumerable and accepted: current workpiece revision identity, workpiece passage/reference identity, exact capture evidence references, stable net element ids, and projection rationale/derivation identity. **Owner:** Mission 9. **Re-entry gate:** its first automatic projection must write the same seam and Mission 7's why operation must resolve a generated element without fixture-only translation. **Oracle:** Mission 9's deployed generated-element why proof plus repeated/changed projection identity checks. + +Do not carry selected-pair provenance breadth, capture durability, owner refusal, or broken-link visibility into Mission 9: Mission 7's visible claim already depends on them. + +## Candidate evidence and oracles + +| Claim leaf | Existing evidence or candidate oracle | +| --- | --- | +| Explicit harness-owned sweep over real Flue history; no interviewer sweep tool; exact excerpt, `{}` payload, idempotent retry | Existing `apps/brunch-agent/test/petrinaut-chat.test.ts`, test `the committed /api/chat door streams a plain Flue agent through server and client tools`, driven by `apps/brunch-agent/test/petrinaut-chat.integration.ts`. Run `yarn workspace @apps/brunch-agent test:unit`. | +| Capture command closure, source evidence, all-or-nothing refusal, supersession/conflict guards, persisted parse | Existing `libs/@hashintel/brunch-agent/packages/core/test/capture-store.test.ts`, suite `capture-store contract`. Run `yarn workspace @hashintel/brunch-agent test:unit`. These tests are evidence for internal historical mechanics, not permission to expose typed payload semantics. | +| Owner refusal, tmp-and-rename persistence, serialization, invalid-format failure | Existing `libs/@hashintel/brunch-agent/packages/binding-flue/test/local-capture-store.test.ts`, suite `local capture store`. Run `yarn workspace @hashintel/brunch-agent-binding-flue test:unit`. This is local-file evidence only. | +| Panel executes client tools and resumes one turn through AI SDK | Existing `apps/brunch-agent/test/petrinaut-chat.test.ts` plus `libs/@hashintel/petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.test.tsx`, test `adds one dynamic output and sends one automatic follow-up`. Run `yarn workspace @apps/brunch-agent test:unit` and `yarn workspace @hashintel/petrinaut test:unit --run`. | +| Mission 3 workpiece is recoverable and cold-readable; empty paid net is semantically false | Existing `docs/evidence/implementations/fe-1525-headless-runbook-pn.md` and `docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md`; artifact inspection is the oracle. | +| Mission 8 application contract is locally verified but remote replacement remains open | `157730cc5a214dd9c543e8d95c7193a219c48aef:libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md`: exact recorded commands include `yarn workspace @apps/brunch-agent lint:tsc`, `lint:eslint`, `test:unit`, `build`, `build:docker`, and `turbo run test:unit --filter='@hashintel/brunch-agent...'`, plus native arm64, explicit `linux/amd64`, and Docker/Postgres/collector smoke. The handoff explicitly is not remote proof. | +| One retained source is exact, and one workpiece is explicitly promoted as eligible for the selected pair | **ORACLE GAP:** Mission 4 supplied no full-run candidate. Resolve before this draft is cut by naming an eligible retained source or addendum-owned run, selecting the smallest workpiece-eligibility checks required by the visible review slice, recording identity/remapping rules, freezing the promoted input, and obtaining owner selection. Do not imply full topology-neutral or comparative acceptance. | +| Every consequential element in the selected prebuilt pair has a derivation or explicit unsupported disposition | **ORACLE GAP:** no selected pair or inventory exists. Resolve with a frozen element inventory mechanically compared with the derivation fixture and human inspection of every unsupported disposition. | +| One typed name/id yields an evidence-grounded answer and broken link yields visible refusal in deployed panel | **ORACLE GAP:** no existing test or deployed witness exercises this operation. At cut time bind it to the exact production-path test and a human panel witness; do not count a server-only fixture. | +| Capture/workpiece/derivation survive the actual claimed replacement boundary | **ORACLE GAP:** deployment branch stopped before remote task replacement and left capture inactive. Resolve with one immutable image, selected durable store, process restart and cross-host replacement inspection against the same ids. | +| Cross-owner provenance refusal | Existing local capture-store owner refusal is inner evidence only. **ORACLE GAP:** bind an outer deployed second-principal probe to the selected identity/access boundary. | +| Stock assistant works with Brunch unavailable | **ORACLE GAP:** current standing lock is not an observed Mission 7 witness. Resolve with the exact host-mode test and browser witness selected at cut time. | +| Latency, usage, transcript fallback, and stale-state behavior are visible | **ORACLE GAP:** define thresholds only from a representative deployed run and owner acceptance; instrument the selected operation without content export. | + +## Verification approach + +- **Inner mechanism evidence:** run the existing core capture-store, binding local-store, and app sweep tests; add only tests required by the chosen narrow reference and durable-store representation. Mechanism checks must pin exact evidence, immutable captures, stable references, owner refusal, stale/ambiguous resolution, atomic failure, and format refusal. They do not establish the product claim. +- **Middle integration/contract evidence:** drive the production built Brunch application through AI SDK and Flue with the frozen pair, execute the actual provenance capability/client-tool boundary, and verify element → derivation → current workpiece → captures for positive and deliberately broken links. Exercise process restart and the selected durable implementation. Compare every consequential element to the frozen disposition inventory. +- **Outer deployed/user-visible evidence:** a human reviewer opens the selected prebuilt net through the deployed Petrinaut/Brunch boundary, types the visible element name/id, sees the attributed answer and prebuilt label, then exercises a broken link and a cross-owner attempt. Repeat after the claimed task replacement. Explicitly witness that stock mode still works. The mission owns this outer evidence; it may not defer it to Mission 9. +- **Semantic adjudication:** a cold reader checks that each answer distinguishes exact expert excerpts, current workpiece synthesis, and prebuilder rationale/assumption/loss. Plausible prose, source-path display, or link presence without semantic correspondence fails. + +## Inputs and joins + +- **Upstream source join:** Mission 4 supplies activation/restraint evidence and an explicit record that no full-run candidate exists. Mission 7 may consume Mission 6's honestly prepared fixture, name another retained source, or produce one under new authority, but must freeze the exact conversation/workpiece/document inputs and pass the minimum provenance-suitability and identity/remapping contract. The join fails if prepared material is presented as persona/model evidence, a branch-tip workpiece is treated as previously accepted, or a Flue identity is presented as Petrinaut net/user identity. +- **Mission 2 join:** explicit `applyCaptureSweep`-style harness operation, evidence archive, capture idempotency, owner key, and no interviewer scheduling. Reuse mechanics only after inspecting current contracts; do not revive generalized typed elicitation. +- **Prebuilt fixture join:** identified preparer, stable net element ids, current workpiece references, capture evidence references, projection rationale, and explicit unsupported dispositions. The pair must be non-empty and semantically inspectable. +- **Mission 8 join:** consume the landed application contract and the open infrastructure handoff accurately. Mission 7 must add durable capture/product-state scope before claiming replacement durability, then join to actual ECS/RDS/collector/ingress identity supplied by infrastructure. +- **Petrinaut host join:** use the existing generic panel tool-execution surface and canonical net identity. Any new UI or user behavior updates Petrinaut docs in the same change. +- **Mission 9 output join:** hand off the accepted seam and why operation so automatic projection can generate derivations that resolve without a fixture-specific adapter. + +## Risks and assumptions + +| Risk or assumption | Impact if false | Cheapest discriminating validation | +| --- | --- | --- | +| A workpiece promoted from the selected retained source can host stable passage/reference identity without becoming assertion cards | If false, links churn or a second semantic artifact is needed | Prepare references for the consequential prebuilt pair and revise a non-semantic line; observe whether meaning-bearing references remain unambiguous. Stop before choosing a new ontology. | +| Visible element name is unique enough for reviewer input, with id as escape hatch | If false, a name-only why request can resolve the wrong element | Inventory duplicate/renamed names and exercise an ambiguous query that must request/disclose the id rather than guess. | +| A minimal companion derivation fixture is sufficient for why review | If false, rationale or loss cannot be recovered without transcript reread | Cold-read one consequential and one unsupported element using only workpiece, derivation, and captures. | +| Exact excerpts plus workpiece context are enough for a useful why answer | If false, the UI may expose provenance yet fail the review task | Human reviewer judges whether the answer explains the modelling choice and its uncertainty, not merely lists ids or quotes. | +| The Mission 8 Postgres application boundary can durably host or coordinate activated capture/workpiece/derivation state | If false, task replacement breaks provenance or requires a different durable owner | Implement the least candidate behind existing storage boundaries and run process/cross-host replacement; do not infer from Flue-table durability. | +| The selected short path need not cross Flue compaction | If false, workpiece/evidence recovery may fail during the visible review | Measure the selected conversation against compaction behavior; if crossed, make compaction recovery part of this mission. If not, guard and disclose the limit. | +| A model can explain deterministic linked material without inventing provenance | If false, free-form generation can launder unsupported claims | Negative controls remove or stale one link and compare the answer; require structured unavailable state before explanatory prose. | +| One selected prebuilt pair makes the peer set enumerable | If false, stratum closure cannot be distinguished from one tracer | Freeze the pair and inventory all consequential elements before broadening. | + +## Accepted constraints and guarded invariants + +- **One authority:** this file remains non-executable until converted into `MISSION.md`. Guard: exact warning and absence of live mission headings. +- **Flue history is canonical conversation log; capture is not a second transcript.** Guard: sweep reads named Flue entries and stores exact spans/excerpts only. +- **Harness owns sweep scheduling.** The foreground model receives no sweep tool and no scheduling instruction. Guard: existing app throughline test's tool-name assertions plus production manifest inspection. +- **Capture envelopes are immutable, exact-evidence, and domain-opaque.** Guard: capture-store parse/idempotency tests and schema inspection; stop if SDCPN fields enter capture payload contracts. +- **Foreground Markdown workpiece owns semantic synthesis.** Guard: cold-reader inspection and absence of an automatic capture reducer. +- **Prebuilt means prebuilt.** Guard: visible product label and frozen preparation manifest. Stop the line if the pair is described as automatically projected. +- **No observer.** No token threshold, `useAgentFinish` scheduler, asynchronous model fold, assertion consolidation queue, or automatic background update enters this mission. Guard: dependency/tool/state inventory. +- **No typed capture ontology or assertion-card default.** No closed kinds, slots, subject/predicate/value, per-capture SDCPN hints, or typed completion algebra. Guard: public-schema review. +- **No automatic projection.** The net and derivation are fixtures/prepared artifacts; no workpiece-to-mutation engine is mounted. Guard: tool manifest and preparation record. +- **No transcript fallback.** A missing stored link returns unsupported/unavailable. Guard: broken-link negative control. +- **Durability matches the claim.** Task-local JSON cannot support replacement durability. Guard: restart and cross-host replacement with the same ids and owner binding; startup refuses incompatible/missing durable configuration. +- **Ownership fails closed.** Guard: existing local owner refusal plus deployed second-principal probe. +- **Petrinaut remains contract owner and stock assistant remains independent.** Guard: no Brunch-specific logic in the published Petrinaut core, host coexistence test, and stock-mode browser witness. +- **No content-bearing telemetry by default.** Guard: trace/log inspection for excerpts, prompts, tool payloads, credentials, and owner material. + +## Cross-cutting obligations + +- Workpiece sufficiency: a cold reader can reconstruct the relevant objective/process meaning and distinguish evidence, inference, assumption, unknown, conflict, omission, and construction-opened loss. +- Evidence provenance: exact conversation evidence remains attributable; normalized workpiece or model prose is never presented as quotation. +- Projection fidelity for this prebuilt pair: every consequential prebuilt region names the workpiece material and preparer's rationale; no automatic generation claim is made. +- Failure visibility: ambiguity, missing/stale links, owner mismatch, durable-store refusal, and unavailable Brunch visibly stop/degrade the operation without advancing or fabricating state. +- Interaction quality: the why operation remains in reviewer language and does not expose construction schemas or block on semantic extraction/fold work. +- Security/privacy: consume Mission 8's restricted-boundary posture, trusted identity decision, durable state, and content-free operational visibility as actually accepted at cut time. +- Host continuity: preserve `useChat` / `onToolCall`, separate histories, and stock-assistant availability. +- User docs: update affected Petrinaut docs with what the reviewer types, what appears, and failure behavior; prompt for screenshot replacement if the UI changes. +- Architecture docs: if a new Petrinaut/Brunch folder forms a real architectural unit, add the required local declaration and run the Petrinaut architecture-doc lint. + +## Expected touched paths + +Tentative only; re-evaluate after the real boundary survey. + +```text +libs/@hashintel/brunch-agent/ +├── MISSION.md ~ cut-time authority only +├── docs/evidence/ + selected-pair/provenance adjudication +├── packages/core/src/evidence/ ~ only if narrow reference/durable contracts belong in core +├── packages/core/test/ ~ corresponding contract guards +├── packages/binding-flue/src/ ~ durable capture adapter only if this remains the honest binding +└── packages/binding-flue/test/ ~ ownership/recovery/refusal guards + +apps/brunch-agent/ +├── src/capture/ ~ explicit harness sweep and durable store composition +├── src/conversation/ ~ narrow provenance capability/client-tool signal if earned +├── src/agents/chat-agent/ ~ mount only the why capability, never automatic projection +├── src/http/ ~ selected deployed route/identity composition if required +└── test/ ~ production throughline and replacement integration + +libs/@hashintel/petrinaut-core/ ? generic host contract only if an existing neutral extension is insufficient +libs/@hashintel/petrinaut/src/ui/views/Editor/panels/ai-assistant-panel/ ~ generic visible why/failure presentation if required +libs/@hashintel/petrinaut/docs/ ~ affected user-facing guidance +deployment/infrastructure source outside this checkout ? actual durable restricted service resources and remote proof +``` + +Do not create a capture ontology package, observer package, projection engine, graph database, generalized provenance platform, second server, or TUI. + +## Fog-line + +- Which retained source or addendum-owned run is eligible for promotion, which limitations are accepted for the selected review slice, and whether stable references can be embedded in Markdown without changing its semantics. +- The exact source conversation/range and capture replay needed for the pair. +- The smallest honest derivation representation and whether it travels with the workpiece, net, or as a companion manifest. +- The consequential-element inventory and what counts as consequential for the selected pair. +- Whether visible names are unique, ids are discoverable, or the existing panel needs a generic element-reference affordance. +- The exact durable owner for capture, workpiece, and derivation data at the Mission 8 replacement boundary. Flue Postgres durability does not automatically include these objects. +- The deployed host selection and trusted identity/access boundary; deployment branch evidence stops before these exist. +- Whether the selected review crosses Flue compaction and, if so, how current workpiece and evidence references survive it. +- Representative latency/usage and the smallest non-content operational signals needed to diagnose why-resolution failure. +- The accepted broader scenario portfolio. Do not infer it from Vestera or from the one prebuilt pair. + +## Stop or reorient + +Stop and surface the smallest blocker if: + +- no exact retained source or addendum-owned run is selected, or no owner-approved eligibility and identity/remapping decision promotes it for the selected pair; +- a selected pair cannot be prepared without inventing unsupported operational meaning or hiding material workpiece gaps; +- product language or UI implies the prebuilt net was automatically projected; +- why resolution depends on rereading the transcript, model-generated plausible links, or source-path proximity rather than stored references; +- a missing/stale/broken link produces an answer instead of unsupported/unavailable; +- task-local JSON is retained while the product claims survival across task replacement; +- capture payloads acquire SDCPN types, assertion-card semantics, closed kinds/slots, mapping hints, or completion state; +- the foreground model gains a sweep tool, schedules capture, or ordinary turns wait for extraction/fold work; +- an observer, automatic projector, graph database, or one-artifact capture/workpiece merger appears to tidy the route; +- one green element is treated as closure without inventorying every consequential element in the selected pair; +- the published Petrinaut library gains Brunch-specific product logic or the stock assistant becomes dependent on Brunch; +- remote deployment, trusted identity, owner refusal, telemetry, or replacement recovery cannot be observed at the boundary being claimed; or +- the scenario portfolio must be invented rather than named honestly at cut time. + +## Carried evidence and rejected alternatives + +- Mission 2 established the least capture pipe: explicit harness range, one exact envelope per user utterance, payload `{}`, stable ids on replay, no model extraction, no sweep tool. It did not establish typed semantics, a workpiece join, or durable remote product data. +- Mission 3 accepted one Flue runbook/workpiece path and falsified real-model construction on the exercised provider-visible schema bridge. The hermetic non-empty callback fixture proves packaging and canonical validation; the paid empty net is not a candidate prebuilt pair. +- Mission 4 supplied no full-run conversation/workpiece candidate. Mission 7 may select an accepted Mission 6 prepared fixture for the named review slice, but must preserve its prepared status and may not silently invent candidate status, claim the full topology-neutral portfolio, or redesign accepted upstream semantics while adding references. +- The pinned Mission 8 handoff supplies a locally verified application contract and explicit handoff: fail-closed Postgres Flue state, IAM/static-password paths, TLS, image, health, content-free OTel, and container smoke landed; ECS/RDS/collector/ingress credentials, real IAM/provider turn, replacement recovery, rollback, and acceptance did not. Capture remained inactive and local JSON. That distinction is load-bearing. +- The full capture/workpiece A–D alternatives, shadow-join probe, measurements, and re-entry conditions live in [`MISSION.next.md`](../../MISSION.next.md#captureworkpiece-seam-history-and-rejected-mechanisms). **Mission 7 selects support links only for the prepared pair:** immutable evidence stays separate from editable Markdown synthesis and joins through stable references plus derivation. Complete independence loses only as a sufficient FE-1476 delivery posture; capture-fold and one-artifact shapes remain rejected here. +- Versioned assertion cards remain a possible future response only if the selected Markdown reference seam fails under observed revision strain. They are not the Mission 7 default. +- Typed capture payloads, per-capture loss categories, closed kind/slot catalogs, precision ladders, completion algebra, `firesWhen`, plugin/repertoire runtime, graph storage, and a target-document ontology remain rejected until a real consumer and failure require them. +- An asynchronous inferential observer remains absent. It may re-enter only under later foreground revision strain with its own evidence for ordering, failure, flush, prior-meaning preservation, and latency. +- Automatic projection belongs to Mission 9. Mission 7's prepared net and derivation make the provenance contract testable without pretending the provider-schema and repeated-projection risks are solved. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/9-traceable-projection.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/9-traceable-projection.md new file mode 100644 index 00000000000..879211b56d3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/9-traceable-projection.md @@ -0,0 +1,341 @@ +# Draft Mission 9 — Automatic traceable projection of one meaningful region + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +- [FE-1438](https://linear.app/hash/issue/FE-1438/project-an-evidence-backed-workpiece-into-a-traceable-live-sdcpn) — tracker projection for this future branch mission; the eventual branch `MISSION.md` remains execution authority. + +A fresh builder must resolve these authorities and evidence before choosing a mechanism: + +- [`../../MISSION.md`](../../MISSION.md) — current closure pointer. Mission 9 may be cut only after Mission 7 validly closes its accepted join and a new owner-authorized mission replaces that pointer as sole execution authority. +- [`../../MISSION.next.md`](../../MISSION.next.md) — compact future spine, FE-1476 floor, cross-mission obligations, standing locks, and current Mission 10 handoff. +- [`6-resumable-workpiece-petrinaut-fixture.md`](6-resumable-workpiece-petrinaut-fixture.md) and [`7-capture-backed-review.md`](7-capture-backed-review.md) — provisional viability and provenance predecessors. At cut time replace assumptions with their accepted evidence, exact current workpiece/derivation seam, and real browser mutation behavior. +- [`../mission-archive/3-structurally-typed-runbook-to-headless-pn.md`](../mission-archive/3-structurally-typed-runbook-to-headless-pn.md) and [`../evidence/implementations/fe-1525-headless-runbook-pn.md`](../evidence/implementations/fe-1525-headless-runbook-pn.md) — accepted workpiece leg, canonical callback fixture, provider-visible nested-schema failure, vacuous empty-net result, and explicit next-boundary decision. +- [`../specs/petrinaut-batched-construction-tools.md`](../specs/petrinaut-batched-construction-tools.md) — candidate `pn_read`/`pn_edit` design input and its corrected transaction, outcome, identity, carrier, and ownership constraints. It does not select batching; this mission repairs the known single-action carrier first and admits a batch only if subsequent probes establish it as the least sufficient mechanism. +- [`apps/brunch-agent/src/evaluations/runbook/headless-petrinaut-client.ts`](../../../../../apps/brunch-agent/src/evaluations/runbook/headless-petrinaut-client.ts), [`apps/brunch-agent/test/headless-petrinaut-client.test.ts`](../../../../../apps/brunch-agent/test/headless-petrinaut-client.test.ts), and [`../../packages/plugin-sdcpn/test/construction-tools.test.ts`](../../packages/plugin-sdcpn/test/construction-tools.test.ts) — current bounded six-tool callback route and its limits. +- [`../../packages/plugin-sdcpn/src/tools/petrinaut-construction.ts`](../../packages/plugin-sdcpn/src/tools/petrinaut-construction.ts) and [`../../packages/plugin-sdcpn/src/flue.ts`](../../packages/plugin-sdcpn/src/flue.ts) — the failed Valibot `looseObject({})` + `rawTransform` provider bridge, canonical runtime delegation, conditional construct-only mounting, and exact current tool subset. +- [`../../../petrinaut-core/src/ai.ts`](../../../petrinaut-core/src/ai.ts), [`../../../petrinaut-core/src/action-schemas.ts`](../../../petrinaut-core/src/action-schemas.ts), [`../../../petrinaut-core/src/schemas/entity-schemas.ts`](../../../petrinaut-core/src/schemas/entity-schemas.ts), and [`../../../petrinaut-core/src/ai.test.ts`](../../../petrinaut-core/src/ai.test.ts) — canonical Petrinaut AI schemas, mutation callbacks, ids, nested types, and JSON Schema evidence. These are the authority; Brunch prose or copied field catalogs are not. +- [`../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx`](../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx) and [`../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.test.tsx`](../../../petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.test.tsx) — current `useChat` / `onToolCall`, canonical input parsing, mutation execution, diagnostics sequencing, dynamic interactive tools, and visible failure surface. +- [`apps/brunch-agent/src/http/petrinaut-chat.ts`](../../../../../apps/brunch-agent/src/http/petrinaut-chat.ts), [`apps/brunch-agent/src/conversation/client-tools.ts`](../../../../../apps/brunch-agent/src/conversation/client-tools.ts), and [`apps/brunch-agent/test/petrinaut-chat.test.ts`](../../../../../apps/brunch-agent/test/petrinaut-chat.test.ts) — real panel-to-Flue transport and browser callback resume. +- [`../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md`](../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md), [`../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md`](../../packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md), the skill's construction/check references, and the exact source/workpiece versions eventually selected by Mission 7 or a predecessor addendum — target-formalism guidance and current workpiece contract. Mission 4 produced no full-run candidate; do not treat branch-tip resources or probe evidence as one. +- Commit `157730cc5a214dd9c543e8d95c7193a219c48aef` on deployment branch `ln/fe-1569-brunch-agent-deployment`, read with `git show`, especially `libs/@hashintel/brunch-agent/docs/evidence/implementations/mission-8-deployment-handoff.md`, plus its `MISSION.md`, application persistence/telemetry files, and open infrastructure gates. Mission 9 consumes the actual deployed contract inherited through Mission 7; it must not imply the branch achieved remote deployment. +- [`../../../petrinaut/docs/ai-assistant.md`](../../../petrinaut/docs/ai-assistant.md), [`../../../petrinaut/docs/drawing-a-net.md`](../../../petrinaut/docs/drawing-a-net.md), and affected simulation/formalism guidance. User-visible projection behavior must update the user guide and prompt screenshot replacement where applicable. + +The accepted bounded workpiece region, Mission 7 pair, derivation representation, generated-region semantics, and Mission 10 correction are not yet canonical paths. They must be named from accepted predecessor evidence when this draft is cut. + +## Visible product advance + +**Release note:** Brunch builds a recognisable part of the net itself from the conversation, and can still explain every piece it built. + +**Demo script (no engineer present):** open the demo workpiece in the Petrinaut Brunch panel, on the deployment posture named at cut time. Ask Brunch to model the named region. Watch a non-empty region appear in the live net that a person who knows the process recognises as the thing that was discussed. Ask why about one of the generated elements and get the workpiece meaning and quoted conversation evidence back, exactly as for the prebuilt net in Mission 7. + +**Previously impossible:** every net in the demo was prebuilt by a person; Brunch's only real-model construction attempt produced a parser-valid but empty net. + +Consequential generated elements carry stable caller-supplied ids and derivations that make Mission 7's why operation resolve back to the current workpiece and its exact retained evidence. + +The region must be visually inspectable and operationally meaningful: it must exercise the canonical coloured type, parameter, place, transition, and arc contracts required by the selected process meaning. A toy place/transition pair, empty net, parser-only artifact, headless-only result, or one successful nested tool call is not the visible advance. Repeat, changed-input, and failure-class obligations harden this line at the readiness gate; they do not define whether the first line exists. + +Provider-schema repair, the transaction probe, and the per-action versus batch decision are internal sequencing for this mission and are recorded under the throughline hypothesis below. They are not the advance and must not be presented as progress to a product manager. + +**Completion:** the mission is done when a product manager can run the demo script at the readiness gate below, not when the first nested tool call succeeds. + +## Contract stratum + +Close the **automatic projection stratum for one named meaningful workpiece region and the canonical Petrinaut mutation classes it uses**. + +The bounded stratum includes: + +- current workpiece-region identity and revision; +- canonical Petrinaut coloured types and nested elements used by the region; +- canonical net-level parameters used by the region; +- places, transitions, input/output arcs, and any executable fields required by the selected semantics; +- stable caller-supplied ids and explicit treatment of additions, changes, unchanged elements, and removals if the selected changed-input case requires them; +- derivations from every consequential generated element to current workpiece material, evidence references, projection rationale, assumptions/defaults/omissions/losses, and projector identity/version; +- rejection, bounded repair, unsupported/default behavior, and visible partial failure; +- repeated projection and changed-input projection against current net state; +- semantic correspondence and visual inspectability in the real panel; and +- the exact predecessor provenance operation and durable state boundary. + +Stratum closure is over the named region, accepted scenario/peer set, and mutation classes actually used—not all Petrinaut tools, all SDCPN semantics, or the full optimisation handoff. Mission 11 owns broadening to its accepted full handoff scenario after Mission 10 proves bounded reviewer revision. + +Petrinaut owns canonical schemas and mutations. The projector may select and sequence them, but no Brunch schema copy becomes a parallel authority. The current Markdown workpiece is the semantic input. Captures remain evidence referenced by the workpiece; neither captures nor the full transcript are semantic projection IR. + +## Boundary crossings and current throughline hypothesis + +```text +accepted Mission 7 workpiece/prebuilt pair/provenance seam + → select one bounded current workpiece region and its expected operational meaning + → person requests projection in the deployed Petrinaut Brunch panel + → AI SDK transport dispatches to the Flue ChatAgent + → projector reads the current workpiece region, current net, and accepted derivation state + → Brunch/Flue exposes mechanically preserved provider-visible schemas from canonical Petrinaut Zod contracts + → real model emits canonical caller-supplied ids and nested mutation inputs through the selected per-action or bounded-batch surface + → Flue suspends on client tools + → Petrinaut panel parses inputs with canonical schemas and executes canonical mutations through the selected, proved transaction boundary + → client-tool results resume the same Flue turn + → panel visibly shows the meaningful generated region + → Mission 7 why operation resolves generated element → derivation → workpiece → captures +``` + +Authority and actor crossings: + +- **Workpiece → projector:** semantic interpretation occurs here and is recorded as rationale. The transcript is not primary input and captures are not folded into a semantic model. +- **Petrinaut Zod → provider schema:** schema exposure must preserve arrays, nested objects, refinements that can be represented, descriptions, and required/optional structure mechanically. Provider limitations and any lossy conversion are explicit. +- **Provider → Flue runtime:** provider arguments cross Flue's supported tool-schema interface. Runtime canonical validation remains decisive even when provider schema accepts an approximation. +- **Flue server → Petrinaut browser:** client-tool suspension/resume carries mutation requests/results through the existing AI SDK panel contract. +- **Petrinaut schema → mutation operation → live document:** canonical input parsing precedes canonical mutation. Provider-envelope rejection and canonical per-step rejection are distinct. A non-throwing action may still be a no-op, so operation success requires an explicit outcome or verified postcondition; callback success is not semantic success. +- **Generated element → Mission 7 provenance:** the predecessor why operation consumes the same stable seam. A fixture-specific translation fails the join. +- **Application → deployed substrate:** workpiece, derivation, Flue conversation, and current net identity must survive the accepted Mission 7/Mission 8 replacement boundary. + +Internal sequencing hypothesis: the first in-mission tracer is narrower than the visible advance. Repair the provider-visible nested schema path enough for one real model call to emit and apply a single canonical nested input such as `addType.elements`. That tracer retires the Mission 3 blocker; it does not complete Mission 9 or select batching. Next, a bounded core transaction probe must establish rollback, readonly/extensions parity, indexed failure, and honest no-op outcomes before a five-action batch is tried through Flue and the production client path. Mission 9 selects the batch only if that comparison shows a material advantage over per-action tools without weakening feedback, identity, or failure visibility. + +## Throughline proof floor + +The smallest deployed end-to-end proof must observe all of the following: + +1. A person requests projection of one named bounded workpiece region through the real Petrinaut Brunch panel. +2. The resulting automatically projected live region is non-empty, visually inspectable, and semantically corresponds to the selected operational meaning through the required type, parameter, places, transitions, and arcs. +3. Every consequential generated element has a stable caller-supplied id and derivation to the current workpiece, evidence references, and projection rationale, including any assumption or loss. +4. Mission 7's why operation resolves at least one generated element through that derivation. + +This floor is the first internal milestone, not mission completion: one automatic traceable meaningful region and the positive generated-element provenance link. It does not close repeat/change behavior, schema-class breadth, stale or partial states, repair exhaustion, derivation atomicity, visible failure, full-net generation, arbitrary revision, Mission 10 reviewer authority, an observer, simulation fidelity, or the Mission 11 optimisation package. Those first seven obligations belong to this mission's readiness gate and stratum closure, not to acknowledgment that the working line exists. + +## Readiness ratchet + +```text +Mission 7 deployed capture-backed why over a closed prebuilt-pair provenance stratum +→ inherited provenance/durability closure required by automatic generation +→ single-action provider-visible nested-schema risk tracer +→ bounded transaction/outcome probe → per-action versus batch mechanism decision +→ meaningful live bounded region with stable ids, derivations, and positive why +→ readiness gate +├─ close repeat/change, schema/mutation breadth, stale/partial state, repair, derivation, and failure obligations +├─ admit a stable current-region projection seam and selected correction into Mission 10 +└─ leave full handoff breadth, broad regeneration, observer consolidation, and optimisation unearned +``` + +### Inherited stratum closure + +Mission 9 requires accepted evidence, not draft promises, for: + +- one current Mission 7 workpiece revision and stable region/passage references; +- exact evidence references and a durable, owner-bound capture/workpiece/derivation boundary; +- a why operation that resolves prebuilt element → derivation → workpiece → captures and visibly refuses stale/broken/cross-owner paths; +- a selected non-empty prebuilt pair whose consequential elements have closed provenance dispositions; +- the real deployed panel/AI SDK/Flue/client-tool path and stock-assistant coexistence; +- the actual Mission 8/Mission 7 persistence, identity, telemetry, and replacement contract. + +If the predecessor seam cannot accept a generated derivation without fixture translation, or Mission 7 did not close durability and negative provenance behavior, Mission 9 must stop at inherited closure. Automatic projection cannot turn a provisional provenance route into a dependable base by using it. + +### Readiness gate after the new throughline + +For the named region, enumerate and close: + +- every canonical schema and mutation class used, including nested arrays/objects, optional/null fields, ids, descriptions, runtime-only refinements, error messages, and actions that can no-op without throwing; +- provider-schema rejection versus canonical per-step rejection, repair budget, duplicate tool delivery, client-tool resume, timeout, abort, and partial sequence failure; +- if batching is selected, its supported handle scope, rollback contract, effective readonly and disabled-extension parity, per-step applied/no-op/failure outcomes, and state postconditions; +- unsupported consequential defaults and every assumption, inference, omission, or construction-opened loss; +- semantic correspondence of type elements, parameters, places, transitions, arc direction/type/weight, executable code where used, and canvas-visible structure; +- unchanged repeat, changed input, stale workpiece revision, stale current net, partial prior projection, and concurrent/user change behavior; +- stable ids for unchanged elements, deliberate ids for new elements, deletion/retirement behavior where exercised, and absence of unrelated churn; +- derivation completeness, atomicity relative to applied state, lineage/change account, stale-link refusal, and no successful derivation for rejected mutation; +- visible partial failure and recovery/retry without duplicate state; +- path isolation from the stock assistant and from unrelated net regions; +- latency, usage, transcript fallback, compaction/recovery, and deployed replacement behavior where the real path crosses them. + +Mission 10 may inherit: + +- one accepted bounded workpiece-region identity and current revision; +- one generated live net neighborhood with stable unrelated ids; +- one traceable projection operation and explicit impact boundary; +- one selected operational distinction whose correction has observable but bounded consequences; +- derivation lineage/change-account semantics that can represent retained, changed, added, retired, unsupported, and widened-impact dispositions. + +**Owner:** Mission 10. **Re-entry gate:** an explicitly authorized reviewer supplies new evidence in 3–5 focused turns, a foreground phase-boundary synthesis creates an inspectable revision, and the same projector applies a scoped patch or explicit refusal without unrelated churn. **Oracle:** Mission 10's deployed correction/qualification/contextual-coexistence/conflict matrix and stable-unrelated-id check. + +Do not defer provider-schema fidelity, repeated projection, changed-input identity, derivation atomicity, semantic correspondence, or visible partial failure to Mission 10: Mission 9's automatic projection claim already depends on them. + +## Candidate evidence and oracles + +| Claim leaf | Existing evidence or candidate oracle | +| --- | --- | +| Canonical callback path can build a non-empty parser-accepted type/parameter/place/transition/arc fixture | Existing `apps/brunch-agent/test/headless-petrinaut-client.test.ts`, test `constructs a parser-accepted document through the bounded callbacks`. Run `yarn workspace @apps/brunch-agent test:unit`. This is inner headless evidence only. | +| Current plugin exposes exactly the bounded six-tool subset and delegates runtime acceptance/rejection to canonical Zod | Existing `libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/construction-tools.test.ts`, tests `exposes exactly the bounded canonical subset`, `mechanically carries the canonical input contract`, and `delegates accepted and rejected inputs to Petrinaut's Zod schemas`. Run `yarn workspace @hashintel/brunch-agent-plugin-sdcpn test:unit`. | +| Petrinaut tool metadata aligns with canonical schemas; JSON Schema for representative tools is AI-friendly; callbacks validate before applying | Existing `libs/@hashintel/petrinaut-core/src/ai.test.ts`, suite `Petrinaut AI core exports`, including `tool metadata stays aligned with input schemas and has no execute`, `addArc exposes an AI-friendly object input schema`, and callback tests. Run `yarn workspace @hashintel/petrinaut-core test:unit --run`. | +| Real panel parses canonical mutation input and executes callbacks through `onToolCall` | Existing source in `libs/@hashintel/petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx`; current test covers dynamic follow-up, not automatic construction. **ORACLE GAP:** bind a panel integration test to the actual selected mutation sequence. | +| Real server/client suspension and resume work | Existing `apps/brunch-agent/test/petrinaut-chat.test.ts`, production-door test driven by `petrinaut-chat.integration.ts`. It currently mounts no construction tools on ordinary panel conversations; that negative assertion is a guard, not Mission 9 success. | +| Mission 3 provider-visible bridge failed specifically on nested `addType.elements`, 0-for-9, and empty parser success was vacuous | Existing `docs/evidence/implementations/fe-1525-headless-runbook-pn.md`; inspect the paid-run artifact named there. | +| Live provider accepts the mechanically preserved nested canonical schema | **ORACLE GAP:** no existing real-model call proves this. Resolve first with one budgeted single-action `addType.elements` tracer using the exact provider/model/schema artifact and retain raw tool call/rejection evidence. A successful call does not select batching or complete the mission. | +| A bounded batch can preserve canonical semantics and improve the selected path | **ORACLE GAP:** follow the three probes in `docs/specs/petrinaut-batched-construction-tools.md`: single-action carrier; first-class core transaction/outcome contract; then five-action production-path comparison against per-action tools. Batch selection requires rollback, readonly/extensions parity, indexed failure, no-op honesty, supported-handle scope, production client routing, and material measured benefit. | +| Selected meaningful region exercises required type/parameter/place/transition/arc semantics | **ORACLE GAP:** region and scenario portfolio are not selected. Resolve with a frozen workpiece-region fixture, expected semantic account, canonical resulting definition, and human visual inspection. | +| Mission 7 why resolves generated elements through the same derivation seam | **ORACLE GAP:** Mission 7 is provisional and no generated derivation exists. Resolve through the real deployed why operation without fixture-specific conversion. | +| Unchanged repeat is idempotent and changed input yields bounded identity-preserving change | **ORACLE GAP:** no projection operation exists. Bind to exact current-net before/after definitions, mutation log, derivation diff, and stable-id assertions when implemented. | +| Rejection/repair and partial failure do not advance canonical or derivation state incorrectly | Canonical mutation rejection is locally evidenced by `headless-petrinaut-client.test.ts` and plugin schema tests. **ORACLE GAP:** sequence-level atomicity/partial-state policy and visible deployed recovery are unresolved. | +| Semantic correspondence exceeds parser acceptance | **ORACLE GAP:** establish a workpiece-specific human adjudication; optionally admit a simulation-backed check only if it discriminates the selected meaning cheaply. Parser `ok: true` is explicitly insufficient. | +| Mission 10-ready selected correction has bounded observable consequences | **ORACLE GAP:** choose the correction with the owner only after the generated region exists; record expected retained/changed ids and behavior before Mission 10 is cut. | +| Deployment durability/identity/telemetry survive projection | **ORACLE GAP:** consume Mission 7 accepted remote evidence. The Mission 8 branch handoff alone stopped before infrastructure/replacement proof. | + +## Verification approach + +- **Inner mechanism evidence:** verify mechanical schema derivation from canonical Petrinaut sources, provider-visible nested shape, canonical runtime rejection, stable id planning, deterministic derivation construction, and no derivation commit for rejected or no-op calls. Keep conversion tests structural and compare against canonical source schemas; do not bless copied snapshots as a second authority. If batching is selected, prove its transaction semantics through a first-class core operation rather than direct handle access. +- **First tracer:** make one real provider call against the single canonical nested schema that failed in Mission 3. Record provider/model, generated schema, raw arguments, runtime result, repair count, latency, and cost. Stop on a crisp upstream blocker. Passing retires only the provider-schema risk. +- **Mechanism decision:** after the carrier tracer, run the core transaction probe and then compare the bounded batch with the repaired per-action surface through the production client path. Record schema size, calls, latency, correction behavior, state outcome, and failure visibility. Keep per-action tools if batching does not earn its added core and host contracts. +- **Middle integration/contract evidence:** drive the built Brunch application through Flue client-tool suspension/resume into a real Petrinaut instance. Apply the complete meaningful region, inspect canonical state and derivations, run unchanged repeat, then changed-input projection and an invalid/unsupported case. Include stale revision/current-net and duplicate-delivery probes where the selected protocol permits them. +- **Outer deployed/user-visible evidence:** a human requests the bounded projection in the deployed panel, watches the meaningful region appear, inspects the region, asks why a generated element, repeats unchanged projection, changes the selected workpiece input, and observes bounded visible change or explicit refusal. Witness stock mode remains independent. Mission 9 owns this evidence. +- **Semantic adjudication:** compare current workpiece meaning and explicit expected consequences against the resulting SDCPN, not merely tool logs. Record assumptions/defaults/losses and any mismatch. A simulation-backed check may supplement but not replace this adjudication unless its discriminating contract is accepted. +- **Failure verification:** provider-schema error, canonical rejection, client callback failure, stale state, repair exhaustion, and partial sequence failure must be visible and must not produce false successful derivations. + +## Inputs and joins + +- **Mission 7 join:** accepted current workpiece, durable exact evidence references, stable workpiece/derivation/element seam, deployed why operation, selected prebuilt pair, negative provenance behavior, and owner/replacement guarantees. +- **Mission 3 failure join:** exact paid-run evidence for the failed Valibot open-object bridge and the canonical six-tool hermetic fixture. Repair only the provider-visible loss first; do not reinterpret empty parser success as partial semantic success. +- **Petrinaut canonical-contract join:** consume `petrinautAiTools`, `mutationActionInputSchemas`, entity schemas, and writable callbacks by import or mechanical generation. Mismatches between file-format and action schemas route upstream to Petrinaut. The batched-tools design is candidate input: Petrinaut core may own a generic subset-derived schema and first-class transaction/outcome operation, while Brunch retains subset selection, Flue carriage, client routing, and projection identity. +- **Flue join:** use a documented supported schema/tool path. If Flue cannot preserve the canonical nested schema, produce a crisp upstream requirement instead of deepening an opaque carrier. +- **Host join:** preserve AI SDK `useChat` / `onToolCall` and client-tool result resumption. Mutation execution remains browser/Petrinaut-owned. +- **Scenario join:** owner selects one meaningful workpiece region, operational expected account, accepted peer cases, and one Mission 10 correction. A toy fixture cannot supply this join. +- **Mission 10 output join:** stable region identity/current revision, impact boundary, repeat/change semantics, derivation lineage, and selected correction with expected consequences. +- **Mission 11 horizon:** record omissions needed to broaden from this region to the later accepted optimisation handoff; do not implement that breadth here. + +## Risks and assumptions + +| Risk or assumption | Impact if false | Cheapest discriminating validation | +| --- | --- | --- | +| Flue can expose a mechanically preserved Petrinaut nested schema to the provider | If false, automatic projection is blocked upstream or requires a different supported schema bridge | First real `addType.elements` tracer using Standard Schema/supplied JSON Schema or the least mechanical shape-preserving path. | +| A Zod-to-provider conversion can preserve the load-bearing contract without copying fields | If false, provider acceptance and runtime semantics diverge | Compare generated nested JSON Schema and positive/negative samples directly against canonical Zod for every used tool class. | +| One bounded region can be both meaningful and small enough to close | If false, the mission either proves a toy or expands toward a full net | Select the region and expected operational account before implementation; reject candidates lacking type, parameter, flow, and an observable change. | +| Stable caller-supplied ids plus projection-level operation/base identity are enough for repeat/change locality | If false, generated ids churn, duplicate delivery mutates twice, or stale edits land | Run unchanged repeat, duplicate delivery, stale-base submission, and one changed input against a frozen current net; inspect all ids and unrelated definitions. | +| A first-class bounded batch can improve construction without weakening canonical mutation behavior | If false, atomicity is handle-specific, no-ops appear successful, or coarse feedback increases retries | Compare equivalent sequential and batch results under readonly and disabled extensions, inject duplicate/missing IDs and an invalid late step, then run the selected case through the production client path. | +| The projector can consume bounded context rather than the full workpiece/transcript | If false, locality and later revision become unreliable | Project from the selected region plus explicitly named dependencies; withhold unrelated transcript and observe whether the result remains sufficient. | +| A desired-region recomputation followed by bounded mutations satisfies locality | If false, internal global reasoning may cause hidden dependence/churn | Compare accessed inputs, proposed diff, and applied mutations; owner decides whether applied locality is sufficient for the delivery contract. | +| Mission 7's derivation representation can describe generated and changed elements | If false, automatic generation needs a seam revision before implementation continues | Emit one add, retain, change, and unsupported disposition on paper/fixture and run Mission 7 why resolution. | +| Bounded repair can recover provider mistakes without loops or silent defaulting | If false, projection latency/failure becomes unsafe | Inject one recoverable and one unrecoverable canonical rejection; enforce and visibly exhaust the accepted budget. | +| The selected changed-input case prepares Mission 10 without pre-solving reviewer authority | If false, Mission 10 inherits an irrelevant region or this mission expands into revision | Choose only expected operational consequence here; leave who may revise and how foreground synthesis authorizes it to Mission 10. | +| Parser plus visual inspection is enough for this region | If false, semantically wrong dynamics may look plausible | Try the cheapest workpiece-specific simulation expectation; promote only if it catches a plausible wrong projection. | + +## Accepted constraints and guarded invariants + +- **Petrinaut owns canonical schemas and mutations.** Guard: imports/mechanical generation and structural alignment tests. A generic batch, if earned, is a first-class Petrinaut operation with explicit supported-handle, readonly, extension, rollback, and outcome semantics; Brunch does not reach through an instance to `handle.change`. Stop if field shapes are hand-copied into Brunch prose, Valibot, fixtures presented as authority, or a parallel schema package. +- **Provider repair is only the first risk tracer.** Guard: acknowledge the working line only after the meaningful-region and positive-why floor; mission acceptance then requires readiness closure for repeat, change, and failure classes. +- **Workpiece is semantic input.** Guard: projector input manifest names current workpiece revision/region; transcript and captures are excluded as primary semantic input. +- **Captures remain evidence, not projection IR.** Guard: derivations reference evidence through the workpiece; projector has no capture-to-model reducer. +- **Stable caller-supplied ids are load-bearing.** Guard: unchanged-repeat and changed-input before/after assertions. +- **Applied state and derivation agree.** Guard: derivations commit only after canonical mutation result/current-state confirmation; rejected calls cannot appear successful. +- **No unsupported consequential defaults.** Guard: expected semantic account and assumption/default/loss inspection; unsupported cases visibly stop or remain explicit. +- **Non-empty and semantically meaningful.** Guard: required canonical type/parameter/place/transition/arc inventory, panel witness, and workpiece-specific adjudication. Parser acceptance alone fails. +- **Bounded repair and bounded region.** Guard: named repair budget, selected region/dependencies, mutation/impact log, and stop on widening beyond the accepted boundary. +- **No unrelated churn.** Guard: stable unrelated-id and definition comparison for repeat/change. +- **No observer or automatic workpiece revision.** Mission 9 projects the current accepted workpiece; it does not consolidate conversation evidence or decide reviewer authority. Guard: no scheduler/fold queue and no canonical workpiece writes. +- **One agent, one mounted job skill, existing panel door.** Guard: composition/dependency inventory; no second server, TUI, workflow engine, or subagent topology. +- **Stock assistant remains independent.** Guard: path isolation and host witness. +- **Deployment claims match observed evidence.** Guard: consume accepted Mission 7 replacement proof; do not cite Mission 8's local image as remote deployment. +- **Visible failures do not advance canonical state silently.** Guard: injected rejection, stale state, partial sequence, and timeout/abort tests. +- **Paid provider evidence requires cut-time authorization and a stated budget.** Guard: no real-provider tracer runs from this draft; the eventual live mission records model, maximum calls, and spend ceiling before execution. + +## Cross-cutting obligations + +- Projection fidelity: the generated region comes from the current workpiece and every consequential decision has an attributable rationale. +- Evidence provenance: Mission 7 why reaches exact evidence without laundering model prose into quotation. +- Workpiece sufficiency: construction names a smallest gap instead of silently filling missing objective/process meaning. +- Petrinaut acceptance: canonical schema/mutation validity, non-empty state, diagnostics where executable code is used, visual inspection, and semantic correspondence are distinct leaves. +- Identity and derivation integrity: repeat/change preserve unrelated identities and explain all necessary impact widening. +- Failure visibility: provider schema, canonical rejection, unsupported meaning, stale state, client callback, and partial failure visibly stop/degrade. +- Interaction quality: projection and why occur through the real panel in operational language; construction schema vocabulary does not take over reviewer interaction. +- Deployment/privacy: stable owner-bound product state and content-free observability survive the accepted replacement boundary. +- User docs: document request, visible generated result, why flow, repeat/change behavior, and failure states; prompt replacement of stale screenshots. +- Mission 10 readiness: leave one meaningful selected correction and a trustworthy bounded projection seam, not a generic revision platform. +- Mission 11 horizon: retain omissions and breadth gaps needed for the eventual optimisation handoff. + +## Expected touched paths + +Tentative only; the first real schema tracer may expose an upstream boundary and shrink or redirect this manifest. + +```text +libs/@hashintel/brunch-agent/ +├── MISSION.md ~ cut-time authority only +├── docs/evidence/ + provider tracer, semantic adjudication, deployed projection +├── packages/plugin-sdcpn/src/tools/petrinaut-construction.ts ~ provider-visible canonical schema path and bounded tools +├── packages/plugin-sdcpn/src/flue.ts ~ mount projection capability only on the accepted product route +├── packages/plugin-sdcpn/src/skills/sdcpn-modelling/ ~ construction/projection guidance only where observed strain requires +├── packages/plugin-sdcpn/test/construction-tools.test.ts ~ canonical alignment/provider shape guards +├── packages/core/ ? minimal derivation mechanics only if Mission 7 places them here +└── packages/binding-flue/ ? no change unless supported Flue schema translation belongs at this boundary + +apps/brunch-agent/ +├── src/agents/chat-agent/ ~ compose bounded projection capability +├── src/conversation/client-tools.ts ~ client-tool names/results as required +├── src/http/petrinaut-chat.ts ~ preserve production dispatch/resume +├── src/evaluations/runbook/headless-petrinaut-client.ts ~ evidence harness only, not product substitute +└── test/ ~ real provider tracer and production throughline + +libs/@hashintel/petrinaut-core/ +├── src/ai.ts ~ canonical schema export/generation; subset batch schema only if selected +├── src/instance.ts ~ first-class transaction/outcome operation only if the batch probe earns it +├── src/handle/ ? explicit transaction capability only if existing contracts cannot support the claim +├── src/action-schemas.ts ~ only canonical contract correction discovered at source +└── src/*.test.ts ~ canonical, transaction, readonly/extensions, no-op, and mutation guards + +libs/@hashintel/petrinaut/ +├── src/ui/views/Editor/panels/ai-assistant-panel.tsx ~ generic host execution/visible failure only if needed +├── src/ui/views/Editor/panels/ai-assistant-panel.test.tsx ~ real host integration +└── docs/ ~ affected user-facing guidance + +deployment/infrastructure source outside this checkout ? only if accepted product route exposes a missing deployed contract +``` + +Do not add a hand-copied Brunch schema catalog, graph database, generalized projection framework, automatic observer, capture fold, workflow engine, second agent/server, or full 46-tool stock-modeller surface. + +## Fog-line + +- Which Flue-supported schema interface can preserve canonical Zod nested shape: Standard Schema, supplied JSON Schema, a mechanical shape-preserving conversion, or an upstream Flue change. +- Which canonical refinements are provider-expressible and how runtime-only constraints are described without pretending the provider enforces them. +- The smallest operationally meaningful region and accepted peer cases; the broader demo portfolio remains unenumerated. +- Exact type elements, parameter, places, transitions, arcs, executable code, and extension requirements for that region. +- Whether the current Mission 7 Markdown reference seam supplies stable bounded region identity or needs the least additional revision marker. +- The smallest projector implementation and where its projection plan/derivation state belongs. +- Whether recomputing a desired bounded region internally while applying a local diff satisfies the owner, or genuinely local computation is required. +- How deletions/retirements are represented if the selected changed-input case removes meaning. +- Whether repaired per-action tools or a bounded batch are the least sufficient surface after measured schema cost, calls, latency, correction behavior, and failure visibility. +- If batching is selected, which handles explicitly support rollback, how silent canonical no-ops are reported, and whether one history checkpoint is acceptable in the stock editor. +- Repair budget, provider-envelope versus canonical per-step feedback, timeout, and partial-sequence policy. +- Concurrent user mutation, duplicate delivery, operation identity, base revision, and stale-current-net behavior during projection. +- The exact semantic oracle beyond workpiece-specific human adjudication; simulation remains optional until discriminating. +- The selected Mission 10 correction and what counts as a sufficiently local patch when connected semantics legitimately widen impact. +- Representative deployed latency/usage, compaction/recovery behavior, and any host selection needed for this path. + +## Stop or reorient + +Stop and surface evidence if: + +- Mission 7's accepted workpiece/provenance/durability seam is unavailable or generated derivations require a fixture-specific translation; +- canonical Petrinaut field shapes are manually copied into Brunch; +- Flue/provider cannot receive the required nested shape through a supported mechanical path; record the crisp upstream blocker rather than extending the opaque open-object carrier; +- batching is implemented before the single-action carrier is proved, or selected without explicit transaction scope, readonly/extensions parity, honest no-op outcomes, production client routing, and measured advantage over per-action tools; +- one successful `addType.elements` call, empty net, toy pair, parser result, or hermetic fixture is presented as mission completion; +- the projector reads the full transcript as primary model or treats captures as semantic IR; +- construction silently invents an objective, process spine, resource fate, contention rule, timing/tail behavior, or consequential default absent from the workpiece; +- repeated unchanged projection duplicates elements, churns ids, or mutates unrelated state; +- changed input triggers unrelated regeneration without a visible impact boundary and explanation; +- rejected/failed tool calls acquire successful derivations or partial state is represented as complete; +- semantic correspondence cannot be distinguished from attractive canvas output; +- the bounded region expands toward a complete net or all Petrinaut tools without an accepted consumer; +- an observer, automatic evidence fold, reviewer-authority mechanism, or generic revision platform enters to prepare Mission 10; +- Brunch-specific logic enters Petrinaut's published library instead of a generic host/canonical contract; +- stock assistant behavior or separate history becomes dependent on Brunch; or +- deployment, owner binding, visible failure, or replacement durability is claimed without real-boundary evidence. + +## Carried evidence and rejected alternatives + +- Mission 3 proved canonical Petrinaut callbacks can construct a non-empty fixture through `getLatestNetDefinition`, `addType`, `addParameter`, `addPlace`, `addTransition`, and `addArc`. It also proved runtime rejection of a zero-weight arc and correction in the faux path. +- The paid Mission 3 model run falsified the provider-visible Valibot `looseObject({})` + `rawTransform` carrier: nine `addType.elements` arrays arrived as strings, all were correctly rejected, and the parser accepted only an empty legacy document. Preserve the 0-for-9 result; do not describe it as partial construction success. +- The next accepted move from that evidence was Flue support for Standard Schema/supplied JSON Schema or a mechanical shape-preserving conversion. Extending the open-object carrier or copying Petrinaut fields into Valibot remains rejected. +- Petrinaut's canonical Zod schemas, action schemas, AI tool bundle, and mutation callbacks are current authority. The file-format and action-schema families are aligned by source code and tests, not guaranteed by Brunch; discovered mismatch routes upstream. +- Mission 7 deliberately proves why over an honest prebuilt pair first. Mission 9 must replace the prebuilt projection step with bounded automatic generation while preserving the same provenance contract. +- A comprehensive requirements graph, process-domain ontology, universal subject/predicate/value model, closed kinds/slots, typed completion algebra, deterministic capture-to-model fold, and full regeneration engine remain rejected. They re-enter only under repeated observed inability of workpiece prose plus explicit derivations to support projection or readiness. +- Optional SDCPN mapping hints remain advisory and absent by default. They may re-enter only if projection repeatedly misses consequential structures and a hint demonstrably helps without biasing workpiece meaning; they never copy Petrinaut payload fields. +- Stable caller-supplied ids remain the current least identity hypothesis. A stronger identity ledger re-enters only if repeat/change projection demonstrates unavoidable churn or ambiguity. +- Full desired-net recomputation with bounded applied diff remains fog, not accepted architecture. Unrelated churn or hidden global dependence rejects it. +- Broad 46-tool parity with the stock modeller is rejected; close only the canonical mutation classes the meaningful region actually uses. +- `pn_read`/`pn_edit` are candidate model-facing names, not accepted architecture. Reuse `getLatestNetDefinition` unless an alias earns its production routing cost; retain repaired per-action tools if a bounded batch does not earn its transaction and host surface. +- An inferential observer remains absent. Mission 10's default revision mechanism is foreground phase-boundary synthesis; observer promotion requires separate evidence for ordering, flush, failure, prior-meaning preservation, and foreground latency. +- Mission 11 owns broadening to the accepted full optimisation handoff scenario. Mission 9 must not stop automatically after one tracer, but neither may it expand without the named region, peer set, and oracle. diff --git a/libs/@hashintel/brunch-agent/docs/mission-drafts/README.md b/libs/@hashintel/brunch-agent/docs/mission-drafts/README.md new file mode 100644 index 00000000000..1c66541f0f3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/mission-drafts/README.md @@ -0,0 +1,70 @@ +# Provisional mission drafts + +Files in this directory are detailed context repositories for possible future missions. They are not execution authority, do not create live missions, and must not be implemented. [`MISSION.md`](../../MISSION.md) is currently a closure pointer and [`MISSION.next.md`](../../MISSION.next.md) is the compact future spine. A draft must be re-evaluated and converted into a new root `MISSION.md` as the sole execution authority on its own issue, branch, and PR before implementation. + +Each planning item has one authoritative planning home across `MISSION.next.md` and these linked drafts. A spine summary is only a pointer. Keep accepted decisions, rejected alternatives and reasons, re-entry conditions, scenario classes, evidence, constraints, fog, stop conditions, risks, assumptions, and named mechanisms in one discoverable home at the precision needed by a cold-start builder. + +`Visible product advance` must pass the product-manager litmus defined in [`MISSION.next.md`](../../MISSION.next.md#current-authority-and-accepted-spine): a product manager who did not watch the work must be able to notice that the product materially moved forward. State a release-note sentence, a demo script a product manager can run without an engineer, and what was impossible before. Keep snapshots, manifests, ledgers, negative controls, and engineering sequencing out of this section; they are oracles and throughline hypotheses. `Throughline proof floor` is the first internal milestone, not the completion bar; the mission completes at `Readiness gate after the new throughline`, when the demo script works for the named scenario. + +Every draft must begin with the non-authority warning shown in the template. A draft may preserve a visible product hypothesis, contract stratum, provisional throughline, tracer floor, readiness obligations, joins, constraints, fog, stop conditions, evidence, and rejected alternatives. It must not contain `Status`, a final `Imperative`, or a final `Proof`. It may mark an acceptance leaf `ORACLE GAP` only when it also states what must resolve the gap before the cluster can be cut or the leaf claimed. + +## Lifecycle + +1. Before promotion, re-read the draft's evidence and dependencies, inspect the real deployed boundary, and confirm the accepted scenario portfolio and contract stratum. +2. Convert the selected draft into the six-section live mission contract in `MISSION.md`; do not blindly rename or copy it. +3. Give the live mission final `Status`, `Imperative`, `Throughline`, oracle-bound `Proof`, `Constraints`, `Fog-line`, `Stop or reorient`, and `Deferred` sections. +4. Return every item not admitted to the cut to `MISSION.next.md` or another draft at full fidelity. +5. Remove the consumed draft so no duplicate quasi-authority remains, then compare every affected planning file before and after for one surviving home per item. +6. When the eventual live mission is accepted, archive it under [`docs/mission-archive/`](../mission-archive/) according to the existing [archive rules](../mission-archive/README.md). + +## Draft template + +Omit a section only when no earned content exists. Do not collapse known precision merely because a heading is optional, and do not add symmetric filler. + +````markdown +# Draft Mission N — Name + +> Draft cluster only. Not execution authority. Do not implement until this cluster is re-evaluated and cut into `MISSION.md`. + +## Cold-start reads + +## Visible product advance + +## Contract stratum + +## Boundary crossings and current throughline hypothesis + +## Throughline proof floor + +## Readiness ratchet + +### Inherited stratum closure + +### Readiness gate after the new throughline + +## Candidate evidence and oracles + +## Verification approach + +## Inputs and joins + +## Risks and assumptions + +## Accepted constraints and guarded invariants + +## Cross-cutting obligations + +## Expected touched paths + +## Fog-line + +## Stop or reorient + +## Carried evidence and rejected alternatives +```` + +`Cold-start reads` contains pointers to exact canonical paths or ids, not copied content, and must let a separate builder resolve the cluster without the originating conversation. `Boundary crossings` renders every consequential layer or actor transition from entry to visible exit. `Readiness ratchet` names the prior throughline proof consumed, inherited closure now load-bearing, obligations closed here, and obligations carried to a named next owner with a re-entry gate and oracle. + +`Candidate evidence and oracles` binds each currently observable proof leaf to an exact test file and name, command, fixture, artifact inspection, human witness, or adjudication. `Verification approach` distinguishes inner mechanism evidence, middle integration or contract evidence, and outer deployed or user-visible evidence, with explicit ownership for outer verification. + +`Risks and assumptions` records each consequential assumption, its impact if false, and the cheapest discriminating validation. `Accepted constraints and guarded invariants` names what must survive and its existing or required guard, with stop-the-line invariants explicit. `Expected touched paths` is a tentative directory/file-level manifest using `+`, `~`, `-`, and `?`; it supports scope and overlap detection but remains revisable when the real path exposes a better boundary. diff --git a/libs/@hashintel/brunch-agent/docs/reference/architecture/capture-store.md b/libs/@hashintel/brunch-agent/docs/reference/architecture/capture-store.md index b8528fde9eb..53d22ca1841 100644 --- a/libs/@hashintel/brunch-agent/docs/reference/architecture/capture-store.md +++ b/libs/@hashintel/brunch-agent/docs/reference/architecture/capture-store.md @@ -1,7 +1,7 @@ # The capture store, in plain terms A plain-prose rendering of what the top of the stack establishes: the capture store -(FE-1390, `packages/core/src/capture-store.ts` + `packages/binding-flue/src/local-capture-store.ts`) +(FE-1390, `packages/core/src/evidence/capture-store.ts` + `packages/binding-flue/src/local-capture-store.ts`) and the ask/reply machinery it will eventually serve (FE-1389, where the two touch). Rendered from the code first, with the kernel spec (§5, §9.6, §14.1) and CONTEXT.md as the claimed semantics the code is read against. The strain report at the end is the review yield: every diff --git a/libs/@hashintel/brunch-agent/docs/reference/architecture/topology.md b/libs/@hashintel/brunch-agent/docs/reference/architecture/topology.md index 3b39e9f3bec..8e636b7eec2 100644 --- a/libs/@hashintel/brunch-agent/docs/reference/architecture/topology.md +++ b/libs/@hashintel/brunch-agent/docs/reference/architecture/topology.md @@ -1,34 +1,26 @@ # Topology: verification and specification -**Status: ratified 2026-08-17 (Lu) — recorded as [ADR-0002](../../adr/0002-topology-and-placement-rules.md); -this file remains the living reference.** Verifies -the current app/package topology against the three-lane model (cheatsheet, boundary summary), -spec §12.2, and Flue's project-layout guide; then specifies where upcoming work lands. Pseudo- -style: tree nodes with rules; `✓` complies today, `✗` violates, `→` normative rule for what's -next. +**Status: ratified 2026-08-17 (Lu), application layout updated 2026-08-31 — recorded as [ADR-0002](../../adr/0002-topology-and-placement-rules.md); this file remains the living reference.** Verifies the current app/package topology against the three-lane model (cheatsheet, boundary summary), spec §12.2, and Flue's project-layout guide; then specifies where upcoming work lands. Pseudo-style: tree nodes with rules; `✓` complies today, `✗` violates, `→` normative rule for what's next. ## Verification — the tree as it stands ```text -packages/core LANE 3 (harness; substrate-free) -├─ capture-store.ts ✓ the storage port's contract + pure command surface; owns envelope -│ invariants. Never: substrate imports, IO, per-substrate shapes. -├─ session-log.ts ✓ substrate-neutral archive/version/anchoring rules; archive ordinals -│ are evidence identity, substrate ids remain provenance only. -├─ storage.ts ✓ binding-only storage support subpath; not part of the plugin SDK. -├─ affordance.ts ✓ envelope schemas (free-text form) -├─ naming.ts ✓ identity/tool-name policy (ADR-0001) -├─ plugin.ts ✓ plugin identity + one declared proposal floor (FE-1392); grows the -│ full catalog/tables/ops contract at FE-1393 -├─ prompts.ts, -│ repertoire.yaml ✓ guarded `./prompts` subpath: validated harness-default teaching; -│ binding/evaluation composition only, never a plugin import (ADR-0008) -├─ testing/ ✓ test utilities subpath (mirrors Flue's own store-contract pattern) -├─ ask-protocol.ts ✓ substrate-free ask/suspension mechanism: affordance minting, guard, -│ reply-binding signal, and instruction fragments (FE-1422). See N1. -└─ sweep-protocol.ts ✓ substrate-free settlement/sweep mechanism: trigger/high-water facts, - replayable settled range, repair continuation, quote-only extraction, - and affordance-bound accounting advisories (FE-1392). See N1. +packages/core CORE HARNESS + Flue-native agent contribution +├─ prompts/SYSTEM.md ✓ authoritative context- and formalism-independent always-on prompt +├─ skills/elicitation/ ✓ core's one capability skill: `SKILL.md` + `references/universal-elicitation.md`, +│ packaged through `skills/skill-markdown.ts` and mounted by `flue.ts` +├─ flue.ts ✓ `useBrunchAgent()`: model, elicitation skill, returned core prompt (`./flue`) +├─ evidence/ ✓ active capture-store and archived-session evidence authority +├─ conversation/ ✓ tool naming and the harness reply-event contract +├─ _suspended/conversation/ ○ compiled ask/affordance and settlement protocols; not mounted; +│ re-exported only for contracts other packages still type against +├─ client-tools.ts ✓ public browser/client contract subpath +├─ storage.ts ✓ binding-only public facade over archived-session evidence +├─ index.ts ✓ substrate-neutral evidence and contract facade +└─ json-value.ts, + readonly-deep.ts ✓ package-wide representation primitives, not a generic utility directory + (plugin/, teaching/, interpretation/, prompts.ts, testing/, and schema/ — the YAML plugin + definition, repertoire, and typed interpretation machinery — were removed 2026-09-02) packages/binding-flue LANE 2 (translate harness ↔ Flue dialect) ├─ capabilities.ts ✓ capability declaration — the binding's contract-of-record @@ -39,10 +31,7 @@ packages/binding-flue LANE 2 (translate harness ↔ Flue dialect) │ cannot inject pre-classified archive entries. ├─ capture-accounting.ts ✓ recovers active-session Flue ids from session-qualified archived │ evidence pointers; contains no accounting policy. -├─ index.ts ✓ useElicitation is Flue HOOK WIRING (useTool, usePersistentState, -│ useAgentStart/useAgentFinish, useDataWriter, harness.prompt, durable -│ step.do, ctx.append) and calls core's ask/sweep protocols (FE-1422/92). -│ Never: elicitation semantics, store rules, prompt content. +├─ index.ts ✓ active public history, reply-projection, and local-store adapters only └─ local-capture-store.ts ✓ versioned storage-port implementation (capture store + session-log archive, legacy provisioning, parse-on-read, tmp+rename, per-path queue). One per deploy target per binding. Never: business rules. @@ -54,52 +43,65 @@ packages/transport-aisdk UI REPLY WIRE (substrate-neutral) out-of-band. Never: binding/Flue imports, inference, conversation rendering, or diagnostics dispatched as user evidence. -packages/plugin-gherkin LANE 3 (target policy) -└─ index.ts ✓ identity + one `statement-noted` ConditionStated verbatim floor; - strict schema forbids parsed structure and silent hardening. - Never: harness mechanism, substrate imports, storage. +packages/plugin-gherkin TARGET POLICY + Flue-native contribution bundle (not yet composed) +├─ index.ts ✓ pairing identity only (YAML definition removed 2026-09-02) +├─ prompts/APPEND_SYSTEM.md ✓ optional always-on plugin append +├─ skills/gherkin-specification/ ✓ `SKILL.md` + `references/` + `templates/`; routes to core `elicitation` +└─ flue.ts ✓ `useGherkinPlugin()`; no tools until a real parser/binding capability exists + +packages/plugin-dafny STUB contribution bundle (topology pressure test; not composed) +├─ prompts/APPEND_SYSTEM.md, skills/dafny-verification/SKILL.md, flue.ts — placeholder homes only + +packages/plugin-sdcpn TARGET POLICY + Flue-native production contribution +├─ index.ts ✓ pairing identity only (YAML definition removed 2026-09-02) +├─ prompts/APPEND_SYSTEM.md ✓ compact always-on SDCPN append +├─ skills/sdcpn-modelling/ ✓ `SKILL.md` + `references/{profile,pn-construction,checks}.md` +│ + `templates/workpiece.md`; activates core `elicitation` for human knowledge +├─ flue.ts ✓ `useSdcpnPlugin()`: append, job skill, doc tool, conditional construction tools +└─ tools/ + ├─ petrinaut-construction.ts ✓ bounded, schema-validated SDCPN realization tools + └─ read-petrinaut-doc.ts ✓ Petrinaut editor guidance exposed as a client-executed tool apps/brunch-agent LANE 1 SHELL + remote server (imported from apps/dev) -├─ src/app.ts ✓ single fetch entry; explicit mounts; assets beside agents -├─ src/routes.ts ✓ the one shared mount constant (doctrine per routing guide) -├─ src/agents/gherkin-elicitor.ts ✓ thin directive-marked host (§12.1); flat file OK until -│ the second agent (then per-agent folders, FE-1385) -├─ src/elicitation-session.ts, target-document-path.ts ✓ host-owned session/document binding, -│ full mount URL/transport, and opaque local target path -├─ src/petrinaut-chat.ts ✓ thin Flue→harness-event→AI SDK composition; `/api/chat` mount and -│ opt-in JSONL inspection. No second conversation renderer. -├─ src/db.ts, db-path.ts ✓ convention entry + separately testable path logic, deliberately split -├─ src/ui/chat.tsx ~ hand-rolled client; tolerated ONLY until FE-1385 adopts @flue/react +├─ src/app.ts, db.ts ✓ Flue convention authorities: one route map and one conversation adapter +├─ src/db-path.ts ✓ testable package-relative path policy kept at source root because the +│ same relative URL must survive Flue's flattened `dist/` bundle +├─ src/agents/chat-agent/ +│ ├─ agent.ts ✓ sole directive-marked registration and composition point: generic core, +│ │ selected SDCPN/Petrinaut plugin, and deployment instructions +│ └─ tools/ping.ts ✓ app-only server-path diagnostic +├─ src/http/ ✓ HTTP authority: assets, route names, ownership guard, local origins, +│ and `/api/chat` composition +├─ src/conversation/ ✓ identity and projection authority: shared payload, client-tool signal, +│ Flue-history transcript, and AI SDK stream projection +├─ src/capture/ ✓ Mission 2 application composition over binding-owned history/store ports; +│ no elicitation policy +├─ src/evaluations/runbook/ ✓ runbook experiment drivers, artifact recovery, and headless client; +│ not product runtime authority +├─ src/diagnostics/ ✓ operator-facing transcript CLI +├─ src/ui/ ~ hand-rolled client; tolerated ONLY until FE-1385 adopts @flue/react │ (divergence risk 1). Never: growing new part-rendering features here. └─ test/ ✓ reviewed substrate inventory; child-process eval (audited: composed from documented parts; do-not-weaken pins live here) - -evaluations/protocols/legacy-baseline/run.ts EXPERIMENT PROTOCOL ``` ## Specification — where what's next lands - **N1 (the structural repair, discharged by FE-1422 + FE-1392).** - `packages/core/src/ask-protocol.ts` now owns pure affordance minting, the one-live guard, - reply-binding signal payload, and instruction fragments. `packages/core/src/sweep-protocol.ts` + `packages/core/src/conversation/ask-protocol.ts` now owns pure affordance minting, the one-live guard, + reply-binding signal payload, and instruction fragments. `packages/core/src/conversation/sweep-protocol.ts` owns range selection, trigger/repair decisions (including reopening the loop guard after a refusal), prompt content, and advisory semantics; `useElicitation` contributes only Flue projection, hooks, persistent-state, private-prompt, refresh, and durable-step wiring. A future `binding-pi` reuses both protocol modules. -- **N2 (plugin cells and the repertoire; amended by ADR-0007 and ADR-0008).** Plugin-owned content - lives in plugin packages; never in per-agent app `skills/` directories, which would put lane-3 - policy inside lane-1 and outside the boundary gates. Harness-owned teaching lives in core and is - exported only from `@hashintel/brunch-agent/prompts`; bindings and evaluation composition may - import it, plugins may not. The committed YAML remains directly assertable outside the bundle. +- **N2 (plugin cells, repertoire, and the proving runbook; amended by ADR-0007, ADR-0008, Mission 3, and FE-1563; retired 2026-09-02).** The YAML cell/repertoire machinery described here was removed on 2026-09-02 once plugins became Flue-native contribution bundles; this paragraph is history. Reusable plugin-owned policy lives in plugin packages, and harness-owned repertoire teaching lives in core behind `@hashintel/brunch-agent/prompts`; plugins may not import that guarded prompt data. FE-1563 established a separate Flue-native production seam: core's `./flue` subpath supplies the stable agent prompt, while plugin-sdcpn's `./flue` subpath and exported `SKILL.md` supply SDCPN prompt material, progressive teaching, and target-specific tools. This does not reactivate the generalized repertoire/`useElicitation()` runtime. The app retains only the directive-marked registration point and host-specific capabilities. - **N3 (application composition; amended by ADR-0004 / FE-1437).** There is no dedicated demo shell. The standalone `apps/dev` was imported as `apps/brunch-agent`, which owns the remote Brunch server, target gallery, and diagnostics. `apps/petrinaut-website` owns the user-facing integration. Applications may compose Brunch and Petrinaut public surfaces; reusable libraries may not know about one another. -- **N4 (experiments).** Experiment runners (FE-1404, future condition reruns) stay beside their - the JS-API pattern with `observe()` accounting — never in - `packages/` (they are instruments, not product) and never a bespoke daemon. +- **N4 (experiments).** Experiment runners live under the consuming app's `src/evaluations/`, use the JS-API pattern with `observe()` accounting, and never enter `packages/` or become bespoke daemons. Reusable cases, oracles, and protocols remain under the context-root `evaluations/`; observed output remains under `docs/evidence/evaluations/`. - **N5 (storage-port implementations; local target discharged by FE-1391).** One per (binding × deploy target), always in the binding package, always implementing core's `CaptureStore` + parse-on-read. The local implementation provisions a versioned target-document record around @@ -108,9 +110,4 @@ evaluations/protocols/legacy-baseline/run.ts EXPERIMENT PROTOCOL - **N6 (plugin-assurance, when chartered).** `packages/plugin-assurance`, same shape as gherkin; its existence is FE-1387's contract-freeze instrument, not a feature. -Ratification note: N1 was the only item that changed existing code; FE-1422 extracted the ask -protocol and FE-1392 continued the same repair for sweep mechanism. -N2–N6 constrain future placement. ADR-0002 records the ratification. The boundary gates in -`test/boundaries.test.ts` should learn the enforceable parts as their packages arrive (N2's -"no plugin content in app skills dirs" and N5's "port implementations only in bindings" are -both mechanically checkable). +Ratification note: N1 was the only item that changed existing code in the original 2026-08-17 ratification; FE-1422 extracted the ask protocol and FE-1392 continued the same repair for sweep mechanism. Mission 3 later narrowed N2's blanket app-skill prohibition for one directly authored proving instrument without reactivating plugin composition. N2–N6 otherwise constrain future placement. ADR-0002 records the original ratification. The boundary gates in `test/boundaries.test.ts` should learn enforceable package rules as their packages arrive; N5's "port implementations only in bindings" remains mechanically checkable. diff --git a/libs/@hashintel/brunch-agent/docs/reference/hash-documents/sdcpns-a-common-language.md b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/sdcpns-a-common-language.md new file mode 100644 index 00000000000..e1a8e269916 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/sdcpns-a-common-language.md @@ -0,0 +1,221 @@ +# SDCPNs: A Common Language for Complex Systems + +We want to provide formal guarantees of AI safety across a wide range of domains and applications. That creates a modelling problem before it creates an AI problem: if we want to reason rigorously about whether an AI-controlled system is safe, we first need a mathematical representation of that system and the potential consequences of action within it. + +There are many established mathematical languages for describing the world. Differential equations describe quantities that evolve continuously over time, such as temperature or motion. State machines and automata describe systems that move between discrete modes. Markov decision processes capture decision-making under uncertainty. Petri nets represent concurrency and competition for shared resources. Each of these formalisms help model different sorts of behaviours that we find in the real world. + +Hybrid-system formalisms are designed to bring multiple of these lenses together. We are investing in *Stochastic Dynamic Coloured Petri Nets* (SDCPNs), a Petri net variant that provides a way to represent continuous and discrete behaviour, multiple interacting entities, structured state, data-dependent decisions, concurrency and randomness within a single framework. + +That combination is particularly useful for *cyber-physical* systems: where a physical process is monitored and even controlled by digital elements. A physical change may enable or block a decision; a decision in cyberspace can alter a physical trajectory; and failures often emerge not from either side independently, but from their interaction. + +In this post we'll build up an SDCPN from an ordinary Petri net, explain what each extension adds, and then use several cyber-physical examples to show the range of systems the resulting formalism can represent. + +# What is an SDCPN? + +A [Petri net](https://petrinaut.org/) is a directed bipartite graph where **places** (circles) and **transitions** (rectangles) are connected by **arcs** (edges/arrows), in totality representing a process. Places can represent conditions or resource buckets and are *marked* by **tokens**, with the markings recording the state of the process at any given moment. Transitions are enabled by the presence of tokens in their input places, and when they *fire* they consume tokens from their input places and produce tokens in their output places, thereby changing the state of the process. + +For example, a very simple Petri Net modelling a pedestrian traffic light might look like this: + +![][image1] + +The above Petri net includes two places, Red and Green, connected by two transitions. The net starts with one token in Red, when “Switch to Green” fires, it moves to Green; when “Switch to Red” fires, it moves back to Red. This illustrates the logic of switching between green and red on a traffic light. In practice the firings would all be near-instantaneous, the animation above was slowed down to illustrate the switch clearly. + +A plain Petri net records what can happen and in what order, but without modelling durations, likelihoods, or what distinguishes one token from another, and it cannot answer "how often" or "how likely" questions about a given scenario. + +An SDCPN extends a plain Petri net with 4 features: + +* **Colour**. Each place is associated with a *colour set* $C$, which defines the type and structure of data that tokens in that place may carry. A token residing in the place has a particular *colouring* $x\ \epsilon \ C$: that is, a specific assignment of values to the attributes defined by the place’s colour set. Colouring therefore makes otherwise identical tokens distinguishable. For example, if a place represents stored food products, its colour set might specify the tuple *(product ID, production date, expiry date, …)*, while an individual token could have colouring **x** \= *(P123, 01/06/2026, 01/09/2026, …)*. Token values can be used to influence net behaviour, for example the introduction of a transition guard function $G(x)$*G*(**x**), which evaluates the colouring x of the tokens available to a transition. The transition is enabled when $G(x)=1$ and disabled when $G(x)=0$. For instance, a transition representing “ship to customer” could have a guard G(x) \= 1 if the product’s expiry\_date \> today \+ 365 days, and G(x) \= 0 otherwise, so that only products that have a sufficient shelf life are eligible for shipment. + +* **Stochastic firing.** Rather than firing immediately once enabled, each enabled transition *i* independently draws a random firing delay from a probability distribution whose parameters can depend on the token's colouring **x**. In the common exponential case, P(fire in dt) ≈ λi(**x**) dt, where λi is the firing rate of transition *i*. When multiple transitions are enabled, the one with the shortest sampled delay fires first. A higher λ means a shorter expected delay, so faster processes tend to fire before slower ones but a slower transition can still occasionally go first. For example, if a delivery has a firing rate of 0.2 per hour and a van breakdown has a rate of 0.01 per hour, deliveries fire far more often but a breakdown can still happen first on any given run. + +* **Deterministic dynamics (ordinary differential equations \- ODEs).** While a token sits in a place, its colouring $x$ (e.g. fluid level, machine’s wear) evolves continuously through + +$dx=f(x,\ p)dt$ + +where $f(x,\ p)$ is the deterministic drift term which dictates the trend of the colouring evolution and $p$ is the set of fixed constants such as flow rates or degradation coefficients. This evolution replaces the assumption of static token colouring: a token’s data changes continuously while it remains in a place, according to differential equations that can represent processes such as material consumption, fluid-level changes, or progressive equipment degradation. Whether a token enables a transition or at what rate the enabled transition will fire can therefore evolve over time depending on the evolution of token data. We can use this to model the freshness of food decreasing over time at a rate of $dx=-k{e}^{(-T/20)}dt$, where k is a base decay rate and T is the storage temperature, so warmer conditions accelerate the loss of freshness and the product becomes unsellable when its freshness crosses a threshold. + +* **Stochastic dynamics (stochastic differential equations \- SDEs).** The differential equations can include random noise, so continuous evolution can be perturbed by random noise. This is represented as a stochastic differential equation (SDE): + +$dx=f(x,\ p)dt+\sigma (x,\ p)dB$ + +where $B$ denotes a stochastic process such as the Weiner process through which randomness enters and $\sigma (x,\ p)$ is the diffusion term acting as the scaling factor of randomness. The first term $f(x,\ p)$ is the same drift term appearing in deterministic dynamics. Examples of stochastic dynamics are demand rates or ambient temperature drifting randomly. + +The four features together allow SDCPNs to represent all aspects of a cyber-physical system in the same state and clock: its physical process (a tank emptying, a machine wearing), control logic (when to dispatch, when to service), and the randomness that affects both (a breakdown, a demand spike). + +## The origin of SDCPNs + +While ordinary Petri Nets date back to the 1960s, SDCPNs were developed by [Mariken Everdij and Henk Blom](https://link.springer.com/chapter/10.1007/11587392_10) at the National Aerospace Laboratory (NLR) in the Netherlands, in the early 2000s. The problem they were trying to solve was quantifying the probability of a mid-air collision, an event too rare to observe in testing, and requires modelling many aircraft moving continuously, controlled by different humans and automated systems. Everdij and Blom extended the Dynamically Coloured Petri Net (DCPN) by adding Brownian motion to the equations governing how token data evolves between firings, and defined the result as a formal 12-component specification ([P, T, A, N, S, C, V, W, G, D, F, I](https://old.sf.bg.ac.rs/downloads/katedre/apatc/ICRAT2010.pdf)). + +The formalism was designed to support a safety case, expressed in probabilistic terms: a defensible and quantitative statement about how often something bad happens in a system too complex to test exhaustively. With a view to enabling the same kind of guarantees to be made regarding the behavior of frontier AI models, the ARIA [Safeguarded AI](https://aria.org.uk/opportunity-spaces/trust-everything-everywhere/safeguarded-ai) (SgAI) programme is also building on SDCPNs. Our goal is to enable the development of world models expressive enough to represent both complex physical systems and the AI that control them, with a safety specification over both. The air traffic problem, with continuous dynamics, multiple agents, human and automated controllers, rare catastrophic events is exactly the class of system SgAI is building for. The only difference is the controller: the original use case modelled human operators and procedural automation, while SgAI focuses on neural networks. + +# Modelling the real world with SDCPNs + +The following sections demonstrate how we can model real world systems using SDCPNs with three examples: an industrial supply chain process progressively, truck fleet maintenance and semiconductor fabrication. We built the first example (industrial gas supply chain) progressively, adding one feature of the formalism at a timeat each level until the model is a full SDCPN. To illustrate the expressivity of SDCPNs, we model two further domains as full SDCPNs: truck fleet maintenance and semiconductor fabrication. + +## Industrial gas supply chain + +Taking an industrial gases supply chain as an example use case: a gas supplier delivers liquid gases (e.g. nitrogen, oxygen) to customer sites by road tankers. The supply chain operates in a standard practice where the supplier owns the liquid in each customer's tank, reads the level by telemetry, and decides when to send a refill. The customer draws product as needed and only pays for what they consume, but does not place orders. + +Each tank empties from two sources: the customer's consumption and heat leaking through the walls, which boils liquid gas off continuously. A delivery that arrives before enough space exists risks overfilling. The supplier must keep every tank within a safe range: + +* **Too low**: the customer's production line stops (stockout). + +* **Too full**: boil-off gas has nowhere to go, pressure rises, and the relief valve vents the gases into the atmosphere, creating wastage and potential safety concerns. + +The reorder point, load size, consumption rate, boil-off rate, and delivery lead time are all coupled. A tanker dispatched to one site is unavailable to other customer sites until it returns. + +### Plain Petri net + +First we model the system using a [plain Petri net](https://drive.google.com/file/d/1gm8SxdDZUYbn4GMkaoOOJqGpt38V685U/view?usp=drive_link) with only places, transitions, tokens (without colours) and different arcs and arc weights. + +The net consists of just 1 customer site to illustrate the core concepts. To start, there is a tank of nitrogen on site holding 42 out of 54 units of liquid gas; one tanker at the depot and up to 2 loads may be on order at once. The customer’s production line is running. + +As the customer consumes the nitrogen and some boils off, the level drops. When the level drops to 15 (representing the telemetry-based sensor in the tank), an order is placed. A tanker dispatches, arrives and delivers 12 units (only if 12 units of space exist in the tank). The permit and tanker return on delivery and cycle repeats. + +Stockout happens if the tank hits zero and the production line stops. It restarts when at least 1 unit arrives. + +If the tank is completely full, a relief valve opens and reduces the level of gas (by 1 unit). Under this level trigger order policy, venting is unreachable since the maths of the reorder point and load size prevent it (gas only refills by 12 units when at or below 15 units). In later timed-extensions of the model, we introduce pressure-driven venting since in practice, venting is required when pressure gradually builds as the liquid warms. + +[\[Here\]](https://drive.google.com/file/d/177jPqinyVje7RDLYGzZGaIYqYFVSE5sK/view?usp=drive_link) shows the net for a variant of the order policy based on consumption-trigger. Instead of reordering when the level drops below a threshold, the system reorders after every 8 units are drawn by the customer without accounting for any evaporation. This results in a failure mode whereby, If enough nitrogen boils off, the tank empties without the consumption counter ever reaching 8\. The system reaches a deadlock: the tank is at zero, fewer than 8 units have been drawn since the last order, and nothing in the model can change the state in the system so the production line stops and never restarts. + +Without time accounted for in the model, the net picks any enabled transition to fire without any rules on ordering. There can be a scenario where the transition for consuming nitrogen is fired repeatedly and empties the contents without dispatching the tanker for refill. Adding durations fixes this so events happen according to rates rather than random choice, which we explore in the next progression where stochastic firing rates are added. + +This basic Petri net is useful for checking the logic of the system, such as finding deadlocks (consumption-trigger variant), checking conservation laws (liquid \+ ullage \= 54 always) etc. Without the additional features, it falls short on answering the more interesting timing questions like whether the tank runs dry before the tanker arrives, or what load size and reorder point minimise stockouts. + +### Add time + +\[[The following](https://drive.google.com/file/d/1qi8AlNVYZJ7I4goj9ym8726Bj9EmMmnQ/view?usp=drive_link)\] shows a Stochastic Petri net (SPN), based on the same structure as the basic Petri net with the addition of rates on each transition and a second site (SlowNitrogen) to represent a low-consumption customer. + +Each enabled transition now independently draws a random delay time from a general family of distributions. One prominent distribution in this family is the exponential distribution, where each transition has a random delay t (0 to ∞) drawn from: + +$P(t\ |\ \lambda )=\lambda {e}^{(-\lambda t)}$ + +Where $\lambda $ is the average delay per hour (firing rate) for that transition. So whichever transition is delayed the least fires first. For example, when the customer’s (SteadyNitrogen) tank is half full, three transitions are enabled at once: the customer drawing product (rate 0.80/h), boil-off (0.16/h), and the tanker arriving (rate 1/6 ≈ 0.17/h). The runtime engine rolls a random delay time for each: 0.45 hours for product draw, 3.1 hours for boil-off, and 2.8 hours for the arrival. The nitrogen draw transition fires first so one unit leaves the tank, the clock advances 0.45 hours, and all three roll again from scratch. + +A limitation of modelling journey time as a stochastic rate on the arrival transition (1/6 or 1/9 per hour, giving mean journeys of 6 and 9 hours) is that the exponential distribution allows a delivery to arrive almost immediately or take far longer than its mean. Realistic journey times need a minimum and a bounded spread, which requires clocks counted down by dynamics. + +With the addition of a second customer, the one supplier tanker serves both sites with a new "returning" state to represent the time the tanker spends driving home after each delivery, introducing a risk of stockout if one customer is left waiting whilst the tanker delivers for another. For example, if the SteadyNitrogen tank crosses its reorder threshold (16 units) while the tanker is mid-journey to the SlowNitrogen site, it must wait for the delivery to complete (up to 9 hours remaining), the return trip (\~4 hours) plus its own journey (\~6 hours) before receiving the product. At a combined drain of \~0.96 units/hour, 16 units of buffer lasts roughly 17 hours, so on some runs can result in a stockout for the high consumption customer (SteadyNitrogen). + +This stochastic net introduces time and contention with transitions competing to fire next and additional customers that can lock out delivery for each other. It answers questions on stockout frequency, the cost of sharing a tanker and the effect of slower routes. It falls short on the realism of timing (a 6-hour journey is exponentially distributed, so it sometimes arrives in minutes and sometimes takes a day), distinguishing between different products, and continuous processes like boil-off are still modelled as discrete random events rather than a steady flow. + +### Add data types (colours) + +To distinguish which product a tanker is holding, we can add colours so each tanker token now has a data field identifying its gas type. In [this example](https://drive.google.com/file/d/17nHeK4cmA9MX5q2vfYYifbQzJB2vZq0R/view?usp=drive_link), the timing mechanism stays stochastic but the network grows to three customers and three tankers. + +The third customer requires a different type of gas, oxygen with a draw rate of 0.60/h and average delivery journey time of 12 hours. The depot now holds 3 tankers (2 nitrogen, 1 oxygen) and a transition guard checks the correct product is dispatched. If a nitrogen order is placed and only the oxygen tanker is idle, the order waits.The cycle for drawing, boiling-off and ordering works as before. + +With product identity added to the fleet, the SPN with colours can answer questions on whether fleet composition matters more than fleet count and by how much. For example, the same three tankers re-specified from one to two oxygen tankers can eliminate the oxygen stockout completely if they are the most critical customer from a business perspective. The colour features also allows for modelling of tanker spot hires. For example if 3 or more orders are waiting and no tanker is idle, a hire transition fires: its kernel writes a new tanker token into the depot with a rental flag, and a release transition destroys it when the demand clears. (This is included in the next progression where the colours are evolving (dynamics).) + +This model still has the same timing limitations as the SPN: exponential journey times and discrete boil-off events. + +### Add continuous evolution + +\[[This model](https://drive.google.com/file/d/1wV2aqplN35FruUVtgkJKzD17FjbTBCyC/view?usp=drive_link)\] adds dynamic colours and replaces the stack of unit tokens with a single token governed by differential equations, to model the level of gas and pressure as real numbers that can fall or grow continuously. With the inclusion of dynamics, the net can now model scenarios that simpler nets couldn’t: + +* **Pressure building in a tank with relief valve cycling.** Boil-off gas fills the empty space above the liquid, the reduced space raises the pressure more so a fuller tank pressurises faster. When pressure reaches the relief setpoint (8 units), the valve opens, 0.4 units of liquid escape as gas and the pressure drops just below 8\. If the tank is still nearly full, pressure climbs back to 8 and the valve opens again. This reveals a trade-off where a fuller tank means fewer stockouts but more wastage through vented product, resulting in higher supplier costs. + +* **Realistic durations from lognormal distributions.** The dispatch kernel samples a journey time from a lognormal distribution and writes it onto the tanker token. Place dynamics counts the remaining time down by 1 per hour, and the arrival transition fires when it reaches 0\. This gives each journey a reasonable minimum, a mode near the nominal hours, and a long tail for delays. + +To add further realism, the net models outages in supplier’s own liquid production as discrete events. The production plant can experience an outage, on average every 90 hours (frequency exaggerated for visibility in simulation) and takes 24 hours to restart. While it is down, deliveries are dispatched from a more distant plant and increases each journey time by almost double. + +This model captures the physical behaviour that the simpler nets cannot: tanks drain smoothly, pressure builds and vents in cycles, and journey times have realistic distributions rather than memoryless exponentials. What it does not capture is variability in the environment: customers always draw products at a constant contracted rate, and pressure boil-off does not vary with temperature. + +## Truck fleet maintenance + +SDCPNs can be used for truck fleet operators to solve their maintenance problem, in deciding when and where they should service each vehicle. The service must occur early enough to prevent a breakdown on the road, and late enough not to waste maintenance capacity. The maintenance schedule must ensure deliveries are still completed within the agreed window. + +[In this example](https://drive.google.com/file/d/1hZnao98az5AYhOKSX1mf332PkxV-wwDn/view?usp=drive_link), the fleet operator has 8 trucks over three route classes: motorway (420 km, flat), urban (180 km, stop-start), and mountain (260 km, steep gradients). Loads are posted to a freight board at stochastic rates; if no truck collects one within 10 hours, it goes to a competitor. Each delivery has a time window (2.2× driving time): missing the window incurs a 30% revenue penalty, missing it entirely (breakdown mid-route) means losing the load and paying for recovery. + +The depot has 2 service bays, 2 technicians, and a stock of spare parts. A truck that reaches a wear threshold is serviced (5 hours, resets to new). A truck that breaks down at the roadside needs a recovery vehicle, a tow, and a longer repair (12 hours, only partially restores condition). Both compete for the same bays, technicians, and parts. + +Each truck is modelled as a coloured token carrying 19 fields of continuous and discrete colouring. The key mechanisms are: + +* **Evolution of three wear components per truck governed by deterministic dynamics.** Brakes, engine, and tyres each degrade at different rates depending on the route. Brake wear accumulates 2.5× faster on mountain descents (heavy braking on gradients); engine wear increases when carrying load; tyre wear rises with road roughness and weather severity. + +* **Breakdown as stochastic firing driven by the weakest component.** The roadside failure rate is set by whichever component is the most degraded. As degradation increases, the firing rate λ increases, so a truck with fresh brakes and fresh tyres but a worn engine fails at the engine's rate. + +* **Road condition evolution governed by stochastic dynamics.** The same diffusion mechanism that models ambient temperature in the industrial gas supply chain is also used here to model the variations in driving conditions that a truck encounters on any given trip. Two state variables on each truck token represent this road conditions: road severity which multiplies all wear rates and fuel consumption and speed factor which impacts travel speed and therefore journey time. + +* **Driver hours enforced through colour and guard.** Each truck token carries a running hours field, and a guard that prevents dispatch once it exceeds the legal limit. EU Regulation ([561/2006](https://eur-lex.europa.eu/eli/reg/2006/561/oj/eng)) limits drivers to 9 hours of continuous driving, so the model includes a compulsory rest stop at the depot for 11 hours before it can be dispatched again. + +The model helps the operator understand the interactions between servicing policy, workshop capacity and dispatch rules in relation to fleet profitability whilst accounting for variability in weather and roads. The main simplification is that it models one depot with identical trucks and generic parts. A real operator has multiple depots, mixed-age vehicles, component-specific spares, and demand that varies with season. + +## Semiconductor wafer fabrication + +A semiconductor foundry processes batches of wafers (wafer lots) through 28 steps using shared machines. The same machine group handles multiple steps in the sequence, for example the same lithography group is visited at layers 0, 4, 9, 12, 16, 20, and 24, so wafer lots at different stages compete for the same machines. + +The main trade-off is between throughput and yield. Machines degrade as they work, but taking one offline for maintenance backs up the production. Deferring maintenance increases the risk for breakdown or slow accumulation of defects that only becomes visible at final inspection. + +For [this use case](https://drive.google.com/file/d/1EBTDhy2ffXbIaLOuozMas-sTVhOMYTOr/view?usp=drive_link) we modelled 16 chambers across 4 machine groups (4 lithography, 6 etch, 4 furnace, 2 inspection), 3 product types (logic, memory, analog) arriving stochastically, a capacity limit of 50 lots in progress, and 3 technicians shared between planned and unplanned work. + +Each lot token carries its product type, current layer, cumulative defects, age, and a customer due date. Each machine token holds data on its condition, particle count, hours since maintenance, machine group, qualification level, and batch counter. The degradation mechanisms use the following SDCPN features: + +* **Machine degradation is modelled as deterministic dynamics** that accelerate with contamination. When the machine crosses a threshold (default 0.85) it triggers preventive maintenance. The breakdown rate grows exponentially with condition, so a machine at 0.9 fails 8 times faster than a fresh one. + +* **Microscopic particle contamination as stochastic dynamics.** Contamination fluctuates randomly but trends upward the longer a machine runs without maintenance. + +* **Per-chamber process drift as stochastic dynamics.** Each chamber's process accuracy varies independently via a second diffusion process. Drift in either direction from zero increases defect rates. Maintenance recalibrates the chamber, but calibration is imperfect and each reset samples a small residual error. This means two chambers on the same tool can produce different defect rates even at identical condition and particle levels. + +* **Defects at each step are sampled from a distribution when the processing transition fires.** The mean depends on the machine’s current condition, its particle count and process drift. A clean, well-calibrated chamber deposits few defects; a degraded, contaminated or drifted chamber deposits many. Defects accumulate across all 28 layers but the number of defects is only checked at final inspection. + +To model the dispatch mechanism as close to the real world fabrications as possible, the net enforces all four of the below constraints simultaneously: + +* **Machine-specific qualification.** Not every machine can run every product. Each machine carries a bitmask encoding which product types it is certified for. + +* **Chamber-level recipes.** Processing time depends on the product being made. For example, a furnace step takes 5 hours at baseline; analog lots take 15% longer, memory lots 15% shorter. The same applies to lithography and etch steps. + +* **Batch processing for furnaces.** Furnace steps require loading multiple lots before the chamber fires. Lots destined for a furnace layer enter a batch queue and are loaded one at a time into an available furnace chamber. In the model the furnace only starts when the batch reaches 4 lots, or after 3 hours if fewer are available. + +* **Dynamic priority from customer due dates.** Each lot arrives with a due date. Between dispatch events, the lot's priority escalates continuously via dynamics. Priority is recalculated every 2 hours based on remaining time to deadline. A lot close to its due date is dispatched ahead of a lot with time in hand. Lots that exceed their due date by 30 hours trigger a deadline renegotiation, and the due date extends by one full cycle time and priority resets, representing the real-world practice of agreeing a new delivery window with the customer. + +The model can help fabrication managers understand the interaction between maintenance policy,chamber calibration, batch sizing and in progress capacity in relation to yield and on-time delivery. The main simplifications are that each chamber processes one lot at a time and lots cannot be split for partial rework (lot-splitting). This means the model's absolute throughput figures are lower than a real foundry's , but relative comparisons between scenarios remain valid because all scenarios share the same simplification. Modelling lot-splitting would reduce the cost of contamination events by allowing partial recovery as a secondary effect, but does not change the fundamental question of when to maintain. + +# BeyondEnsuring realistic simulations: guarantees and probabilistic claims + +Running a simulation shows what’s expected to happen under a specific set of conditions. However, simulations don’t show what happens under untested conditions, nor is it guaranteed that simulations which are run are in any way representative of the real world. This section covers two kinds of claims that go beyond individual runs: structural guarantees that follow from the net's topology alone, and probabilistic claims built on sampling. Other parts of the SgAI programme are developing methods for reasoning formally about complex, compositional SDCPN models at scale; here we focus on what the net structure and simulation-based methods already provide.With these constraints in mind, Petri nets provide us with the ability to make certain additional claims. + +## Structural guarantees + +These come from the net's topology, arc weights and connections: + +* **Reachability**. Can the system ever reach a specific state? For example: "is there any sequence of events that results in the tank being empty with no order on its way?" The check works by building the full graph of every state the net can reach from its starting state by firing every enabled transition and recording each new state. If the target state appears in that graph, it is reachable. If it does not appear , it is proven unreachable from the given starting state regardless of timing or ordering. + +* **Coverability**. Reachability asks whether the net can reach a specified marking: the exact number and types of tokens in every place. Coverability asks whether the net can reach a marking that contains at least what you specify, while allowing additional tokens in any place. For example it answers the question, “Can there be at least 10 failed machines?", without specifying token counts for the other places. + +* **Boundedness**. For any place in the net, you can determine the highest number of tokens it can ever hold by scanning every state in the full state graph and recording the maximum token count seen at each place. If the graph is finite, that maximum is the proven bound and can never be exceeded in any execution. If the graph cannot be completed (because some place grows without limit), the net is unbounded at that place, which usually signals a modelling error or a missing constraint. Conservation is a special case, where if a set of places always sums to the same total (e.g. items in storage \+ items in transit \= total inventory), then each place in that set is bounded + +* **Liveness**. Can every transition fire at least once? A transition that never appears in the state graph is dead, either from a modelling error or a mechanism that is unreachable by design. Deadlock is the extreme case: no transition can fire and the system freezes. + +These checks require a finite state space. Once tokens carry real-valued data (continuous levels, pressures, temperatures), the state graph cannot be exhaustively checked and these proofs do not apply directly. Extending formal guarantees to models with continuous state and stochastic dynamics is an open research problem, and one of the reasons the ARIA Safeguarded AI programme is investing in this formalism. + +## Probabilistic claims + +Once the model includes randomness, it produces distributions rather than single answers. An SDCPN produces probabilistic results differently from a conventional simulator in two ways: + +* **The randomness is formal and inspectable.** Firing rates, lognormal durations, diffusion terms etc. are explicit components of the model specification. They can be inspected so a reviewer can read exactly what distribution governs each event. In a conventional simulator, stochastic behaviour typically lives in code scattered across event handlers. + +* **Rare-event probabilities can be quantified.** Some failures are too rare to observe in ordinary Monte Carlo, estimating a probability of 10⁻⁹ would need billions of runs. SDCPNs support acceleration methods ([importance sampling](https://doi.org/10.1109/acc.2011.5991305), [interacting particle systems](https://doi.org/10.1201/9781420008548.ch10)) that exploit the net's structure (strong Markov property) to estimate these probabilities efficiently. + +## Tooling for scalable oversight + +[HASH](https://hash.ai/) leads the “Interaction Paradigms” technical area (TA1.3) of the [SgAI programme](https://aria.org.uk/opportunity-spaces/trust-everything-everywhere/safeguarded-ai/). As part of this work, we’ve developed tooling to enable not only the modeling and simulation of complex systems as SDCPNs, but also AI that assists users in capturing their understanding of a domain while ensuring information relevant to a model or experiment isn’t missed or misrepresented. The same AI is also usable to help users understand complicated models developed by other people (or AI), and enables the precision editing, customization, and hierarchical use of existing models. This open-source tooling is available at [petrinaut.org](https://petrinaut.org), as well as integrated into HASH’s semantic graph platform ([app.hash.ai](https://app.hash.ai)). All of the interactive examples embedded in this post were built and run from [Petrinaut](https://petrinaut.org). + +# Conclusion + +As we’ve shown in this post, SDCPNs can be applied to virtually any cyber-physical domain: from industrial gas supply and truck fleet maintenance through to semiconductor fabrication, and the other use-cases we’re exploring as part of SgAI — with our own work predominantly focused on biopharmaceutical and chemicals manufacturing and logistics. + +Each feature from the SDCPN formalism adds something new: + +* A plain Petri net finds logical and structural failures like deadlocks + +* Stochastic firing adds timing, so the model can quantify how often events like stockouts occur. + +* Colour makes identity visible so fleet composition or product routing can be modelled. + +* Continuous dynamics replace discrete approximations with differential equations for flows like gas levels, machine degradation and contamination. + +* Stochastic dynamics helps to add the necessary real world noise, like environmental variability to the model. + +Not only can SDCPNs represent these various interactions, but it can quantify the frequency, costs and conditions that trigger them, too. This kind of quantification is what will enable us to move away from a world of weak evals and assumptions to one in which AI can be used as a real-time decision maker in safety-critical, cyber-physical systems, backed by evidence and verification. + +[image1]: <data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAloAAADZCAMAAADPPm6HAAADAFBMVEX39/Lf39/j4+Pk5OTm5ubo6Ojp6enq6urr6+vt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fnd3d3Nzs7n5+dzc3N4eHjV1dW5ubnPz8/b29zS0tN2dnXl5eXQ0NDX19fMzMyBgYCdnZ2AgIC+vr/AwMC7u7yXl5fY2Nafn5+Hh4dtbGyVlZbZ2dmvr69xcXHf4OH6+vp0dHRubm/c3NzBwcGCgoJ9fX6/v77CwsPU1NS4uLiwsLCGhoaRkZKjo6OZmZltcHmbm5nf4eN/f3/h4eH7+/va2trx8fLs7OyxsbLLy8uampt+fn62trZqb36Pj4+Tk5KkpaZqamrZ4emKiorFxcWhoaGOjo57e3tfX1+MjIz+/v5iYmLy8fD09PPHxsbJycnc29nz8/H39vT18/GcnJ2srKzh4N61tbX5+frU08/39/b29fSrq6va2dT////s6+nQ0NPU09Db297j4dvv7+308u718+739fD07+hvb2+EhIT09PL5+Pdla3ppa3NaWlqnp6b9/f329fJAQECNjY/28un49eu1tLL19fOsq6r28uf48+n18OTx6uD5+fVxdH75+faoqKizs7WXlpVzcGuIiIuIiYf5+fPy59T8+OpiZ3Smp6jFxcNlZWVQUFDo6Or689zk4+IzMzPAvrq8u7ra2txGRkbk5OWPjo1zdHZNTU3179r89eHo5+Whn5v7+/eChYrU1NbPz8w4ODhcXFwrKyv07df17tn79+p+gIjK4vT168n27c27ubOpqKX379nSzsn16sTMycT89Nfj5+rr4cCbmJL678xfYWrv4rb36Lzj6/DZ0snZ5vDt4MomJibm2rHl4NKvq6KIhH3L3uz46Lqxr6nMxLvVzcPf2cymopry4aOblYWgm47Ux7fp2aLBu6vZzbshISGUjX6uppXIvqnUwqzn2b7g0rS8s5+RiXfn2rgbGxvWyarczqgLCwsNDQ0GBgYAAAABAQEEBAQHBwcREREXFxdcYnEAAABuVRI0AAAAAXRSTlMAQObYZgAAgABJREFUeF6svQlgVdd1Lrz3ma5AYpQuxgbMFAbLQwiOkIy5nu04djyRpIX0JXZMhjZN0/cbkTTua5rktUlbm7Rpm6R5SR07bdy/OJDU2EmcJsaOAAlkbGyDbLABgQYwV0ITSLr3nuGtb+19pquLnf79r/HWOWtPa09rf3vttfcxb1ty1+rV05fcZd1y5fBlouoj/e+eP37hlHff0nOleLdtXymqLrSvJMpl8+lpClHYSfjFZI4CR5EppdDvsvkUej4cprxdSuRw6IlkhKY0J/pNTIlLUNHvPOR05trvt8qcs2JyXPJELnFZKKD2O3/mE/wSdfg2USqSb+mp6JfiiTLoOV/mqTp8mybRURLkSZ9Zffi6gc+sll9vqReilpzviMCUfmAG7PjSkp7HFClDsiF8yX5ScoCCDCyzIM9DdgJZyY/JTlAK2DFBRgbsOIHnySg0KIETRSF2OIrjiUQU00BK5OeAX4SOo7xT5o4kB6xSmshXl5zT5JRSFRJEUeJcPC5nSKZE4spSjHEGqSipzN8pl/Pyy6XTUeJqT5F1K54/8yilOBfUWFT3phFF4apNp5ROLiJ73nXUoTr23ifk1/s79q6il/+T7EOmHzddXOGco/RTxETTwSsVNtBkzakUqbCBVZYSe+ggCG2kMucQoR9nFfoxmUIjQCJKWeZRunESFAwhk7ngwdV+ihlLhy3LXLqpstCDyZQoa4uTT2bmG2A6GQlMR8Vgh1lJ8IeA/JAm60LoABFPqezCYqh0LPilMldx08VgpqPmS5RG1W+lIur+kGjzT6FfkbgybxqTd4x3ZjtOUZcNuG6FpEalZxkYUggaBBSOuoWEYwQiEAbVmTCkLwxDarJPwSQ9+KYiw0ECFBNBVHLwo1EkAjgqkdCxVNFM7k/CNykeMhcyUBlQ0VBjSE47lLb2M7jTqs5J/HrMv8ljV6UEMmUfUHrIzkCWigMpVGh2JDIhkvYjLlEMREE8sKMzZ4oqok8xEZqrJy65r4qtQ0suPhIXXCERGaXnIoLMfvwWlRMUjktymuSJgFDhgLpGVWVpsqoHrmjwK4kxbg1VFvZLVF9YIYrfuEkoK8m1wtUX8wvm0blVEVUrcv27XLawf4CN+Str53XNG+sy39Mxv6tOduae933wStyAJYHMTMWbF5eKy6K7XZJPI2w41dZh64e5xXnHTBi+0G3N8cL2ZCduT4SNO1NUd7o4YQPAQef2yfHY1wq7LDc5kcNWSKek+oEucjhMwlyiDLgfRKyiieKSE9uSM0j3ZAwMbmERlZzJYQaJYYQWi2ulvIh4xHBIdAEZ1ageyKoYMZl7afQGpmM/1JPqehErUUvhDdzgXaI9ULfhu+r4mkPwpN94DEaDFSnl9u1YNnlMzLNqhagTy+r6SYD6jnSpPwlyCCdYzHLgQUr75MWOK0vnmWkjdJUgTwA/aYxRKSXtF5PNwMMAhOMlkVcYxbJ0SiSDwL3P/JaSuYBMDg0OFOOdM9d+ycypaf0oCsOhMIqqlTDzUnlKopjIBR0fxUAvEyQdNb92KopJAcyAHW8CVKrIL5NtkSB7URRbpKMUJPudJyU4wg7Y8UhEpDIPcWV5FK/E9cT8SsMoPX9tfQf9dwj9W4iWjj4urRAXTBf66QrPFYFLE1XNGlQG9dTxahfIgCbxJhWEJtQpwsJACmQuJAk8eR7NTJdbKp6Ua1j+k1Nd0pSm2VR7JA89b5m0lN/VS8MUpmAy5Sgl2yABZBnFmsBiKR+MlwKPOCDGZnCa1KhXVunQkpJXT1NyEEJ4ucAI0DcCWZMjx6N4V8z1NGM2ccB+dUM6tKzFxIoiukUzQ/627a2gAWlBiIxxvnCu1vnJyw00AF6m25xSEEwb06VYM4UHOvnNqFLFF+emh0WcbIBBkGss4SFLv8YslUzTpL52iYX8qOTVbtE1LLdojbssKijKTFQt6rdmyEUlUx1e7pUymBzkhWeFrYpxhcyoAo0yv9SK1YNREUMexDkRtpQuBreUgOQPrnA5CNUYikHyMjBncr7kjEzXrRhkHYkJh1gtBIJbSt6UFaK+Q/Q9KZvFo6ty/aL2azKFJxVsCyFfCkEmQFsI/yphxjhK6k1hokRKqQRDP/VOhX67dIVOQHORBp0oeIJDBZnjuPxUDoujN2FUBKwhY0Fq5akeo3eGZ2/HWKU6TL1NrPt3qF9MWIl0U2GB8VJ+Ou3Ij0MnWIn5VbWm4r5tZaWXQhGMX127+sT4vK7+13yRwOCCZ81wpg0FGYaNnv0TyAAcpKCSwowcmEPrNyQFVBbiiACztEfzjOH6jslOSfiWURIgKwSllxaSHeAXlB/wAEhAFRxohWFUDBi4upGdDP0AMFS+UeYKkEAU8JuCUZwISAwhNI6QANGMpD2VS+xHkxyXj6Nq2IhaS+B1MM2wmLE9EgFaBquaX8Y3KEwSp7FfDEXVYiGsX+ClsCwBerMuFPoKREwKrIE7XShkJ3U9sWPC10QiieaMKjLgYaTiRuAyWkPozFV/oEDsh2IQjB+dN3msy+qYVVcvWnL9VgH5RjNt2QT8dpPz+cnkGNKTPmZhagJAh3RKRUSBo6Nk4JdR5PKUOAoQhfIjEBNkAAs1uvIdwX4hwBEC8ECkkGJ5McwQRrFTDqOizAluqqGFnxHqvFLJlfPLECtKSRQZkGlUNhGQIYpnVarI81Q746UIRjme1MUQgGhaG+VVzCXUDZ6nfn+bzCv6Jcin6+pEi5j1pJE7TfW1t4UHLCQK/rKDv9qhjhwCHOX4qQCVooRkkyZgTMU0C2OxipTiKBiucRQeHcmUYp7wxNgEThSaA8RRIj+fplGX+VVSqZynyNH5x8RyDtSLAhNhMZJM6yGdKkaSrPx4EQggFlIqRTEjv4oZUAMEAGSSgSA3AFcIB0QlA52xw/OCgn3nzyViJ81TOsp5yKo/VPbD37wQHaK+7n6jv76jVtzHqFeHmlDhkJQV2J+YcFSgcl4isp+qn2QDlFdpkKy7sB/FfhDburqREvf70C/qyQy+4wwmNI6p0bJyoszTxY8qRJNlJb+IosmplLAoUU5ETpVc8RQyVjbEosy5gxO/QMvcAHEGKYfJ6ELn8+OXyj25LPPz9T/0hzhK0g9/AeNFR98jBONrDy3rr+3/7jshU24OBnFJ0Ia6iN8x48Zw8J3BYZTLb515wAlErCQzD0EpJ5FMMJGcjqs3AlK5JtMFOckhBzx/lIk8qTefy5kuWzLuxJQixsI/YYUkMgekSfFbkaf/QmXJ/0pLVciljBzB+DvGxua1ZMc6jseaZKV/EzG2Y2Qa4V9G+xryawUq93XB/0EOQMQwOQ1mU+AwhMWs+o0hs8bNicwJP4TaUNZJh1pJjScRWIdm5zx6Suh4I/SZ4DcC2HCSfgC8gFaMTCVKpBcw7ERFZCcsoubJTfLrKU19CJI5XJKxdEohYxqv88hiT2SOkYzippC0Kp2m6NUII3+ELmMsmYEMA6rQE1sKjcQOosQlR7eOM484SBSDYfyY6LJEn6itF331reUQ9O1hW9Ivpa1zmQxF5nmiaDLAN9V9BLDTuaQzMM+rDa2UQQV+bUOrEQmTJxV8wlQaT3awgEkmVzGlslx8oxK/1JCaXxoKpYz4rfn13YTf29RhmjFejbAqNlyNMLnSdnqY0sQFTIVczkOO/ajhw8VCRPZKBOMfPZ3rrzPE6br+vr7TYoOE+sy4fDpWr9T55BXhTDulWMzYRXJmQbPGvXQGWhmbQjVVPgQrhbJLpqPm3hFTzfVBrVA4QirdHpzV6N7wG5tJfhAW7ijnC73dWAjWbqKRrmBUkVeCru/ODv0uYbUn/C5n7uhfnQgzGOPU6V/NqKv8JCswIX9m86gjyDJ6NcsfCnYOzQGAUzeDxiRnYOsiGt4aekVduJGKcU2iGKpqKGuVC61zmSdyJrtUIMZ+NZZTLDlOsXCFF6YQF8NkdQn9q7Z1Md6b4cqhfyUuhgn9qParR3E4yxmGbpcrFwMLgTyTepENGFZ7jhxW6s7xUYXkNxpiqfFqHTqYbgC0WUJYSx2Nn69ItJRQRbzc16yO1oRocEZYxOEZyk/Im5QWlRpwLKPYMWdTOe8TLX2PEIyv7Xiqrr72u5CFJPCOy6Jlua5lHaHyGVAGW9DKI9t+IRxpuUXTOO4F6rfotG/YQck2bWGSxGI4LU30RDtT5CHoGSTRKTlVt/s9X5SgYL7ctyyFnpeqByN4/QLbL7Dy+QWzVAoMA1sNJcE7LYHM+EULfoeB7rme9zCb9P9FJs08NPh8c4FXpDoG056t1NjGpLDbzRJMcQ35itCdkqdD1PxFx13T8hFvVsniIkr/aUcapRJxOEc6Sk/+NA9xhgJS5eIu9wwZ9SPXd8ixAH+5sMuskoVCGEcI2pQMaXvuTNNDSqXSS6ZbKnIiF7uqcbyi56EivdLUya7aqBALi6pCrH5sHKDRsZVrcal3WZ7hcFtbpvR81MWFKz1qCF+YNZQ7CmQZds5TzTlrhjBVz+gsUGugiEXssnCv6QxMND7VxTmLEzBF0dBrgTnLHO133NS7GP48KVRve8tjCUOSZiUtRJDTVZ31HY+K+vq6TQzj6/pO576eAmbvgFsZFaZAnA7GTqTjTSRYMYmy5M0JcjsWwjbPPNrPSCVXKSUv9Rb7laPlAGpIy6/IWMyvmQb1KT+OQuMp5ZtOCYAyzS91SDeVYGi6o4kBUFk5T6m3hB/9rcQTNXqCB2Yp8V7GEzkebAxShZrAk+XHUUjo+chAB4kyiGG8VdtSX9cnRL/pAZyyxKR/BsxqZAkCw2eHGhlOCbAEZKn0fuUzrWp7NQED4ER+1BtSVnPlURJdKSPHDSKnlI4To8SwBOQA7ExIaWIuDoS749HsBWyidoGNYuUoE1JKbRxPMNlLam8rZ57gV1YAkEUR+cXaUOiBM6IsJellkrZ55Rlocok1tEKOq1yUGvw8PCk/M711XVaMEFcqP2qgIIKp2PlXGRC/z19bj73DfotmOVFXW4dVVlIb70W9TE8/sSOoF3o8S8EwAo7PHZ4V73Co48ERcGCtQkLB8GEdVZ5SMoPIj0YL9o6wQ5jKHAwmoihYwj7gqVJKUq2SIjLCYywTTAzCrX12KjFGxTJ8z0ySrThKIYRrUWjFdKWUmOxrGMUUrGsn8qumaRUlFZrFIOZuWAdAQJI3dhWj0OUpJRyUt4LfxCgmlcBL5QKUkYwS8qSiaKSoHLxEKWUJa3W0zHrS6KunvtUCqFqWKccra092sB0HkRr96DGE9XrmVdOvcjyqchNDU6ekABmEKjtRVmEGHrRcE8kx+xFPav0cksPQXGjNarKFuOlE2MFDsgpdXkSEBhup0HDMcJ2go6ilAUJjegmrO4oSBcRwi/24fMpvQufnp4mheVsj0gOr0Eolocw5YtVx6KecqNorMRYu1DgRpVxM5hIrxjHOokTSKSWS0y+sjRd198vbxKwn76wTfT+Wyak6MeUGam87PZ9Th5YlqF5USDUdq/k2Ob9PgABU/VyeRKBkAkEZJlJkz6hoaBwnQE2szKAifo0ijNS4URKsVOYpTInkpEUzB5RJ71QMGGCyX5gAAnHAt4nCnQQWWxPIZVGidMviJt+STaLIxDw3iR9zhOd0LhPUrQyxELaCqUf8Du1hkEqJXhkNJhohNNm4Z9khsaxf9Jl3ZefVLRvrWLkzNhIIlYda08AJp/R+AW/4s8ovqYjDWIvfWCeIvh9GoQZ3icxHE4RScSBJlpc6AZ6VJ2gQ8eSzyaPSDpOjFJLMgRJNBrhU2j5E0WmyEbRWe4a60zIFH+fCDYfKktD5hvyGHCaiYGQrP5cVvqHtrdR6VRUFr1wzcS7MCOtyQw0t61XxHuei4/HgRzziyUQ9Ko1nrN+EsIwZw4BEPQvdJKH5CWQPJEkYRQuViCdf8QuegoSpB3yx7YXcEAVZ8pZIqnGZOyw2lD5WQgNAcxVSyu3bsXpMwMqUYPwhwPgYgk4wEiDInIB0EyEzdE8qCtscJGAxK1DTBg9vj3GVatOgtXAZma1bGTiaEf5NIlPT8cujxLnEsDhBLvlsaMFRTKhXymxZK6YU85uorInq1gpRaNZSfpEJBMd7+ygx2YRxOQ0xNiPxU1Em1iGTY3vghPmJP9H8pDxz83wtVW7OkfTjNsdCJGArUyHq+41+wlp1hOLvDjt16OCvdowEmSqftwASAdR44xceBhNTwpiIQpf5pVLSDiQtD63yKGHaKpcwXzhQN6gXDsAgMPqlXzEQBSYGuFhGCyhyfPofUSmVEA3+1vwqSqjfrBzFj/1MpZcFOVItsTMhSpyS2klnR2GyCRloJ44SkQlBCb1pH+O0ylE0l5ocxH4C5/iSti+iPBe0Pv7y9rToe8RcLWq75o1lH33ZMa2in5lxOuOa0g0Ms7ZAM7fnWbLujG36tCi05PRRru9AnMu4UAaa5tjiQVpHUWhZM27YUrgUpTDZtxDPfc9JNCAOaBQcStsjZ2oRFCKfrRu3oaOW/sXDpucK2/CmLDrjYYPSDa7MY3mCDK7vLfoULfCyRc+3DBKtU2i96TNjC0aCEk1DnlE3Kmj1BjbO2ZQ25sYrj2Q8NJ8QNx5xM0aRoswaEx6tKTwzN1rAIpE4nHfG9OnBlNPOCHBIWVUVwu3MOpf1E6ZXD7MjFOOsP8kXHjLPjmJio549YqnK8rzxKovEoGEVZ444tHKheP4FY9xtpT+pAP6oe0y/pN+l+MINJhdNLr4Ql/fpXdmlwwiOCWzWuYDPVIlRioaUrLGM50Ex6pnnTJ72fHF5cZSKgLqvLajKMmdVnTMNzrzacy1mbETCwtJ1zLqlpwVSMszqcUO4gFjyktOWjXTdmQVK1aNWdMcm+zzY5NTVx0uEuaS7ZtyljCknOX/EV1Pp2eKkAPXnBtNLgtZuklqjMEmUpEWZz7y+4+kV2ezkm7Tlg+jbmsKeKRBXbnzAcFBhUXag6ImgYyIQEgDAZkWbAo4JgK3yKYOvaWRaQbVJZdR+RgUEmYyb4lfyYsCAQgNcaMYnBmKyUtRE5GQgFo0VoiQCqZUKyEbKviKKUjHz8jWEhu1hpHLTbQWio8xxdA+4h8MKQLmYwyT3iD8xc71MkuC8oi1kpcnTSKGmZLof1DBeHbsQ4nTGUjsVPjusS4gUCugwSYUCpeBqP9brJsPyZlcsZgPoJmKZKVTVMgUBVCDtRA86NFWVmjeYLKBnwfJBpQTAmvglXmQ05aV+Bkkc/RgAdMfLdVcxHWcO4KtzAYuocxPTPuBYxF06SuxgNKm5ARHwU4HUb0IR9dTIoJ9qlw2MQeG5j7qcxxteYWwdjxE80IfWD2hszcbHaBDFc5R7GC9WIGgKL3zQsTWZ1xdaaVRJlwQ1kvYTvPlVUo7JI0H/6lqequvHNpeC8afrW13oBItoOzhuEBkThxbFlQyN0WPd/woyZQDoxoeZKg+JCHe7SXKpxPpqxpOGEg16+94uT8mrnHmlM2RmvCAI/dSuUsgTrenDKEWROKxeid8UueTH/MZ+5WueVBGTax4YlFhWpWLEufAyCWRaxUTrhCBxhK48SjmZ48Ulz0SspE65YWk2sUZZ7SxwIMmMrLKVNj4B40/XM4JNmWdGQyTq09pJYnJ2sD8bDn8VpjxKOiXodtLkCWA2IejiRDComEK+CcPoclbjKBiS5ZmrRUdIFuGOf5g5+7E6I4rCkVQUqB/KkgtY8/iO/LIfax7DtQdSjtc8Mavh+oQdU5MV01EG6SLirwedbbyuieKVRUkqSXW81GIhkvEwDIpe+JfQkad+Wl+iMxAxjA+xVu3XRQo+/JbYRQdgTJQKlIhSZmsZkj1MtEzWasRUoPBBvaGoCUTHiC30Cxh6mGlMVMZvEG6tKjLF95PZlasRlV+UgSKXcfjf51c9hzHTmWO6S/MUqjZ1LmUq04n8ngemcgYhTos5rKjwnVghocKXFk8T/KIokZXpYadm8ljt2L5OPV/zNJ44sIQOpPat4YSBfEMYbpl2kftt+JaIokhRckwWMPXGtqMmw4lVmwwBVOg4CijcZeGXVEhG8eHH2sU4c1Y+RyUDhZWVntK5YpiCrP3QnrosOnMIqTBzqBBTjFXiF8HKKovlEK+KlUSK60JbkqoFaRgFJrW6LOgUko1WPQG1FDxgtJrMnMsskpXFqavMITUF7wuSUANC0ifVQn6RCLJCSsxYuohqtQ1GwQcfvmSVQ1hEPyxinDnIkZUpOpho2Xvff8HKtEgrdT0d8658AkdMPK8VT/O0jkjgCK1XPQ+YmJh5GZhgawBliqBB1297hv28FgoxXgLKmWhu4MdAZEIu5yGHfoAzyo9XYCag3XlKLtWVSTG/BZONfjS/MFpNRik/PVeWOWolYUhrAhMBpkbq4bdToFYma41y2eG2BE/RYbFaglx9s1aJcJrWPVG9xA7+ho7Qm7Q86gONDHgWDxPhpwhHRMnxCwBHjE34Fg8VpTy0djBWZLh9Gq2xeGhhzfFOB3KSyfGoUhSB6YDJEVL0IqQYFiyVkhI5OqUyv/ORlV8KzqAEHl5MlICnlApR4IRVq0uuyBCLYUqoZATQrUH16ob2grAuxMoS1rqGgWWmqdeahiENmnJgcm9gKa3IoR8czpwziBuQhVXIGFM03ypAzJPhYXv6UZHjw2KA8QqK0b/Zw6Jocaip1MGlS8LlI2u05cm1M7GhQU+eBStaJHflatyKgca52EX/Bzl5k4BuuEJY4tobzEIRNb14hsolEJQCzDiC4OpqhoX0myJ8fezxAio0shz9iE7Jr3OJzjc1XAlhj0RuirD1NdxlKd7s8YKvrj+4sVTI2AVaH99U0C1cv9A3DUz53mK3ZGJdHNSNSGnjiGFQSzOO4cFmBufnuMZQDMSTI3whAvVo9+rAEnxTw6BnGsWCYbrekOUUuA6vGMtIv4goF5kZtv0uXQ57FVVEbm/D8xaVMN/g9Ny4XfRh4yln2tTSXCuzGFZRl5o9pu91qKUeacIwOpjNDc5F5Aanl8tcmKJQ6WbUhb1ziqXVFtfQvOXDNGXWuy2kTVCgSteyFCPSsPmqnunQ10PLIK7xfcVqddiZmmaoJ2op1WVx54PwXY+Yd2fjUgiMSjnuiIDkvm/WHNbpm3eMdcjOWfM7qoZNmjxFfmWeug/0uH2TMZuSVNm7tJPGmOtZJ+ad9gBeAmPYVgpxeeQIhQYslr2O71guLdg83ybBjLHypi0cQ0IpYlmu6VJT+9YhyAsqc/6Mo9XYeUa1NLflDepQUDD7g5M9C7UtjLMYmJSlPf245fOZI1ysAfWKZx2dDLhL3kftEjT9vucsPOpmYClm9Avc2IUMTghm1AiqT8EaNPCsvsuOSeXXWQXQSCmMXfGWLQvQxp/MBA4yD+Rx23WK1N3cI1QMYsGyjGOGQQmYwr+4BxaBBIunuwKICamYpQzVlulNG/Kw0U8xFvQR4IW2+80avvHJCE5YLksaKQckq7/5vikUC8rcUsBdUIhxj1VZMuizBZTpgVPwAHnQHRb2CKosLqJkhbhhvlEF+216Md0Sn/+hiipCmFGME1LlIkcHC5QjLisaslj5T85MD9s3VCuUD5qVSK9VUaP71Ipmle+iNdwT08e4EJ4/WuAObvil8UwAPCjkpDEEZfWXgCggQDb42Y6n78uOiVp5GxaKdf89bTysoEAOUmsUnUoYiNFeKl1ManG6SOW8xs9xSmZ6KQgyqiURSD+wU55c5MdrHR8WHIBrtfOCY7/TLa7/eU1Lb403qZCKxLf5vQ1HyDHmITTyhR1IqjoqLKaSOn9OsCz5CfmkUooU+JocauNxA08qJU5Ev4mqs6X5F9199DIx8EzR3lXtxxq9coVbDLrKkJeCovAzxm3NWCK70KhGNkN2dQjcZSqUPXYYuLJysCLAS5OpYJVu29Ih0+QM1ElxAiHyL7/itGJKOrnzkAn/mqIoKmSeOtxmr/75mqHfvfTQfBbiM0bGftlbekkq4w9Bosywz3evVLIsTIbldWL7I2kBUsZv3DiJlGCJEeWi7Qoyb2POkc68TEMbZ5665Kw4+8rM8mVTBkRtf23/8WUH/716pNtLdVmZ6AEBAEe8ocQOAAqLCNWVBAsEKUsxT3yXKcF4aOMJxp8W/0xS3C4BZEuNd9ksOUQxDDYiJyKn/TTFM2GMFESGsMqpHJeNOSf6WUF4fZgpGIlVyo6diZbBUUoepHTAeD8MwECFeVHI9PP//nD//WOtPzgyWdjWB+oeGStdvOK1P/7MjPyLu2fuLiIK1/D5Mg/JprrfJSyLyuBtoiBAYlUAk3DNW1kUJFsxJT9FDtjhcgJvcHJhBpGp87LVK3vmHz9w7u/7LPtPf7Hshe/6CxePb+qdL16b/fnQWIc7izqcGL0bgLTRmUUcDsDCIL4kBhM6CT9ZdBQ7sZWp0m+Jr6U6p5Zt+v23nhA9Rx2OCUWOutQqKYAmyHjJm8acAOYSlTgqLh0oeoMxYIoVRdZ7rz7AIW88qElAvu0Es8h/uP9vC+7xT7PY7mj378DZpv6f3Laqd37VI0/TbFFKMI5+E739tnpgqc3xy/lNpctHgsr9BN9FnQyE0qRSKsucOYz2xnmzV2LrGgG9oOGz01686IuZMxv6cA9DRz396cgd+kndyvpLar9/8DgFFzZbD5+vpcL6TcMPXmnDLwoUbk///6iNJzINP74sM51S9PZbpBSWqlJK5VWqo4T9utx2IPIj6BHWlYrE5Gkb1rz4712313WIPYFrG4W1T98rOuof8+84eO1j93bU/6Jh3f43Hn/HyUj7lR+GiaKkWUnwS9R0MZRfWd9ksmeyKn5iycPKCqsnjqJzicwjpn925b/seuv3qTuhgMeKNsn6gm0s6FxwLYbV2PXT/qTmRSEskRxLyCTiNyi/JgJOwmg6Jkfa+DdWdC07NG9s3yk+khJpipUtqoSGlS2KBVhkTQqTlTY9UsEKtibAHyiYWcbFKfkQzvw20TZZkQNMmNpP5QKltg+BxtppnZIC/hFj6HmJlEL7Yy/KJVTEc6q4niJxh8KSv1r8r9YLHat+dfOW3mDxJbfsv+pc1/6XTt6425hene/a3/PKyOGpz33wE8XZc05C5Qe9NTtK/c7LPEWWrLJHbgkyq8ENmFdFJuEIGvObVHorMl81jLrgAKwjR7XTTAMFenxBhtbGhxWJqoqqJ0xOpa1mSPK5fsWjjX8+6fjRT4hH9w4vmbtjqLj2zUAu+OArYsmxa5/Y3+s819B9pOqPbj7Y76L3qFuDaRFpkzD1bLMYsONIdlxAN5DhwJLOKScv1Np48zNi8tjkffn6fSVfrXMNdsLIYcJ2ImEPfiUm+xZBIZuWnTrhwHOZnEwJ6ttEcopFw3cJJIdkI2KflcsgUzzOPJlSmItKSeooqcKaJZHIJfIzyLE4X5XSPZvdL16abxPd1lVtG3cPdmfWbem5c8b6na3N7t6unk1er794zTz/sd67prYN+jAdxok3OFG9swN1SIANSU0muUFkWo2zH9QK5JjojTiT7JpWyBOVpazhDCPBr4PvQXC1U0VLG1pzm3KyaZx6vP/Jd2XjjADqfkJLqToMWzHzuw3f+cm7nr55xbdfXbbsQHd3s3vyUOM6t+GRxu5u8437btw52Lxv+beOPvHR971+BvoDAwaGlukKC1sXyoZRKZjhYOZmsnTZRED7ubgSRpGzNZOpQ8knzTv2ZUVf9bnsSx6sCbDXpZRFyrIXlKlFrAiQcBEWjiSphYl9dnXsAYo/hCZZYbssFZnMiUCQMi8UWPG55gRNdSVwN20sYjHJvo6SJHPm1TSgWOejGSNniou8jGRhPRvn0SnerNGSZ1kCM2FUFpU2cln8129sfFfTFupC6EwtPYFc+Vq3mJGjJz+3C6KtSw68ueuNz174perPL35tJOxFvK0NnZIWwZgGLN5LtaS+J1OaBTQLLSsNY2bRKJmGZ8mSaRWota2S75quJTEnuKg+ZuyaHiNqOAZKEhsyNGIt4Rf4Ok0+rsHFgGNTw7NDbWBbBYxi1MH5usDl96//0WOXGXvv68i/JYbXtTa3ta3bZXS9tfD/lStndAtxEwrd9tZg0NT2d9n/ffgcVyozRrlB5MrRyTRqkAHVg86luoBcCJiNVGntJO4gcB2Y8YoFWSHyovqQ+Z5sbdcyma39DY0yCjXdoOHCw3BqgWY3WLxOazhmwILZqMm6WgeY64YhvzSMledGbch+Cj4OyY0tz7MZpSkVNxxlSU+UC0dxHNE0vbFzaGsij1zxFqsy3eBsdQDupDt9yjkD+XnBBeNG0fQyNDqphVzbt9yzUwtQ6VGzjmX0FwTMKtclbEFcXEDjFWa+gTluI6LlGk3DJdS4l/FcyBIsWGeeY1Ne36mb9dDW7VZbW+PcrT1ti5bWPN2YWfpc953uwbaexs6TbYuGFh3sXbT0lv2NTt/We7usS851GBZErLl0EIOachQzR9X85F9zHCfs0ednjmMrxZTehTR0YBTqeVNLLrQ5bnDdUT9jlDw/U3tpPzSnVPLru11WJgtjdMTgzVC/xjeKtpcJPK9gsXmwb8ztt2EP6Ju5XpYn9LSy06aOXAqMC6YUYd5IomDauJYGM/ts6fiEgIyqgiEdWXKcob+46HBtZsfNKzrauhfnjbaFP1840Lbo7lcG31i09Lmej2eoDhozBz/+bFNPd8+m2uzdb71exStA67ojkiftYOTqU6zT9vx5Y9yPpDvJKuj+19hrAsFQlZQyMNegmrjg0C1dnfXL6+oSMB674ik8lsSMkcYzBo4Yutryw0xoPHlPIwr034TxyHwiTxFjvHSNVYWIp5eunFJq6aoXNpn6P3Q/9TvbaZAKecfpXItoIw4f2LxRfMMP7gekNx/or90sH3jILKw9eEwuyLbP+OpDR9CKZmXuU4yfb/mU1I1iOMSBypW6UaC0UjfhxyuVVOaJlipbQN95+/YLO5JFbMpn6x9dlT8mg/s62knA0BMtYAKUM1glxJ5vXvDVjpLmSK83zrMuTbCdXpf+fnR1276ayV2jnTX7Xd/k+Rw9wSYpbxshWqE3j+QQJvUY0tHkE0MsTc4A4GC4JW9VdmMUKBzBOC2MwuabROaUTNN34ecrTKQMqgBwwL2peGL4lYBRDFlKSZhppJEXATKLAtgSl9syO2s+vfX5u//Nt+bM7ZaHT17143VXtwX+noV9T/hN6/ft6W6iKfIlf93O1k2ESO5x5hzoNj7Tet/xAWyCKZRJst0G7IMDSMgc2MBwqKcUmdCgpf0UUlTXTAfJYrBGM+Y3LItGXhGopakEX4mAH1W9aSGlgKdoVBGmGTPGwVFKF33ymb58Ww/2vlav/sYuvynz6j3bM93dg81Lb/xO90a3o2uw2T31+qI10w/MnTOnrafbbG27SxxTUz3LesxcNP8KQl8GO65gxxbsKD8/IltEHm1Y0ZHPZ8ei7ekged4KjoDDyys88QJE+Skr08oKPr1rnSazo8jhTi8SMcB/GJomSd5rxmzHFL66M7IkjVLyExnEdhASDKIv4iXBKoQp0saQU343Xv2TSTWPXiX8HImLRbe3BC0tD5CgFbMCmWvJwZHKwcsjRBYPtFzyP//wCzZfe4T7kTwYg0f24+wYvBhkSkg2cVIoCG3LWcmEk4Q4G0UZRCa5MGw9b2Wpl1Dhy0s+TS6RYxKsRHuGZ8gSUULnT770P2rr65vGGhcE8qGW2wORy8pHZK6RSvfItoCK2EBPs3yx9nQu2MPX2T6w7M0fXn0rwCPN0rCNF9gNfccfr/DDXwUr08pbcWjucjEbBoqUL2V6SkC8aGurXJCmyKEg5VxA5gmEQ5fND9Gbj2pNRlHk5LGUMJeUQkh9Q2vbdxpa9gRNOTg0UdD0h4mCpr8F2T1SNgiaFvI0N0SzxYJs/SNS1H5168+SB5EmlE2Gx4BSZG39Cfw9gV/mKSRz2LTfxEuQ0lFiP0bsaTKrW+Xv3fVH72ozoceSUVma9hi+vK92c0BT4xFpNPBk6ctwstwjV9Uf/YPbqkxBCxC3UJXiIZ1LGoyAzNAnrdeq6+qU8oW3+yQBrBERW8tIUCAcWBUjER6rGArk2eoTFBLn24MJCqxIHZM0/Uz7mfrEO68/2fKCFUlhSlh8RqG1I1ixgwUDcwgKf5UIpeVPYqjhThTrvV/5k9v65YyTx2Z1DhpO9UlhLOjr3VVb+rkRLM0PymBO/q4V+er8gByq7g3uzpOzNP+cIRqX7179sSeTuZaXzcBCmRWUSTKqD2QBBQVbmfLskFbPgV+WN8mY0IrzRT8V6hB6LpWSSi7Ua2nWBDoa6um74h/fl83MyewdWriko+ful8USKouz1BmUQ303v2w42ZNiVf6gEdxRGpS7PYcqJEt1MCffeuB7c9uLmJuwYOA1GpyopLoC0JHQJ5LFgFSNrEyNe8WyvmX1dbUkrxNHg9JG9ojMMWMK7nyJvZUCQmC/Cu2qDQNT4hl/2cFfyF6wFZFV5QrYL9EEaEKtqSzYMOlh3tMpoRbD0InIPE8qfQcogq3tMBpMzkUCrNH007Ch+faHOra3+V+sWyv8tSSm/U7REBzMNgQyn6WA+WOPUTHoKZsP5PN7ycGLzJ9uf+h7j1KloF7YEfHJs8QvqhH9M9SdE+gO6CaYZXhjFrdf8G2J+ufrP1xELgHqMEYBEZkHuskFMoAn+KfnQh5CHIDnDfnP236cFY8KlAuleTTQBWPnB/d52T1ekLt2YyCWZam22rOrPLHzUhm054NDH18xG7dZQVNXoQHfrhXxNzwsloTxMvlJkyRaDmGmBe1rBEGtGDLTmkKTtbYuUwGZ6uQICLM60NdkxvIqpcAWjOVLsBjX0FepkyB7gOwZ4pcxhgygizQn8GubBe1HMiCwF3/p0bX71m+Rm7wnXmptXv0Q9KKv96z3D3T3NLsHuhu7Fh0NGpef2NPduG4LwdsDATl4cRu2N3affP9LBck6CDgeg3qCr+xApaX1WuxYOGxHZD59LCxcm6nJMPJy8bUYj6MQng/RvUHrHSi2U5UV4HB2sg4NrJUitXKg9KoWh1YwHqCf/Wbf89czb/2W7Jq58HWxvq/bF1yW9/x6QJUurgfvie7G9V5399zuAROq1APN7oe2/9nkg0XJatyJS5YJ65aSJnN5jFII4y2G8R2QQ7AUqCRmYpmBsQQEzhvrWAOHfpD86kWwdiuOMjE5iHim4LIwPxZBPAb09W+JzM3kdXJKKKWwvNRXm/ELY3nmLso3Ykxc/me7dl0Vi5QW6TcpvE5wA86sptxDEiBMwXhC8OxH6J7IYvf9/3HviEUYRDgedvv50jootFgSRZfWXXZ29EbxayHg3Ch20LC4Pnw7PQvOq/RCDpPh3Cg4JL1dTv7G9aEfSKDolE6/uizfwYX2ZTEsOSYJFC2qLF1e+jfpH259X31/03PX5Q+aHoqhy0JiNNdy/yMte9ql7M/tpSKSRGsXuTYz37SHcD4Vv4WczgefGgxKtCIPsGJhxwNaoZ6EGQFkkgPaD9p7JmMLMrAvFWIvviiGDwTPOj2rriP/Jol63z5XZWKIybNVo9W+7ZdIJJ6bROVx0GpjUyh6QThQO2mcN1qlN2KHq2AVBkhXM6aVVGt2RrDSwrq0YCEtjQpHp2g4OF4z4Wqn8TCfkmUWnGKGGJs1UKThX3LEiIW7WgEeiVWdBDGq8ingRlf41Z6yhO8U8YEms2CXhO1bU9wL978PmJxxLKPWI1KuygHBTwSzTN6JDVyhlT75u1fdMdkVnuOa3rmaEFfbnubBg59YOPj/PHlk6jD9J+hfNj8s6EGIxfSwOC/U21QBP/0QOhRoKgWP/HTI8IFTGhvyhFsExFYNNEnX2cgUXqkQ7bbnPMMu2a4Y/dHMx+v6tq2ZsFJZldvW6TXlrxWs3FLo/hiNC5STFzBK57V11tItNkyDqVoKU6jx0BojqsnQua1wYaGu8afHoqMXGmMPaBifXiFyKNVwnAo/wMFEXmFxEu7Tn3dZBlJqdfNbWlRocplFhc5cjd2IFew5lKUUmucgEA7hwSb3EfEG9KOVa/ox0YAq1dUNawC9VBQLj8l7O8RekBvG2k8IVx1YKFuyqcynTln5VOBn1NEj11q912PLPJrrsH0SnuCBPirm31TGfq5VdHhksGVkUWJDjqJgx8NTV+UL+93iRD+somjapelo7PbnRIB+VGH1Xnd/zxPl3UUml4rtNF46BVE4SFhyZQcBv2z7d6z72bCxyBc8CWyNlOdS3ggh+YuHTtfXikPm9HzN5LGO7LfuflVNotFWsJOc5kVST6lUm0zWECAFcPzEPr026+BNIXYESU1e+PFNcTiBJ3yPrfL5i9fkCDYUsIDKmSxcLBO5rwasGMReM6NBvQlepiQtg3aBzfjDXH394z/quTkzt8dvbe6fvHv3Jrejq6l3zolXF818dXj/qjltg0Ju/PGG1a1nuoeHRvf0rlq3Zbir0RkcaHa3d/fcMf3A3APtG2e9ICzT8xPb6ToXBUWXXf1vhrCLlg2YbvqdRLcLVNcBSVwcVMgYam8L2r9Q6VjC3WVF36I5wMWDS9jQy0j+qG7JMwgtwvjB96pM/11j5wquU8LHx23p2j0UyMcn6ieW/NKbv0I1v8HtDubeeqJbzL11lwga1/8AALKbHWAqHxRf4UoFLv2Bm/YxuKQAh5b+qIrAIU0ZgetnzAjDlZdcF19BZwnyNTWTsx0nxp807z+B7enOW/7NV9vT2GAEMom3dCdsemKPLL0hmtghlqzQsiW0eZAfE9S3ZkKrW4JZgCuVVhfmP6GfHUTKXiSCSR578Xp3nUTk+TJXZA8faEQH9sIN+Sv+4OHlK/Y79fnMgOnmn6Cu2hbIldNzJ3o29PX4Yv1WETTNOdG9a88Drc27h9btMpy+HtGYe23I9/PLeoIZsw51i9zWD/+oRmDjOM5cc8CZT84+WYUDNNSZUCgJZSYu/qNegwOmWLDiXDVJPBvHLzCgEBfHuiggHL45xKJ+5tKKr4qEM03kuOgJJ1X80vHhe9ol9c9SpcyTPJ39+vYr7t5N9dUte9rWtTbP/waNYyqQ9HK7TCrQoDip/IRkPz+v/QLll9tldBnBpt1juBXfqlC/6f4QkoXg+ap6AaxM578n0sbLUBsffkLsvDpepdZJ4ObIT5MtHGYi0aUuUpCQmmmYmXAiclDhKgig7UpRksoHnh/jKPijV8EYHFCx8b/bjzR0dPh7ftC+1ngg1041nW+S8nRuWzvhWArc8gA+cZwL5AMtwcPBAy3Sy5JIbfvG2mBTrjNHoU9Loyk37TvbphVLJNs9UzmecvBSsmuWd1S5uE6bGUfVSHy6ANpkQ5q4m4+mPlp2ST4cyJjYwE/iM8WGCW/ylw7JhaLDh4pc/qAD/bDeIX/3zSnXO4WSnYHxSHllRRUSXPHEVy+s29x0TuSF0dgggodQOi+bCwKC8Z7MZYOgoS0vg8YHGpUfikh+tCqZlQ0aG3JY4az9kfh2IEo+rvpUn3JOHVbk842CNSiKbGKyYXJlbbzE1BnDBwAjJqWRTGKm1cAoBXAEaoJqtATNC5KU6jbdcFb+/3pCxoeulY0alcLf0NvpOiWZSqJsO330cWzTJvaevSaCILlt4gggVyCb++FLf+Db0l7u26LV2MX/8/rmlJY8PJoCMHRXzyuRDerb3c3IZHU3Y/LMszHOF+1Ly01ar5YZtTa0kcCzsVlOAi2swXLcs+jB318NcKj3oRlGHSPMGO01rMofNZjcICLkxWRW11OyUNc3//oLn+K1E1XkSFWqwRIVEBpyJ7pJysq0JSv2HVWjTWkkBVb06uKH86ifWYvKOnCQWW8MMkkKqGklVuesvw8wufohtvLxMQ/JD9ENs1rpzk4A20K1HRdhLxXXRosiisJlKJW62TXSFoeMQzut+A2Vq8TAPa8dqTm3tzFz19ijnjPQtmhJ5qBceLBtYKh0V3uPsWjp1v1z8oM0Cjt+Jhf05Qc+np/T3b3n/lk3YW6kKaR9f2Pu1PQzxrueu+cp38FRSz5vCQMsR5lMEeo52fiKDc703gQqkx0gCyZr1TyT0QwgB+oYooEL9n2MB0Pf3afMi3A+kw1a2LGM0ennCHyiigWfNUsbj7GwFOKhf79oT9B464mnAgZZcDa0Lbn5xbl7aOaftXx3sL7vpCdOLpnTlRnN9gZL2z2xqnqnYTidDtT1J4Uc6usVbZOmdvY5QphFTzrpBkv0BwObIPF+AMihNp6tTLMd+fpXi1S4hPkqvvzyNrANTgxmIzJsQ3mPn8m+y1JMbyBZAN+VUwpzsSXrPiOyMmqQase/LPPI4IGXFnFKoSo29AM/mQ9/e7ao3z9n9LmXxZzRQTn94IxBeSZo7Fo0zFpCpUZs3Qgw27B9ULzS3fvxm3bvfsl3+lvNhvVbjIXVWwcGzIYDN9fvPBcDUa5vjDHeAVv3E+BUdGZC4dQcsLDKUIXbjE6hF6JhQ3XsYlb0gp2feOM4LcFoqrFNQCwH38/ysScnhUmAFYap0LLCKQXCJJzmmMt7A/6ajrYgU7VdpsysuzY/cst78j88tXhmx86m9Vc/saupe7fozqw/0aOUpG27IiXpIJX8RPcmL7fFbG4940cQn5ydg7/+3k/HCfX6gLgJk5jK5ieSzYtBjqxMp3cAdeUXZI4b0DIbZgiZLQic88K2NKRLkfUCD3UABA7wjf0obOC+c0raL0WGZfCEKFiF6ijKajOMEpETUeZ8PFvz7E2nDpxZdL/XPrDKOSbJGRTr/dFBwus0BayHreU6tjL9lgwW37/LwMAV618bDBpzLT3+zFt30hort+uwed+vikkLGT0wUKUXvDXm4fpAAETYhtr4ChTVObaGhI1OZ6EvmtiR93zjBz/Yet/3HXXSj0QXNSFMyjA9kuBXW2a8U+Xjhly0jYs558LTUpYElPt6pVJeWYH50BOD+ededi5d/9QndlLB9lERaSFCT3u0De26XfRCT+t9TX7rVpTcgdgGmf2IkrMG+yTMDirlUtYkBFtCcvXoz4YWrnztFnnfspb6vroO0QJrPJEJStikUg8sxjFD0dqxAKEL3RUuKHH17EaTJQJocjwBQ4PDEAAjl6tO7dS4hrZ14ETKzp94ArfucTwEjCZ2zoDJfEE2LOd0njok4qZS8jlfnRKHnLXiHFCF0l3lHiLwFGKMBP5QsCRofrh5s4IeuYeFjNFKlkItyD73yB9gxa/wkh2VheZuf/qVO5BrBJUwUTNeon6lEYG2DtDquhd6fzqyJQ6kPmkEB8dIEQCX1ijQ5VnUxIS3ZlzeYvL2EafFNQYDBNUaaEBaFHz/kw+ycce2NIxSRYRei4pITauN/5hMExkXWtVKQ7uKcuz63pcTFYuKlLAeiPsD0Id6Q3OyFWZg3lNH/ale9Bl1/bm+OlGfy0HOW6XpZ3mVIQJ3MlqU5JY7dRwXHNA4G60m4U9dT5iTMthzp+68aBZu5sEaodbSn/WxcoHrBtChTjZLPMd6RhFIKBBVwQxoNjAArrlU7U9YchZwAiaLlQN8HwflXYsL7qVLTtGA4p+mnKm0+MC04a2m6T/gb+DVBXgiwTAF5tK8rz6MFS1F9aYVeMxTiYrE1Re+/bO99M5btTLfYsoWvWkbeHty2JlmB35ZKVsWtcCvgQI2SkXW+7rt5Ew+clnGcasnYVFou5IqycWHHYlbm7jJEMjGJ+Js4eIGZNjnuy4UMRCtgn3wATnGoOK2sX96c4pZKpQcp1RwMohie/wdPlQk16WEzTbawxT4tB2l+8tpqAUQs0soUxf9dZJelbrTxqRhVB+x/ga7rtuY6Qbc0oeC5eEE3tEcitgsVfFzqviBNFWhsz7IKCfXxY4vPeibS6Zj4WdkPJmlsY2tJWvBogIXSPi1/K2IEgnnSTAukFS2qq3oT6IPFiuHRG2H6MfuknDtwRoF0F37HG5xoU5oDleh5ajtJp2jqQ0TmzdekKg+KY/2O67n0Ajy+0tVFkf1dqIKYBkxSoiIeiRx5eAWDdyUepat9aj+fvOSUmG4wWnV2zz54lTUGK5M6WdkiO6TKYJG9TasLmeV5i5adxvo9bIP/Yh6qTzrw+YOq/ipACpU3WII913ABAJm+mLp3msX7u1c2HC0zd/Y0Lm3YePezjbVj3SVwsnR0ODqpiep+pHgmqZaJL+c8pMHb13gi6EShC8V8RxfhYYpEAd4YPCHbz+6JXxeyjBsEz2lJEpU4yXb5l6DYxouKlZ8NfdU88c9k3qjZ0GNAXUEvj7J/c+1lDrIk0iC6sl0RAlT4TDsSovU4946Cj2AQf171EdPtKQ5PCUIBj+3964NoqGwW3WPdpxmjgweUF6QW3w9jBq5+I1R/zOiIaZGl1lVOjTqWFi3yiBfQrUHsnT8KM8qaKkS26tTbYxCpNHwCMadZX1UIcuEubrj3Pyx5ZPHXsGuvekVGZ/RxF6y8U0NYRVgblj0QPapHKJoo/ZMCf1OEde2uLgGCWiuqJT0Fit5gD8Qmcau5zH4hoAXRtHgfXpcNU4BCGDjGBeF1mejDCiYORGJT1LCWBgphTBepYSPRIbI38OcADLAbKUovmH5t5+etuClsU/P30UzYW6nWN/S7csEYN0yeLJh3ZbWTR6wvPdEd+smkJ2DQz1KVe0c3L3J29o1vBFGAr+44XgXMtC5wDCaeTKKH3mmCHRqwYCDxgEBV/+aU+g3OLRiZIq8Qseg4Q98CuPei3c0+xf/GvsFagXkUUXjM5e+ZznQqRKIMQ2bj3fQqkDmOm27Nm8jfmhtURQQZzCqpuyKaK7i+xc8ds+3etaL3pNnEkXcekatVE4eVdr4pnX0RJQeOMPOEudgd/Ougd6hhUsO9K5at3uoa9GGLYPr3b03bqcKpDW7DyUWLbBK0LopcKlMrA3dJEVo6qhxXae4Zuvq50c7Zk02akW7EC0tj+EaJoh2FsShEIbg4lMsTMHnIEoB0BzNgRYuqcc+DQRHGAV6KGzMS6YYEEpIMJUcXljKs40i+3GmoAiAQ502SZwA+7A6fuio26+142PNjSiKQt3ch42JNg1QaZtiUtWzz3cYk1q2GcE2sU3ZJrMTmSz7ygSCHUP7QbGpKFnY+nrSY/J1K/6Uv4pKY8Yk6UINjOkfufU4+JpfAH0AYXWByeq5Eqy6uA4gSy1eVNJ/NrH2/i+eCDZmbtxts9pU4ka1AHDFpFD44jF1UgJQJJQkpBY1zrMoalY6lklryvCuYsg2TE7MASBqrfh0vxwXOaoVlEUVcZvP5TRa1oYl37Y2Ntb2slzEZqp5hgYtCwO59mGSafkjVdVVVZ67hpyScDLjxUxc7VzJRlj3tHYi+eXi5pdd94q14lrenu6YRUhefDfC5MBQ+AIoovDFJCkyjluy3orxG743wfjNLmjzVQsIPsLkVDkOVLpA/kobRTgX6wnLh+YUifCKAZaYgaREBDAhEmHo6Li8KigoTI6qtXxgFno0C4YLoJugFHFwKwHjeWlB8a7veW2yBrPHIkwOp80IMS70opsB8TfLBzZvxNZ1jHEXHlNRGP63B3/X8/D4hCIWhHQmXflsApPTuAgVnQIkqD39Ugb33Clt6G/WUE/c1f/wjrSvE2lKw9DqZKDSTE89yTYXXGbUCNWCjxEdV9aD7i69RPEXZ9v9RqExeTa3ubzk+azQ6H5j7Id1zcZt9MSV5cz+8NeidRg0cy4s/ANsXaXrnltD8OrXaj4EGC+M2o56oK4OjGScqsE2DcknNnz0lRSzkCZNjz6mUogtkBljuIzmUHJ8Mww6HS2nBIex0GPckgRaApnlm8vXabusj02M0ECNUBl8VCdCgxayS2BvCkgCp2TvZ4kJVmTRkJaD63CwauRlQxEZREKSQQ0/nTs+9zMatR4NGLUSsDpGYzXEUnkFQiQGLA3qVbQII/LCvFBg9hjHAyCTEF4nVhRYXmDbm7gMyBkrcXlnseYBEgXtUMTCATILit0ilx23zlFfUbK9Saz+uPHgr3YyiHcKJVqVF1yThtUNim2ELhGk9QDQqKJckx6QIEzpqZlplvJ81/JRh1CfSmW8Nb+j7ejGhqPtDRubjrY1NLft8XQRN/tUFl+DLgPOsXaN7uVmja7IQaE3Z3NHgCuD4Drvm2zS7pFI8U3TgFqUNzpxLhWt6Nioe7eUMSxmgjj4+CGG8bVmhvrXWO2++p9o8MMwHYjFgUaOlWIARrYZuEGGUmWMIS0Tx4Ej8BNpPBl/KNNDQIDwoBNCEw6gHo6XOErBs4lMM7SLsQoEJd19GioVcBpNkANUx36FQPkBLjO+M2D2yafRMAACpGTQ7M/nqsLMDTnnfz/ywsINDKOCJkCPJgJWQeP6LYObfLXjH1sDRPhjsLlPkxdt8Lb6TQBkqwDI3GHxi8lxrTC0gyrXLF77G8Li1KMJr6IOoCT1MgSMaOlT5OYJALqgkfIyBL5M0dseHN+ryRmCZm7GGic0c5wqmrCUQfOr52D/DqfAfVoFUJa1NKRIhvgwmTMMB3cSUlJwlPpSjnziyTPCyw8G6x9e34o9dmms+5n0luWzXcZANwqtSse2DiHyijAnozL4tW469TS9nDrwieP9AS4KKPoZkldFy/JKBLlLnhPYtC4krAtNKeFZGuIY6GjAUfGzodGO+jFsPOhfONxJTNDYCfDBexpkkRQIgRE6ZmRgGlHYiShpCACHF8yYigFiccsolnYIbcAY3lHxYj+dEkRUMoMwFzXDJ/xoKYZJCty6yBwbXQp0ucbl0842Z+Ni4tQ4yfcEuqJI23KBydvTIf7QgIzEmYCoyrXQCpmcYHNu8MXb35XOXGPHOUXqQVSfvinwxOADdiC0jsDVkBSEBPF1Fp5gSCtYrrPIYT2JS0tKlySeex8xTf3Ng8qOP0cMhT9DN5IZebU1DDUMsIkHMyXeMdaHYD7w4IsLVrUfW7Voc7B51YKj+UWrgs1+w6a2o+1NG6PSmTAnpdKtDXGlKj77AaKxHwOysZX1dlRvMOekKca0cYUF5hKBXSrwTyOsaGFjEKXMtt8rcGLMyHWoeLwLxTXms3hHrVBbWdgr4v133GOBAxHRvjco2g+b88paJqRA1WXg+AR1VLwHfLOvYkOvBZTGJlwnJP3CNhN8+EJauJWDUwijoBNaYSeEKSm4Vd1ZO6z6UV12in91VDuJH+wZYPWQexhLDNSm2GzCnlfIh/OGCfWXTM2E5OeJ+x7OD2SOKOCNgsF234cO3pflMB41R3Mm1acUxsd4ZygQz4NBhQ6ok12nhh/UiDFs+BeUD7XggiKg+QnUaon6KRQmxC6heBMX05m4gUOIs0iSz4E4y/9MT3LQIxjHsvlVMrlSCeB4sNhW/UgNo7WE5VsoayOxuKH+t7alCXbZYY1Z6FyUYQmaLBeHVgSAE9c4rUzIg1923fsYjd4+QTC+Dyhe/JPCv5OcPlb0ZoLLDhRgJGoGZ2/foVTrZ2/eqdXPo1Uaew6vfAPGI4ZrXXKogL14gqh1AxrMZvsc/mKpGJlWBDQj9H/tb6AHoSQm56tNLBH9oKYAXEgLgmueqYImjSa5S18PtdDjGQ3jLzij9//HpoR6/eFqCovbcJceKfLBDJEZrnZVEu967eIlv2F4a/pbRj9JtRbCeL8R8HRjBVW1sgYItfGwQN0svab8EerVizrDU4mEf9dtf6tQo1cUlx6qfv82zFFBobrmSqonbaFAqxEPR/l9l3dSHRhs0LAPP5FZZJiv65CtJPwyvM6AGSsm2HrBQlUp8Kde/osavYYYes8bbPrhBDV9t714GrktfcWatngqjB4S+neUTKa08fD35cIKKxV22NSjnZc0nrHJ/fItuzWrl7yhl27XPFMddoQpusCqpbDlMPN3VIcS5mrReYLEV608gcFvlc6iaajveb28H0/g2Tom8SEOAq5dBqyBSWpX+wCN9ObkSTo4Lo3hfqhpaez4zojAMKUxOkrtbmNBbBO4CFjTeQIjFHqFUg0GLzpjEeo42Dx0TabaE8C9/RD9Enu+kEoSatNRlqskczNA8SziqigErgGz89BYIhFhwXYg8Fxz0Pj0RQdGsVvrmu96q3b1y8HS/IB0sl3ScLLOwKofb2hjSrAUeq6+/ABbAxjr+np9/5ZvG2JJDsaAzoBY/zQN/4XZbrE0x5BlIJBDHx06Y/E3zwz/dPC++fsHJgkcSchcdBy3uPIFxiY+qpchvE0gxTLIcQR1J9STxJYzghA09XFLOvZtPPsLxkd345SuRZIPWmNKB2cYUFeUEgEbNjDxJr08hWcXaqnMaYwctNSo8db9pSJhm37j8o++fKCpd3p1r5gzWr2uFVvxvB/vZHXxl+ap+Ev3UmVyhVTnB/xMtvfMIphAbNiX7W2oZguQ/MDGfVR8Y2Ewu/V4wIt7OxgSqHs3CE5gJSJhHlMFRR3wj21jIwiS2d+/TAJ/9JmrO0R9tnZy/5O03rDNcQ/3xQWsg6MVJq0JSsCh2nzVlLi9CtI5oEFpQWFmA98ALRtQ9/MTiR1AfHwZ1fWguVQwXrJ+M7JQwDGuJBkwnqJoiJ+JbR0o3Qzu1FbI3yjSPB/5xSmxUtdjdSs0e6Y54z+nfebnDrBAzj8zT1vodjOMn7lhi7GzefWWZetc52DboqX/efGMxpf39C5Y5vyMnPYrXi18HngdML4S8v/lhl8Mkwzimw4Ca6j1U/lgVFhG7qpnsE62bP5cMA2qswszXVWeRYwZwXWDlyzFdTVXH7XtGRf2SYdgsWVSSEwotuG7/5nJvX62RILcI9CPRWSJ7+bK+DzCaLLb8BLVe2bG4tP6jF7RtQN0cKxdZE33LdVHXNN1pn9o5zWd9z/Vs9Hbc6ZnNTmN67w9fMR1oHnXQLHRde8+cSCYuy60tTlx6y65YcuiDX2A8Q8RjM+hnua0DTa3HOim+jnwhyN7fKXOhsWF6+DDqhJaYd+ixo0ODxpFWXDwxUJbXt2ZE7X7smJeDOPjKbXE0BG/EgAkUALW/yGSmQiZIe0ABmLk7/FOZRQF5NCBtJtAZgiKjWftp9S0nkVrbxZdDm6lUoCKv4+h40WKW6V3UFmyQkOIkdP/MPs6z/vrTcXV28OiqV8WZXpom2yDNtTL5u469eSPtwzdfuT5az/gr33eXe1+kWFJOcZtAv5oyX3l5pEP3rnQdTNOyZ+UITT7vYH7b3Dlg41Xvgu6GK5GEzcl/OvMU3/1AauEzU1vx8grz2MN/uyo4d82aQQwyv3gRirYaDUf5vvOX7755b+YOTkwxqQ8e33gLi2MGwTxa9QWjkVl/wFWQt+Y9oEbv3+fuFFVZKyEru196oUVc/27/ubuwUlPrS3nXqzN+X6jaLzVefLwuV+1Lyo8s7mpWGzf3OR9mRDlHQ951/bnxpMLGKHjtuTO/XDbH79XbbHx3RU+7/lh0w13psFcCn6QRqxYoVEgd179mNgmOkS/uTqb7ZvXPyZewAAnqUfRBUlq3AbmgaJUyOqzs4InNUj4Ekl+ZagmDdMsWTQ4sZyGtPdwZT6usMEeCx/spCpQgp+WQBQfgpPWeFRfCM2fzlATgWm6+BYDRcGFHPRAwx8mcvfvA45yMZN4fMwWdnEkIjPUn2EBBEuwT75EGVTB9B7XXBHfUydNqxkJaq6b8tKpvQ9+r3rfEIGWk3LOvurM0Bx6kY0dqzK4UWv9ljpj5jXvXvSrl2/+8G92B1Mu67qjruvZ5quf2NU4NwOZ9sQZkmBbdzeuP7G1SzZv2fMPrS03L77q1zaW48XckRt6Lxw6eXjTz67Zf3i9edRQW08uldP98N9f4tiZqzqXvOfYpA/tfq/1hRn3tV/41+LNI8XHXxomrm9b1pWf/bkP/5hq+MGzJw55hY88c2Pdmlc/9tWfDnvHv/z5W59c0TS3fr+ZMUquDdUESeSB+4dXlU69eJIKnLFJYlgECBx6KWSm5J0VL/9tx/7DX7jz87gxa/fG3Zt+DO4H25p3t+1sylRvef3L//qnxmsffvFs1QdXl74+7cVNW/7+k/0jqwat8bGn25csxUVbS7f0NmYOnDrY2Iubx7b2tF00b2rDzI8tXtOKSREYCGdBMcoxkm22AyQ4CCtn6gqwdaRavnXuvL56Gr7mdBJdYkx0HGfzKhtWhBhyJJPpwSkJA8pM3nslfKmOlTsGZBSlI9mIDQYWAE0swEA1sJx2kaGE0hindPhLH/g8A5Y+2EKT1Bv54gcE4CgmMIVk+wlOCZFxL1hg7KdB4RCwx1khKe7DzGDjCtgAlwDwBrQnX0bGVDzwS4UOLiXsNiKnv9VpixuOvXLpMwOrnIPdG3f3DPZOX7pncNW61l5v7uiZQHafGn5j+AP/dPS5gT8xez5amvF7bxz8yPYdxsk+6oZ5mGAq6HFGOgTIjIV9J4P81Gt+c3jp8UuuuMSqmzn3kmOTusRZpzOgFfnh+z79Q+itCVIZUvj7vzltR+vMzy4orv/pjdsuvPyWoQf7N/3dL5e/4UzOHrzotPvJf9l354GH//SZ7w0dCdr+aNPy63f90v2T0W1P/OCfuxaP/PT5k7OPzf7w3778/e1BkQaSi3s7zEkf/s2pw4eeyVw6mMm8b0lmNHN5Q5d96Xt7ltdUeyPTX1v8zLi44dCPJmsdXqtGAE8Ejb2EvE5Ou6F/66m7dwz/3U/W7Gjed/DPP/XlvSebXr3upanX3W0fVlA0P0TOSXpa2uuDsvKvfvX+Z07tMM4scy4wa65bumzJQvEBa/ZQxsUXPawi9A1QsgNZ4qIKWulKY37dmJw3Rl1rSceCsa7ObDbantamuaUqE3vUBLPYdpENPbGPOnEfmgCQq5SrSW0o61x5cnYpYZydgsN7mmpjM7ziFPursFdV5+qlglEhIIMIZFXsOIHiIu/k7oMfde0i1k6KHcJ/PkYwb5kTMsg2+ZnOhqMXNfZPq5kuFh675eEm5+DQwqVbRaNDsKOREFZAMuvM4k94PYRGpi275SuP1X/2kYvmffdDPzw1uOh4z0Leu01oFxvnRHrV/dfKU+JwZ82pgam9NaP5qYelvHDwrJyWWXHKMZ/0oVYmIeO7mcFn1s157cidn3vga8f/4uklYz3XbZPPrTxx7E45+Sfnbjhg7Om78NKPfqluyXV/608adw9/6Kb5K3av/Uf3Q89Ur7lpx7qde/78Hwv513+/aHRCWwTFQOBNPUuZ3PCaNZJZOMU7OL2urrdn4ZTTPQvNY0PrXxx2a+65qW5g3rErfjmobf5Cu765c/ac6W4898G23m/Y7/vRJ35c9blXuotNx28ytt38jTkvHO6oFzcyFA13tLF1vR6Ur36h9tgNxzqdgeHa2T0L832zdnR9ZMdo17Q5w9hXizBy2OZFvh0h+7MV7X35rLByLULULWtpx1kJCzOmD+U2LUmwpgsxES+/GBOFegzPggGeQjkAYiogYJCOwnAIaj5YvILCG0jRzjTEFUk9nExJ5MLLjQSFMJyFO60QmT8QjVP/8HMdaMaw6kVAI7p3n0Tlew9/6nGz6P/Pf54lfl132a/veenZO+rveWrjQ3Jtiw9VDZyHJByxdttaHFa/43/1L+24+FXvX58YF3/w8qXzc3vX4oQ+JaagxzYKrY62t1q5bU3ypRvODPyyG5m/JYPbM7O6hXjv/3pm/37R95cBLTN8iy9rLV7c+cDF/7Tv1yufLczfBrw6IGaPt/nn6qbsbzrcGshJVxot/vDAtQOr9/jm7y3f+Xxv9sFx1+uvfvP4M/aPnnOXBZNu/qm49CGSzNC+YHIYEK0r7mjvoZo4Ffj+adi6niLySak+nPXaw7t+Pntl1eFrj8a4kn9oY5Hb9dZ//vHnRtfP3X/3wfGn3wh+8qHjZ04+RjPUkhUrvimg0SPh83DzXiPfsnazkdtsei35Lxud9z97x5euMY1R08nLwmu5/LcGPp5pdy95Wbei5KVEbJJAKCrf8NgCIR6718zMmtyRHcveuAsnw5JLYwdKb22iStXFG4bQtPAnFnHKFDeLYsOFUBMtFAGYTEQVsB7li1sxO0+weI3u9YWuQn0xJcylhK6njKaRJch4KOGAlYv7YS18VCE0ZyYgCbsIhI5Nlo2VH5s//B9n3xjprx30jXmHD3jBlN9pftTdoG/AjYxzZe/qVuGz4W6uqvWW1h9+f9nvXve+lieP333V67/p8U7yZknQypslt7Ya6zYbPp/k2z2j8L0HO77VTkhW4BCJGBodEXN6Dv37s5cXJzkvQT2CbRiqo2seuOPO6z/zsxk/+cpbv/rmuVmmdaqz48/fff+7fz731CvvX3BAyNN//m8/md90+46nCaC81f65ptsf96461P/svSs+7R9ZdM8Dc3ZdNPTGK3du82HbLK7rRel6rzb+skMU+GQn0DTVpg80e/HIPnnxW+bju/6sXmxvEC9aTt9JIdY/TFA0PmI46fFtI2vu+6tV80XN1647+/X+3tLNN733TO7ZKbOL1cuhgumGQqvbX5Yjx5/TJXs3zP/p+B0/3HgNzoNKf2jl++ZVDXypc+DmVxqfwo0p3K5oAN/kfWmmGO6l7fcerxY3C/OuyX3V8/Zlx/ayRQ71O97uxekAnJ6BigHQzRQlwHjG5KyOoicooW0kK1iE6YABcDWh9ypgcgs3Fmeg28mQfHEYZUP3Y+CkK46oAK9jBuaTK4z88QkrwvuBixuDkDYvH1yqXw8nZXyY8jL85y9F+lhVmLhdFxmYyxe+tvIr+OSW6Q1OPrn8OSpB92p37WDH7t6Fy/bsks2bewmPb+7d2Na8a8/CZW1tRG7fufWssWzpwcOPH//H1dNePeRvvWMXFu9Dq9b7Pbsa1z0F5yGx0V+3/VTzqcGhnrF7/g6LCKBWGmsE42841kf8Hyp2tFvq/pYijjcfPZJ/4u8t89j21803X7CPFA7vvfPAT+956qWDO3usk71n8Ympi3uPZ7cHPVRtp04WLrxv0tFnO8yGZ07v3+keqnn9e86cgZPBmePowa7spKJTHRSDXijDaCGKFVLAF3kYvlfM+Ne3ljxhr/X+4nf3Ld8tNpygfrLa6wkya0+c9NeL3Y1d827d/WrpmacW7s781Ltgz5TijqXjTwz+x5Tnj5+dueibewDaG9fv3t2LJUAvTYcnm73enbvv/x+9Y391Toz/0eI7e2/uOv1K5xUDbUtaDr+AfROMZj4dApnDekvInMC/LYTxq8W8107Ui1pYmRIkrj2d8dXtlHVnaXYiXGZMH8kA1UhjeO6oD626Ic85PHP5xlRRsviMUzBjhE0ZyLtk47IfQ5TqRvkK2MArWkGRzSLMf36SxVIgLu3MBOoy9FwXgIRrBrMGJCwHqMvWvwU9BLF9rgr3G1Bq527qZN16IGcNMu6n/j1UBShJU/XZDA4qUCrXn3nfs/ZJCmZNO/uZ4qmDVYQFvEMf877vLwLEygzuWrTEOXBqxpKtoo0pPXJ6da+8bkq2/Znlo/l8Zs7oubPDc2pekSerTwZz5j8hDFpNkjN/t3nViX0DwfIdBKe/8/Cx644LXG0hgvq+ZV3ern6CVgVn2s0HeNtG8JagtE69OeIQvBwygmO2X1UQVYds6/mjPaK6Sp4dxEf1Xj8m7N7BE1jTe+bR/3SEKFri+Fj+JK0DjnY4wSmSPMexqvFwbJ8Q6bhnXHBwEtUw7oIYmTcKZSsJgdqzmVzH6zTE3nX8d5+5+Pu9u5rWP9TT7LNzgIaJ/8Qusd5v39149oXRudkpr9LSzek6JNZtHZs1Xv2603h6u6k0yn0ZKFd7A3rqNlxatkhn2pJfVPsLb1j5cmt1n39kvHR0f6nzzDSSmT42HPz6PIFq06QZo0QdnyRJ4C/tap85vwNd647J/dlsx/J+DeOHayyTV7ZQkAJJu8FoBoKX8JlTDC91rY78hDaBCFw+4gVVq40vE2KrvBCeYaoyMalh2/9JhdB9u596Pev9zC4X0xzNIwUSUtTrS5ZxUqIuDXy3L3As7DJUvSn0ZU4jMoT4jtaUupZG/h9+8d5nBo7S8tEsjU+684ejtEi1ocSval2kldHG0nw2c2zpXuqaqMuejWkTzA1bhjeufro7s87t6Fq04QRQ/8GB5t3NtIpf8kTvUHPbyz6tAx43PrmV0AN2DM2+Na8PD5jY8bfdpt/4hC1pBSQcYXo4AoaPGXouziwSfPVwzbtbRPVgmRRkArPk2wriUmNSUaFNVKZXMAXA5Rk0SIAnhO8FGevqoxm7dvpp0yoZvmMXqkr4OmPGLBpFY83pUw5l0jd5e9F07s60tW5iY1lymtbDmdvd2t28+olId9q71FnS3aqvtXh14OMvLeoYRF2olcp6j3XEvIC5Zubp7FUdo+27LsyufA72s57p2u6oYVB/cKuMHtaOQ5du0gzBGvC+S3pXP18NGN8v8CGofljFSg3jfQAhmYTxoYoydHBHSOwHPae2dUhHCWEkj2RgMPWK3RzXDnD6mZcBLh+pMnmsKy0sEmOoTog4hPGsiuUbttO5QJnLL2dfuvxrLo55uDAnejCAhoTwb1BaGfIx8cd3TqU2aXOBaAN5bcseI8i1HA1amrcFfjbXJvyWjX9DMD7n136bBDX9w31arQWJXqN+VDBa3wjs0AjYz3kEHwU+gukBK0AMe6xMLfvBoBJFDTA38DKcT16AAr0hTOdQL89hx3QfSUuIdModhriYMVxDPF/Q16EGRfNYZ9CU3yxpNMFp30vOXtFU3/GNoLGNiiGNluaHCNhTgXItJmw6sq0dVMRViUu41E1c9PKQmP/V3lEzOC2c14604K45WNBAi+TgK+toUFX3fFZRGff6CsZTcb/eQrNhv+iID4thsy5p8ckHvjCNhsfDJtqGGnxYLLQNpfkTIVm3Hp3X8gA7MT0SPOJ5Eip3ThcOiTu+hhYaLuriuP9P8r0c2KaFHQCsU8s5JDyb5PDCq155A6dDUoeZArvwsWte6ODdWX1KLGhuafdMPn5f/wNDNBC5caIF6l6xqr4j3OVVlyAdWyiwo33DyydgCpMysYQSbvqVO0x87gkb7JBogY0PzaiSUkHYVtbn+6SwJadP4QW4v7d8HxpjBslzXJNT41sPfGtqz1SuFTgwpA3rkPe0SebZk68ebeMLnaJDX1I0tDeE5qQy9AvK97A5dMKPz+w3iI9t3w7L4kRLpTKnhiAhSvl6MfmeZYeW9deKQ0ZLfV8H9SzqgtDmw3DSY5MO3uOhVqTVnxpbfBMs3tlbQLxgs1oZmOL+EGX2Tn7/l7T3ALPzrO5936/uaeqzR7baqFhtbOMCKi5jycaAY8AGE8CmxMaEADmBk7hcyAVywkmekBs/kJMcIBUSEwiJCyFgTLdlj201ywWskWVLmj6SZkYaTd97f+2u33q/vWckTJ7n3DvgTzNffet6/2ut/1ovywKjGMODHOpkJUZeuZCP4JTXbBW2OBh3lfsq1xjsmEYTR+Ne5AaoDBkG1NQCGUtsRa7GlvyDwomEXTTvgWOkYJQKKKXT0l9lrX2p7tu2Bvpzy5ptX9q3Zd0WOdy755+yLXcNf2ndtuGMJBuE7Thbch6mcYY75fR+7RxjKZhHCftpvvNVm6a/4ocqSeOyh5/PE2kHLVDajKYMIoFBQbX1nAQ3bBbJGWY6wlanB25tPy5Dhkw9OPcZETxMYCuEqJ3C49hxabp1PKTeH/w/tg35g9MEEkfjd565K8kDcmzQV7ZlOKvRSbMt1re1Wmm3kE/1tNR8y14Yzfa0tEG230hb7D9ctysr2E28A9HUVLun9x0S8fiMeyeCCxTNlsk0Hx46ZU4dNm67bfE2qFjwgAw4wcp2LblP74EGknxBLOjp3JCkHGHeCFNB6W5cI7G5T6v7SoLmN+mcEnYtwUuUinGkQ8dJGAL85hFDpo0U6WjjDxwIfNf3ob4ZtZBxr/04rrXaxxeum/Lxe0jlPFssvVW+9/xDxb1H9961de8Xt6/94v4v1pp0fwfMp45uS9yF9ed0zOkFDUZYvYVgRctkVkags7ZhdKHa8ozLKDIYRTLNODkfvh4BYTEEP37RMvicxHnBMHKoJFega9oWtKdtU+qU0OElaqBGiVVf6NOKvn9UaeL4QO0gzrTHGG00Hz07Ov2d7d37tqzZ171/iw4mByYyVUyYGO1KUevotteKdi6JqtfR7nTXxt/+nMm8f0vrH097pVLE51M/LM/4mpMx7yRCLf0Mvy7UNFsJETj/tLHFaLBYodiwaWaksfs3DqD5y9wi6NdXAG7R8ixDweJ1e1pjlGqBQxVs71mFRxxv7iOCYzON5ZIzsqK5nCFxS0EeEehq+RUOFnd9E9QJYmbjasS7IQQJyvLsx3EFkNchAjwr71ldAX+y+zD6GCCZcFfX9Wuk3vMmB+u2im69fN/Y3XH4yypluR/i7l4bFbbVotYD3WvWv9Q/ruj+tgd292+fxoivhumXCj9cu0EQ8Q1vaXgYw6DOXY802nivSGew/XTE8Ge+OCZyQ6Usi/JXUJIxe46HAUFmnPaj1CuIUCVhZeIUKpnrhZBGUqqoO5TznExDDDZBINXwAxpkydYu4sgyy1FRSwcUYnyrtKHnVN7VsnR4edeqxv7U3Ga5yQ/K4SGpYuHgHkLhztyTPDywtV00FdFddudcB/ntV4jc8seTl94Y9avN0QbmG/kAPUWza3fVxoOepsU9gsU6O5sOtbrtrIcbm1u+ZqWX/ojYwic2Z+GyAosJpr8xwRi8yGRyWOm1mhxTvlHtxthGVefX7Burr7N/YtXPv4KAqr5JD5EVmbOP6DKg4lQ/rqeRXic2LIAAqzxPZbpC87XXfvZnS7YwHw3IFAFluZZVPqWuhIpaO45ptJSNCkOm2bkuokrmul7z9h/2/1iEiEY5EzitixjkYic7hVnQ0Zkpgy5gcyp8+Fh7WZ0FAsc8gGTCU2WA6qzvcHDy+qEPqVywB4Q9FuyYZBxyucyrMqqYP0D1a41l2/D760ba9/hwsedUsSMjPDfTimWwTIdFPntVOrOILDgd+1PEtuNVWaZy+g133vfMnHGhBiS+YptdD9r7+WltcX84u3+4eLDNOFt2jAy1mc62v1NEplAcBAlmN25QRmurwTZdBUVgkeT4LEhX+yPPZiC6BNFgr/mkpj/lS05ZvyRNKMIp0DzeCbM49jRrAiS4EKqGfYW+VmGx4kk+oPA4TwXh/MFTzyqaFGwWEiTGoqFwRt7z0DdfrqHYfRaq788sJj8HzLbPZj7ITwvIJ39CN8FiYP+mv/3wGLhVmyos503lmqC86tQMpmAtY4YOo0ktFP8SG5dpJfRvxfb5duNEicXkj5AnKyaEXcCmA3pfTc+Rd1qi56KLn9QkF2lSR0SZtj1eYs06YVL5wKrbP7tQS68bekjBqQIFj72akiJ/5Xkd2lFmZhUYrX6tVbqcGz7wXmXFchBkY8srA8sjy5/teO1a6me71o8Fxgt2F93QLZpmM2LaOoHxAhguuhrnnpFVa5HrZUphXlLKYdv0xbrSC6KcJBkiEmRBay4jvKWpnUJZxjZ2Ebi7GbkOpWexqBKEHibJAsafjPDowmKlIshUnrzAxa4r6vnSDdW5caFq84S+AN2ZD8WFAWBPlK9JkkE7aeSlq1SZEoyxTKZ383+MyaqzDReIKytLcYaUTmnsFPnnc2+t4qct7Vv3mq2ru/YmW285ut9svWsWeRXXAL8IUVdsQnA+12Q+yxQ+thqKvMz6ZPunpjLTLGWT1vKinZrfxHP9bLRu3BD0BrBS1JNERNnDFFXBloCXbNw92UDUaOIpkFLabADACgSTEZMEPEPGc5W0EawRVj6s+/EaG5bgZKsWE3CBj2JJ09Vr3rN81QffsvamDfNGl7+vJmPO+WmXKlpcuT8btjAe4aU1twie6iPYqqDrA9+80m1rZH8hQMb5uhuRwKyJxVV8V8xVvcg0ijSABlqsaz7cKePJnPLuPFB8VNbGLS8YTTy68eek6WKFP9WQ5xVdexoALTOwNHDdmqOiCkWWjwDK2VIZi9Q+mg7PI3KM02hvsh4Y72QBWqjMJ0j6DC7jvb7bWIhwbLTOMYKlfKf5RL5Z4MnX9RS4qc5d16VZvgpZWnItTptZM1CFdg2CS9ijLB4sqG3Dq5w/5HkbFr4os/awr4n5vXj5FPp9wU+mygXH3Dv0zx+DUtlvVrT2DpxeccPTjvz2jLm7t7UWLNYPqbIf5DWoyOteQVeKvLbdljzcu/2G3ulfbhf4dW+x8RfGXX6SNpKCHW6qKFL0BC9dOFlW0yZxYprHIcoSWFWhESzF6Yy9Dm0MGUIsJCuqK40VyqR2A7XqEKODOQtUVnCwXctzBczOhaBMDNmWZ+q8NMYMf6Fzxosmo1VvLQWfOn1kbHimfe/FL65/V8+TOzY+DlTajbtm78L1oUAswVvmXoFYG9RIOtjr3D0S/nIcA6qgT615J5XWa3tqeHTbrSezB4NTIwHrTeS5J10LeAvl0QLgUvDdqpMkpKanxv28p1b95Jrh4eKmA45XGF59qSlOPyqyDsdYTx0Z51g2G4Gp2KNOewoXvShs6Duq7sisztFMtLHTPZZ7KZ1G8rTp6YAgKBlMgXSqTSUnihvBLzK7BxJYonKQsuVO5dOcwZldR4CDE8nhGMGxGjzlVxHk8aqj22XvTJ6LQ00nK6eH5fC68VOOn4RhpoWOT7N7GhlWCBPIftQyf90zzpW9Azq0dFTZjFqz+7a2P31eqfPo8HumtuxzjL22ezTdaD3az5CF6rQ+8vi1fy9Y/TR5EA2ZSrEVQhITcX7RUITQhpasUT5kjVXnOcEInqrJoCu1r8O3xBjqJ5p6XkZWVmZJUHUZ0jkWvoR0TynUNKwPwKx0/qKB+OLrnDWXf2T04Na+1sqGNY3z+t98sHdk9Ez81OihqaO7Tk9/4oddAe5p59aOAanByGBms+Puj91bOwqnnML6wTWJX7/lhXcJ3ndypz2ZMAZS57bjo1LzkdyXf2Dsfj/2Pd3Cw/huAWoAoD4UzSQhUis77uEW1J5CK0a7Pbnj0TcNd7YUG/y2zvtvN0/saPuaX2Mw697ZwPhYpqYht+Ys7rYAzlHThv3NGhEA4AKAAwtJ9VqGZg36ZKE0BVDsnDf5XLOQ1nezYPaanuMDvlok2ZQ1mwMc9bJ+18EgYv/M1jx9vAkWI/ZSBWW1u2M+8L2/+dG9932nKMNBDtRTgbnapL39xhVY8b0/UGTWcR9xWHLNBfoCZkH3TgrG3UqSl0t/bvncqQcfActJ3mRZeirBrIltUwZDJBVmPx9bpfIs2CbaGHUl/1v+iUIMQ0HAGlG2d3OLGgVlyZRpzUtQZoLMTF/U6x55Z8/j2fz6//SOlcfD7MXs64ChAu7qmPz63RGWqz1S6Dmm9cRpN3tTNu7IzLyTxMKk//vo2zZvLO7fxyYfe0Bla4GT93bs3+3d0zF83/bhU7/5rUAUDfQqOldEh8wgtq0kqNQkKUpHVUfz8w6UPhMYv7ooAN65Z0lHC7gLa/xrw3iZLqxhxJy9NpKWIWxt76+FW/Vu+wjo3GJyF/+NU2bDKQtYawoBXDFc1Jy2GXHldKypTq3pd04JFf7bEl565PiCPEQN3dUGlskvgrbqEoHJ//6771TTepeT0JKzRnlwvRme3l1nR4ybNiyu4nwwrlqp1Rp/V8fwUW/1pRf84Pk55cU3m2snbrby1DSAu7ZdHwEqKXv/noPX5SaS5JaDOIhpU0K+LPKnTRW0A+PtJnu2vflHP3jZglcu/7bnN0hN08TmkphTHFumhk8eWWu+cxR0LtVIpLJ71xZJayE1l/q2L7oDbi7z64f/bVlNgalC/NyAT86H7N1fsC2qIN6zgXB0H8t6nmOu2iU1hUa65J7Dzaaz/bB35QHT2nDAaX0uZQam2O4DnGM+ojvGEcFw0ugvl7Ss8FRlpGoIXAaexOcRQj0ImbRYbZT5ECgCJ9CV7YrVWixtrMLOZ5i6yHlKzOuQPVwTWA/bQj7JaTVrEPDCNdYFRWmOTBqMjjqEQ1hMxk/aBks7e5VlX8c22R4aU8jmAYGAL7cQnfndvzliTTXgpeTB/tF7difDo0Qenhm8dc8Lus2b/nzm1ctH4CyxQnA4/svMHVwui4ppPJ5tuHPBP4hcsHIRKa0VimjV1JtXf8maoz4rRwT7iPS9pCrKbFwwvqGYDYT0OZZ3OWA+hEAUyDSKpaiOrSL41JoWjb3moiBh03nzU0MvFwIc12QGViFhmMmsrYwsGnLmTmekz7x5uLHfWS9VJInkmdtG2k+MhyMD6bb2D21XQjleuAv++w/UbT+YOYPrw9ENw8XBu0YIodva/oi5rfOzfzxjUwmQxEkzBMBXxnhN48v44A9ILjidbaApu+39dt9Q0Qw3NXhv720zfxuaYg7j2TpA5hv8bvIZCJpxEzgTbp4Hi/7FqkloHZ5RVV3mGEnLymdI8y2urCUvxdAGN1nmaiQAyWgmKYIGgOO8zqhpM+FLEKMrbDFGq9nT7OxNeAdbQWSRKguaUUsVCcoUB1t3Pt2fqxbKzcCMGMUwoAUryN0H33X0hEAswVt6GDDmqt2DZ9YIxhUc++CzdUQwEZwlA+xt71lOvEW+6fnDo9sK6zGuaiqIr3/gr/qtDiFfodspL0wgXybWmsse7XHj0KcLvDTSbcJsdlwRMALcpR5hnGEIVXRfcMMc3Qs6FaguvwXs7EGuUtE/mEnSERBufHlEtKMY+Tw+41fpJ2DKhE3lyVjm+YQjsP2C68287dHJ9q8M9G+/9aFRm8ABHsSHYcrf9sC+P1SUqCQkYx5d9nZrJAXLP3Cmf+3mB8a3LrePXH3TX8/dGVgJiGWyUECAiOSK7UU2OCM8T4oRpaLjmsl9g2PT5tFL3Y4W03n7jvZOhohCYGLMZX6gUzKRdYEiHRzuvxzb4GfEFmnYqVbthdaAylxCVgUo1lVwoR64GvipQrRqTn37On6zW4rNnrb+oYwPYEPBtqils+iFe/R7CMHAeUqpz3pNBSQvsSZVX4vT+Hd/eFWLYaswK5vk9q2pIBKTJl809QvlGVP5nXfj3nPNDTY5JYdbOJDHxtogHn7kz3fPqQupDimvkyCO4qef2qlGZhG+vqgfWEy1NkhYRVcKS0l6S4qXTGaEY+2qblneFcV2V1CQhzRGwWGrFUezz1CRDO3FeW80VHXU2nbi23omRgHCrSvv+9k/L/rRF7dbS2k1rYXJvmjIROB9S6v//YYLfoTkuv+Bw5ppVI2k8hrIH+SD2NYxPPWBv5AZUAkKFQ5ZpSCHpBCWKzIx0kh9oXkFsf0GFCNA0ERF5/bicNvtyjKdKpoDbftQ93GMYbSD+ik6WoCHD2lMvgiVfTChWXd85XgyDEmclPgADj/LI788J6SB8mTnBROyyUus3FA9QxZik6dGz7/ieKyE8hKu6Y353faRDF9i6tuU6lxDUfTgNwUJG56a1T8jfSvIn6UTrVQRIEkJGPOJmTnvkucOjLnhiJVa68aON6IqjhxPt7cPfQjFbMuB7ruhcjjzK5c/snZ94/CdCSGvG/az9fdLhfWFDQ9e9oYvFCDLeNh2lcyhbh44RjLMXb8XdTDDBE/6EOPNck7lEHjqkylxWgAhCzWrOQUkmSEFV11Fln8voakzFjrWBOvTIfj4/KeUCEc4dsFNI6w2aSgVj3yRioGchn6SOKc+/uqSgwQinXEH42GSTB+/Z68ZhP249f8GDz1xc3v3Nz+D5PiXN3XQDlVN+eQNT3vLe/dtGz449sFF35hKZxdEKVFEcieXbeINqEMkrvSGdK4vqokJyVxj6PMbVnS3re4sEizWMOywX4/2k+OPFwA/ZJEARik3GasBuxGhUQswcAOydxmcQUAsRfAyrNSCw5oU+eR+YnFNdRcetoYUoakqbIRgUrCm4kZ6xo28uKD0hnS6gFQUTbxSwCbI/iG0rIqha/opDpxSKy4FzLUPRJifCTJLrt35BMLAYU9HQtsibpUhKy+CbxI60x3veb6J2C8ZTNKIZ9YUh88omvIHX/zSKtaI1q/89d3ATXfV3r9xFzbuH01ssNioy642Y2Gx/fhvP3j+6iNEdTK1OGiGIplfASRAv7K0dciRJZHlkrkICsVK4qt1hYhodcQhfmK0+GqolByymJSOWeKRdBh0pQdiR0G/5AIXVXldY39FsyjFylyO6kmoKl9htKqRYspPyQGeRqU//Iv6rQQxrR8cGHO2TB/cemC9wKh9xun8NCvLbcG+K370hlXSln/9BBxu+N37n9lW2Nj51Pbl+weAXxs+9t4SnRuUrhtIaXGTtUwy7AF3dCCdB96KZfjJjZpXTaHzgXeUGszwpj63zXSaIbOkPbfGLwjV6O06/iRUdPIATuy8lghmEbhFDEX1IiVRj8lk7ldm6FD5zytOhkwYgTdopJiJnZk8KVklFBzmeNgJK7pOIuEnSFost8aRUyipgbk9iNjv1PGC81lqbAogsqLIcm4eLTEmGYIlpLCfFSv7y9yFkd7Nfv4Py93AmLIqmCSYYylxVExr2vkoDkevXf7Wu/ccNXfdu+eL29cWNVHQ/i9uX51dtU2zeLyFtVYlkHnblh0Gho38jk3as0SU3e/+8umnHyfjDlEIjHd/IW0uklZablpka9/u2y8prNk+PtO2ebxu8/j49rrNpSvGShPrJ8bapkfHt89sHrtiom183QVXlS4YK023jcthbFyujY1fU7pqYmKibXKUG8baJmfGx3ZsGJuYWH/19OTE+qnpC+a9bsHml4cTtyRDMBDJXZiSueyophOFrKWyiKQzOFycIP3ph354kVRwddf+u7beley9Zev+rr13dexPt5oVuh6+2mSuefdJfru2Mmtz4sc1w3dhjd/0R58SFSiIpL/ZcU/X4B6gOkBlWkkboI4Jad5UiXXXEv9Jb0z8zuEhedGpZu/tB9qKxZFThwYsjJ+uI1VDIEpwnWt3fvW8/j6RuJ4bZRGeF4ZXHVEZLvKxXvOlmYqphAKwXc3vVt1Rts5oeAICH+AJ4aGgBOaKOnEFtVZkYhYqab2DcO/zUsKJ4sBMJZo9ggmfIQ9Z1I3gdZ9V0JABNwln2GtQVYvMROF5U0tHHK5BHJPTdS6cWikX7BPGs5/+aGf3t1csevMwEHXP3ARmk7ckFjZ86W5VhM2Xdw2Ew2PuFb3tmPAHTXZl7/FsxcOfaf1yd+5UEN0vJRR82uDJSqQ7XZ9VOpwYa7xk/6VX9ixaNWZue8/ui48vHt9cd+Foe3FN1NYQ1sWLlznl4sLzj15/8NK6lnJ7sa5lcXtaadkcTy8ZX9wu1zbP5zBaXNJe7I83R8sq4xzai2Mrh5Yf8NIQzlRGooJyyNZkKDdZI24NZKXfSPOhVibrd55WqfVwH4i8sD7cwD7AD7iXvhcY8ec7b4k6l24SIX14n0EbNppWpeX7tMqV0irR//WdXR6CScBEj80UF3h+atRdUggc/bByUGK4LaJwHaP35WzmvdBurfG+JZlubN6D3cgGCgJP2DAVwCKTEhivq5AaQ/gpRJbgFqlNSXMIkuRadQ4Z2CG52i2Cr6JYcCyBrmr9c4i2V5snSeIyELzlbPCHjCZ5SaJ5Fi36RSyT/FvkZo4ZZw2oFkWK0NxI5EueuZ6pFcpzMRKYFZitKmPvz+5958cv6GzfY3Qg5VwHOfzo3TsohknvUKRkzDOfUs+uGhLvc+75zi33eVvM8JoHH2zwsMpaMxRc0FQWRAdK6xXDQw0N0boFz5wotDyz9NXRi0ZH123Y/dWsS4b/YNTrnMyezU44g7LynSy7waBT7glfFFnTh9WIw0A8JTJn0Msq3RD6egLH9Iku2e3IaQ59mXOcu4M0N4viPZUqBhVrn7F6jaoWoIzY8T79On8pTLRMtwc+dou1EHvwqEUsv28w+/Lnp0nM/7NtZv++7cNqOf2ijIAOw33O107+WOYKYDD1YhCRLAKxx66eCEcBR8jtag/PGsalTF581f23dz6xo01gvFlyeKVzYFPzy2fBeG8OwLYwPgfYHKR+NuCL1DW6LxGZrMm4oCgbQ47IFxlyMuZBCbpLD3Qt/BeYNBhXZGoIIkYZwWKu/CqzUOAZvo9CIgdc6U6BUEUBh6ShOUchAG9gliCAre/y1iMoiTji9JARwMD+ZtjH0QNEg3/pxk0/tLkMthX0cHBP3/bbHqyLb2Kofven08H5dMxRt2/qzRq6sGLv09tXPMxuUfsHNnzsm7r2G01sGDpIqcQrXRe82b98U2Fp3/v3zHt5ZkHbmvluVzi/Y+DMiV+O+kGGep4rLGwj4hAyDtRl1mYphghPQ+h8lfSC3AqAUhc9QG10chobnR9olgdZNfwCO4/SNKhJmb5e9Ro6QrBOiAdAGjwI/uov377QTJ9xnOWqudxwQjSXfrPiVla9v75ub++idyOhx7r2i6YyOijL5tPbbk32D2y79b69ffc0PX2ANtQsObr/A9RfdEIcVDA9UJXU8qgqnxd7AuPRl+bC+GiaXVzbGv4+h/EpAW9IQrYyImm5vFfWPhnhZIFwdI64qHMKsB3CTfgm7jDBtyhKuV01JaEPuNslLMFlsvi6/agqQdp+rD8BVHgTwJjEsGrYvxSOnCFHlYM/EgEBnoQvH1t1HJ0PEyLCna1vkqCt1HPupvWGpKae4OzYJa2A6E8P33Hh09YaOqLyPxxAeXJ2fwZK/n0nn/rJv31YCvDRl/YNjjUeN8n+5YWx5cOjDvj3Pbf/ZbdmFW0a3bKk8M4V60Yvedcr/qq0ZWWy6vnOk09M7C2dysYGjh7uGZkpTQRoq25Edu46JAV+Ql9hgU8ISaCV4H9SA7ItYuz08BMR9YI2gmHFU5a/tYbK8AMJateA3OHdgDRpapQpOsbuW4xmKUjZm3zDb74YsR/Rhv1PETx2ohh29d+T7j90kzTZ+zd//SeP1qEDfGG/xjYl1pVInrGO48nfr/7A8zDFEpvQMdX9b6SQBKIassAYaClKwCC2T5UqYLxmUQtWbmwyS5o6He8T8u/MsOM0HEPDV5Zpbb8qjI6RvEnQkZwucdqaNnXXMTmbkglfpnDsq16kGbWweMIN1T2FcWHGWZ21LkJDxQ+jbER9k8xHdgPhtOUuUoKQpLCuno5I7VSlNArkl89BMCVjXvVaQN720Lmz92iujzkcVO1QdRO7N8svgMj4b1i4xTn05sIDA3u2rXjw+RXq8Q/Lf4A0uvmdH/nwncySB35BhgdBHUbNiPfErb39ydsa/+riY4uaW97efPHikaFj41dNNx2funAoeWH5T17ZeKR87Yf2kLs/JqUGyq+rJUBzjsCVUhcBgdQTw6ImF1YWL/ZnogxJo+tUtJ3YJxGdUhY+2kl1bjKIWlzpSSP77MvsayYMdquOifvkWr7VmjJ1RaL++NIbv7ylz24QLHILvoe56iGn75NUtv5dn6iXIZZ992fWGu8OxoUzqMR3d+wzFzX/9BXN3Q1jSFUWlQ0YPgV2YXPSpQLtlpxegFqvLH3mlT0696ULO4eblvQ2etcfKM5IazQQewrm9kCqHqH2jkXhItU147HhmkiynIqgfVbdQgy5zenIUx6EiAo9DaSSJTNAu6SvSQuHN4DzgBSH4Hx9XVrD6xB2Yr0RJDa7XxW5ymWO5KiMtqt+HAOEs+jFQZ9MZ5RUNAsXbAc6dNCTpZM8FXWHHnlb3f2K0bPl1uN/fCws9v/9JzSfGMjRHf3qg9uMtQWlcB3WjgyP7P3U77n/K9rS6P3+mtWnu/f2xKXR5wc7B053zZTqu407EKV9z4jEVYYCDnGYoVKmSK02eRUTbJ86ynXwq/FZeRuYmnE8ZmlYYXfWfANmDrqbijZtzX6heeH0YVV8DYyKahvqB2gsy0rZ1fCxI9c+7FQzEcjBpLfG3YffrmBT5X26Y2wtvKGaNX6bTKM/XfXUT2MVLgwdaC9s+EjkYRknOLs3K1amBCzmSjizQwF7S9y2azCc7m1rcO5QjqlZ8gXuZahW6YP4a9VdFlSIftC9rHJGJx5imX0ZEyzVeC2wnTr1bOCQ+iuR0azIRl3IrKNSSjeoWB3VwTNp6aQ+MEtfV3bz17EMznpctUxziKqanBbjfc2F61yTeLtAWbOP5I5uHL6BZrvQ09ff+sPi39bnXMta1s7Nb8l3hTbO1//InOWQLu5PvHWfn3nyuQWjXSdJ8Jfk+UjZiaMsyIPt5OEYK3lMlBo2J1fHMXaWX3VWUxzCDv/PGwt/fa1V1F9veypVG17K2sk6qV+0vfHN+htuf+RsIu0W85MP/o26p1FcPu5svM9sb//OUcgfsExFazn4tie/lcoiWw5qnF9l7WY2CInQPdzBjpaOxbBWppz5Cst0yanOFr+5eclhpmyYol+R4cOhskpkpK9hJGEoBRlYbQzzmEIp5l+sLhi1KYeRoilZAnA7xH4E8QtEgYmJUC8B8I4pE9KKKqpmJzRLGpPE/vAFUwiqtuQifNBQ7XfVAevyO02qhAka16dZsasmT61e5ChiI4hRJm0ZOZWTn9mpUnrWMBh/9uzHV61s6uqWdhaFaMuwMnbbP5L96+sWSk39O049a+DR0/Idw0V0rMT7y6VffjyBShEEMqCqMar6L0jUHgQRGDRRWTsiSuhgysVAmME1Uoe82oEdMjJTFzCYKlwiu2JIXCTucdNYsaosqMxlDLF4Flx8YC7/qLHSodmUC6LNbnic0aiEhjCii0kKkX1w6bfjhzZbyqhya46tMcNv/t6R6SdRw5/45g+3Dg9JR5ijMlRv+U431dj8yub/OaB9yDAVVB5kMx76F3HcKUOccRxpvInILyUtawfKqCOSUorRfHio7ZRIK+/Kzt7WTQccZ18GqDSFpnIaSIV9p97NIrotK0yHsTRVkF0zM51vyTqNfQPWzipvhsoKbL3seERiTmnnyRDEKU3SWLHxnP7YsgpjJHDTBmnbCsNjclkUuboP0tIK5AgZywubpmjFIG5OdYNgVhiZOoYPTBcjGKluJZ1EO1dLfVMECUMgTnNZRnElm/JK7UMJgRrSvtOklKC5r+mhXqIlLplQZ2tpb/bR0eHyVkG4ywcGBeYePb56JDz9H9Nf/tiX7hvtdbquMDfsSVaYPufWETO41Uy3fP6Pv9KHGp40VtBX4A0snVKTtHFayhHmGqlchdA1aZrkvKzsFoj4KWYVyxHMmic9FSPJ9IpJGe4QSifrdHZI7eo1Ulom62VDSG8U6BR2AECtOIGlQcbY1V2q8sjStGRK5pRMZXdlYUYXi9Spz4jikPI0j8gSBX8ieuMxrFLe1CMH/rJn7/rlYZdNo7KmKMrgmkWHRm/98y/9Z1JauHt8+aBZ3jvoOssPHHUXNv7EfL7zaxMmCd302mO0twCZiUUVtlARNb44jaiV78xrO5GwLCbZpUPQXQQ/hRMCF4PId0tLLlpprfHewuEtZqbYXXyRNDN+Ms5qqZspGyaV4MJ0UcUlVa6pdI+7KYZLL2pAzyU4aHS8DkxeibNTBNuD7oN6XZzLaTgeYOzyI6cwo1ZFkSQRXwG+Fsw0ZgYBntPlDApE4kd1U5bUMLO1MKp5EryK9EeQJK5vxnQtTgqud11fzqjYVj8KxvWgyztxof2IyV6WsqEQVK7oJ5GtaByNnGKrjWgKmnTobxzr3bbgpm9fufBN/zEo2vaeM9uXH+wb3X7Zygc2bnr/j365YtXe/t13p3v7t9963wCRVJe9sfE7MFBMVhybRyJfeMsl8vbgvzvjVQG2gZ/gy6I2CauJgs1sbRySVUCujWVaDd93Tkt3pZCBAxoExgwbA6ONeF6fNntSgE9USHwRGtGpBhmPWEO7HcAPiRbWjGgyPq9yfn91WzORWpaVMtngC/oSVdhiuT8sAABypElEQVSsHhSNLZDFZ+qG1092XN7+dLat3TztbJBfIHK/eMUHs5eev+r7XqacDjUd37th+uf/+Jv//ANRD3wRL91V1FY3TWpJnKFv6EFqerFfGS+R6FeQV78lYYhW8ebjCfzRqHBqAcFiW2YavDtHisaMNDY8F2M6gNbiqtqmkZYcUsxydrc/qTWJ0MtV4CgLXD0kJNJZCmDFp5/QuhnGNseVO1NfoFOSb7GWVM0C9MG0bkaMtaIGQSdyvO4cHck/LgKiYJKC0nqqOZy680ecrhFFrUgwxzdvcq5+NkNBUbtWn4t7Ui5WMFnLdJlc8caJC+o+Uj86+vE3fGnoxJee6l/buqjtgBlwl5t+49566AHvqqkDXW7YeNxxr+g97izv7XfNbf/7X1b+9UOZNUtNBklULW9moSvh0bleU9vdHBOeViPrG6Fg0huBaNOIaFc0KBkPJPfHkuLqltSaw4CaWyJ3AJ1EJEJEajuH3lDys81YJh8fEtGF1pMOq66iH8fBOBfGp9kAu0KyqY//SHfL518sPukOXClYfkWrJWvjPX3auT7tz7dGbu8Y2NY7kn7+mf/xcqhxCm4kZbVpYUQJ8q3DpDvQySBzQzSsCJoPVkhb/bRLRF0ce1LfxtEXQ7aW0i0JDg+ZlofP5XCeyzK1AUksU6xIit10Lyt28fLZskIhPjcpKyJH0urRZjWASPIa5FNLTJVhTXpdvQaMz6GvvBuMyxqngJUln3VF067WtiG23MxP/+fhaC7AVqQov4yZD5vnok2Da9fev627b835b/r54Ei0ofE34y92fUrjwOCcqtH9qMHo3q1ZEey+wb0Lv5x99JQXy+g2Zeryf6J25PoFA/1sfQi0Cpa0ULvWhnoIgTm/2obn9k3Or0XNe42+OZcP/Kl1//7M7+cOhrPrumVOPNwDn4kfeow8HrVAuF+jWuRhffopdOGcPaytEtHsNZap6e6daitOv+wRb4kPBnq9mjEMPG5cn0BmDLGo8gpbgQkWy6B1GppJxggjw5B6Qjf0ZvVXhEuxBBOxc4pqGTAWDY5+jCTMYGLuXdZzfTHR1jgX9AMaKIXeruZWPKmu7pGiN+r4Y80HSr48sqXPqPlhKrrgspLXftG8sbempaalb6h/eu1wlwn+rXT0dDzS/bPDJ0vlY0e/N3DLJ34+cd1Nrd8Z3d7a2jForhw57jhhY2eyrX2kf03j8Ojpu9bdcfpPvjsOnNHVzMXLQwPj8WdVh3JEvTPd/pi4LQCio2I0tyhbEqjOJfA6VnhQupI/dMDNaUN9lzSytmEWzm3DjEzTjDUSJrGSgM40S4uBqwv1gHerLYceckn+BVgjlizY9f3L7j09cMIsf1izKt+TPHxqe2H9/r6NbypOa1qt42bHoVt2fm7gx1nISCcPt4AQZjr9F6QwG9CecZTQGJwGRGtMLh1m55S66KQ6xY0znS1Og7ehoTjcZpYcGozyAHqS2VgmqVJGExFyqFoewhAbR27DrJo983B7VngeIUF+RoxYTsGsWfJIoJqvy3q6ojsgyMLusCdVXN3LVG8owySFPanWUAimyjIty8czTb8b6e6wGbEbshDBpIkvqz8Wjy1vvPF275KLk9Pv+GXvBcMXHlp5ZW+w79Bo/7I1lecRnqnANemCSCDnyNKffHzp8a89c82itr/aM3BP8sDA1uW3PtK/7fLHn+nffsMDXSt/f1nTT7/z0T0ZWb60vEpoDTM1VsaxFFGkIyY9PY1Rt2DtwLlFmT1W2RcN07ETWbOnWpRTpWfiZc94iceWDxhQDTkeEplX2pAlQmXyBrFtiBdYDaiMWMYx10Al7NHgKiWX7ftE3azyPzUDgifYrzO8+JL3JQ/Vsw2GcwVG1Bt6B53lm75y/NS2wZHjU+d97saHe/Z0IWYFJ5Ooo6CrnQx66WAC41SOxtjjyS+HFcv2Yt65lnZry7tp356xxqmi8YeG9t9ORM9um8bJWgvy6BoGYYxz16toWlor+O21ua7gBBqD/YNxTMiU3p2/rvqIfWf1TIixiTM21S0S/Ky7MxELKrUSUZsEbjBBHByLTHsNKhGJ2mJ6l72l/sdma9cLhahw3pVHw19c9xdJv/eygIzyS+nRdw2k80TRffWI5ljPozFST/T/4KH0ybbKZ1qPPrd8Yt4tHW1P+WSTdUxzJWnddOjb5tkfzRw2/tfmlElLnmv9WsSwbDc1teWtHZAcWiGo5JaXGykvlxt8Wo5EHPgm4AgRFGeNNBVViqXNbSYWJDmsTXWd85U5XmDCDTT9GHYXXhtIY8ljIbIlpKipFqxaVO9f/zV7/efebZ4d7D7yvlNmW5vpaLn979s6tj81ceCi69f1zPzG4ojYOojniGUOsSYqYfNrmVqy0qmpFo1cD4wQCm3NS3nNMU44WXHH/UrSdW5sae5s6zRtX3PL8i6RRRkHom2My4Ftjy2EISjAQhjtIbCVejL0TO109RHuPveafVP+gXNOn/2VXzktBzeJ9K83PrJsVXnF0df1Rv3hksE1dUtWHlj+s7X7inffVxbV3cIdmomlOYVUzrMp1AzFZfa9hNQ4oTHJst96+aZ55oX48JPlpqnivU310ib3nLy8Y7bm55RXN3yebRWtIt4DXpfaEtZOR7BR/HPqUnuTXque/tVWKaT2TZrCYM5XgJ5zv6JlUu583htzvlJ7RBS8LJped1X2/tFFM+bbHa87OnnDBRuPXjoz0fcvRxaQDU7xmZfVXnfOx2msX+kSreJsn9vTbTvMkg4ZVc6NbboR1MhDeboGGed+rVdSN8q1nRQGAaflL+imNhpHhJTewGkLp3kBBlvwBwlK9TTeKG4Jyz7QwCJcj5JIJTJmSv4C4nR0eACEZaWtvSL23IoTTm5cPLRh+2NXP7qxszh8fMehls4bdtWNjDaVpWqFUx/4QcL2gLwDrQq/qmPHE8SGs94LOpQD5lSTtv9i8cj904cvNeaFdaWmP1k71FFXx94dWtmqRgH4tLjbReGdbRUPwjIWXb0GInVnax5DstZaUSLIErWWwICaI/+YVsuLasPFag1pP6AsMb1bn2P21Eqvp89+U96YTC7NqTH32vTW+kO/d2Ecm55LJ8z3Dm7q61joirYztxc13mdOFeU09u3ZPkd+C9Y/6xEXDYPaZd47m0fYIHjEuUMHVvPIQ2pydaYExNuyTDblnTHtFvIem2hy8lpFDTSnyRrqz9hfuJY3KfBVb5qezzCe2N69bpgGV2Vjzi//1V+vfdr+nl79zAIzqicXBQfTKCjMaBXXdXkydG0vTIVeXg1DFWjgybogxe3jzTj1esrL5C47X+pHBb9iE5eXlasCyO6V6aQbj1YHRFoVBqmIQZ1+U40MHT4B0zulDdt3Y5fm1NSCfPZcs1vznkiLuI15nzVNE15Jv8w0VVtwflXSs+Wo9tmSIZUR2Chmq1GwY76hfrjaU435kJ2gijzrLBhPmBop1sxcJEw2OoxKLSUWfzk/XZ/Lyh3+49jR5NrOJzNbsGm3yb5iqsnkA61Sn8vgHbsK+etLjZSMosID02k0dZeVVc3uEHvXDXU2+wQQesnbFhE1R+Dcao/9yL3UvfMGz1RIFTa+lohz6Z6kvY7Ed0b+W2g0f0Yan59EsWeCKAkKqU2pEbRVPC+98jzvksMDR84cXde1Xg6H1/d29Z45sq7riD10rTvKtTPr5bfDcmZd9XQXZ87wG6d55Ngrr5zuWX/6lfXfWte1f33PK6cPHR5duD0KyhswOsp3d6RNAcEIonHceT22SSn9ZIlkF57oDh/Gq4odqHmrQxNJUZcXPPz4SdRwfZgKiEhif1Fk4krIWpTtEGAlPe31GbZzkNV4OoCQHUZReEGlkvCcv8aPYgE5ceC/kbwNcRQHr06I8h+QNeROkHAsKsdzMwmlSMLLl0vv4jBhkyEaK/SS82XYucjYt894YUT0zOR0QPb9wEuvv06DaoLKmcqcapAiwku2tWUep6Lk6sSWMHn7fCI7/CCNdTskmHbT0HDg/E2e7yUV0j5GLW4kOkmcVJLrnCj2w6hcePUZqOWRwP+fsl84vbjclTN8oPW90hgEXnjbpoi8q1TqhlvsNT/4kH0u8BvqpMLSFsHUhzubT5lOEVzOPXiOBGv9Y+VcTDR3zYYQOBcZnLOeC3aplz499xGTeIuX7qkvICo15MeI+jS7sgR6RjQOrguAwaCl15DdTCDih3gEa49HLAWriMBXFg2kgBdUVqQnqhjj5sfGalgrZt2QCXsu/iikee1esxpOWauR4zAFTpVfgzF+tbHOhaIF0QSriI5TFYfsq2fDEpFpJS8pKEeXYmMX8pz2XWGq14A8UFYymS4E0egjBcNXvJK8jdK9dpmYGw7Ndm7B5DQFK5BOg6bNcD5X0RW9HKUFW7A5H3ftV6RF5CWiJ5iy6Ixnjwf7AVckankn4RamzXgLh4sjDcNtIy95PvEwXpImpA/w/fZjynQMkGA6sHAjwZiMYP2zXYHGckFmU54hUS4ayyXP63NZYtzCumcWixg3ZQEjbklmrIa4qsYasndugiMqQ3/KcGDDbMW2wR7qQU8E362svCXErSCNJCVCh703rGkr9ZeNNJZJvCuljKZtYl3ppdx3nMYBNBx0ektMRb9UVqzuYgCXzJ7O8DZDiMX3qgFSPhFRXntXnUlcpbhKFV24MUpxlf/YWSw3XCdhUDZx6JeNr40ld5NkRKQnbxLhGUOI9aD2eaSRlDb0cEnwJlK4xgAyYqlVJTemn/qCbkRYKLeAoorCKE0b0/b0FHOHlp7TU3lkHVszeMmOvgIVgm/pwiisFkw7kBoYVFUUSKPB9vjjsSzWGovwPA3sqjaWTGjXumu0LtLKnM6vUUKGSSw1v3FkZW+bDCrv5uKSQ8WiaTgAIQSxIYo+9svsCJZNjVZVg1mI0VQhnc/OdUAdHLRY6ag+QBksYNSGqRZASIevW94HatYhqz1LUVKYP1j4IITq69EY9PU4bB12UnCV0uVZbpIszMTtMXYTdjEgT7nmzsm8kanmFaeMS3bm1t/K+vBrK39XJJyGRyTKKiIxBd+x3IMdPbrDpYsryJ52aMQdPQgP6mo9zlKqbvgaOOKpHeHLONtT3SNbLZlk9VGuCZx0vUe1cWwHmSYv52QGC5TTHtZeo6AGqyOjiMA2bQAIkKBhgDl4BOSOKmQDs1w8sXyRIF+MLylrKMNBQIjpytiaWy20aq6lirsCvN6uWqSt4TWY24FeqoZoVbEYXrCUqEwZtp/aajFPqt06jOxzbMrsgAq1BFyrlUBnM1kVqJXf2nxAMNbKA96GlWCuhs7W3rwBYS1DiDLYfzS0Fd+gDjtNkg//EE0MbjNeOl6MaV37jN0wtHOUVZuVw90Yd0DhSk7C1+PRpyE2bZc0dnSXsa+noLyeXM12gtTGo4sVEAu2bp866ysT6LS+9VUMqsYcf25oyoG/SKReqNusemVX4EwZWyMeYNUK6AodGA6eQFlmNc+sntZuRlPRxnIFc1j7OnOnOgfUgoPaQFNo72hLK16iLQg/5H3SfjQb3gOZNngzaCfpVPA+NaRzYBLZ8UcD6GS1MXKY9DUO9NoeTZ7hYBCXjgB+OxpXwYt9guyIX+CazmYH47mONoYTnYQZkOLoxEjR5MzOXr/C7oWogtrnWl695sg1tWxBVreBpiavkLpMtEwVxALjXB0jljfkIJjgsTumfeTyA0XT1+ot69Uh1va9CAqqAGJoHbIUE9xPPGUMTNIMUuz2m5vWC8atOAUWM0Ic2TQeU3U64PgYpglHNIkIaWf6nUv7KkblvJoACjIdoZQoXY28yqTc0N3rHCdkOSOOwzcgccGeAj3gt8fK7gi18bCK1/kBaWXJoeAJUH79c5PGv6ZPlummtCKjyqdp3SAj3zoFp32JchYRqLt1xkmdSzV8MlOIhMa0LoXCkuxDU6hkSUHt6/JLSLycS2OwrZ41uwNCIHLLmE9INpPvtufzElnaeJ1muwhsRlkgi/oveATzWszWbbq9FpkaBBOkdTJxQlZna93WnZbVtxEnQR9bwvkVWW+Dsgz23KPhkB2C5BnySjX3E7lsXQEs7bmd3JdrBVjk2lMhwaj6gT74+TSklDK1fhOtIq3SV0l4hIQeUWBbTB6hxWYdDgWusQGYpuatM1omgSsujzjRdNuMs/JAq3HbWgTF6w7n+oMyAXGPqUK6XP1BL2S6MmORxaKHZpho2YpBp0OKdquzUrV9janCLr/yJ0+BZPRJGAlxupOpA5tJZpWoKeU4iQ1pqeU1vnN1x4/cQp3rBj4BRfD6yRvGZAYxZQnxqmpBcSBnqNXbyfa1iVLTQXxayydhEUMHlL4iUAE5oAl4mGP8y/qLUGLnllR5pQJ3dWHHCwpBU2/QuhBaqAJDZSmGcHu6eoCxRlxcfhpnhgsAdjCHw72cndJ2mVJhYK/adko1n5sTsNGIUfOXfpyy0NzVkkhRHRlwBOsjqxSD2Mss3Sp4MpYFziFIuEGFL2fwZtJTGl5H3gztwCgjPHbOd6v1rHZuZiPzFCvo6bzSrLYZNGr5A3a1q8ExeaugZ8lri8YMdZgRERHNS3RgCbpMYzxDTEyyo8R+HBDRCo2U0/j+XIw3MaFgOK/ktKh3hNmo71WvKbOboPXAnbjycFNZdAy286tpWB3BjKhE7oxXs1H7LlF9VDxYVChc8RQ7c5JjXlQXKYqox2geMrlKMjs1Q1VMuFtZJCfuea907Q9lxLGbSdZ/sDDFNgEEz6VYWUnN47NNsQgLXXdY0KkG7kmIIQBDrUZJc6VYp6S0vEftXFOIHBFCO3fVxxrgxrVYg230Lwc0HRjkB04pEjK7qo/BUESCecRSSGOBshRl2oaU50lmk2kML4BUnRcU0/Lo7VcEGmHCAbHaj6ckRFUlknyWFJp2qJUJ2U7GVKi8ed4Xe83VnmLniPwDaVmkLo5uiD0ZNJ68hzHsSauVfXKv6hkHbb36JkLoKQqxfXzFUVI7qpC9O3FF4MpS1DayUfTDEXI+zGzqazBLTJ9SNzMoV6wg6GF5WjaLMRA1mkVMqQy62DItQSsgEsa/rN+LC+8Y+0zYWmi6YziYnlc3NqncLQ3zysgczy50hBhonAFMLxj8FpN7zpGvfuzif1NcnALmdb9Bm7xD7tRlTQ2yKTSmmoZ10YlpB20s8q99pkzwoy/SGmOrl6W41qyNXLNp8kqqoSAI/x3VUPDN1qEqqLmiBA1gscrLpEdkWhX5KyxzNCEe4ClWCgN2jkyZLypTqC+nBajoWK41FjcA7dDHWBk4PUfnqaJsAJkKG5odjRfVEWnkAKMU8QmMckiCjLwA8Cvq1eJXNZXugKlEeZHNiiIVmCPk6iK4Eg4kO+WrKrTjUxj+XVJlWF4dacI8LBJeRfcSzFB0odupOyNn4Tmwf63mKXpvbBJ/lYXxfW7biOloNiOndkdJRXBrG0mEwRHxRk0FJyW9OsOEhm3ucteNIrTXEiZEgSbO+hlRskMyBoveub64bOk1K396ZtcGs84pzGSlE/2FhrpE05vKKhyklUIhiuvx3HEmCoMkMkGQVIgKi0idVW9Mx7sEdriF+ro4Loi00IPv+RAwKgiPuN2p9+MIuc9P5O1qO+OEZJdwVy4ZdwXyRALD2tUkKjM/mQ5QBgI3XeDm1WiYCVLMu1INae2IsLNxEdBci5NphxhKLISO3boi8ucXougpr0QcnbQq+264RVlI1UxbCrlb6hNeJ0PgWhFM3qYpVxsrMG062KQjpmIaS1qsWIjrpOUS8vp52oamMqlKkLzmmljwH19JS7qXhbykDVKwmlttrmaZQBGKv8jCqD1p1golyXqZRGq5pKcqfFxun6H0FTNF6gBqPlnK7arJJg+J46aRaZoB10nrXM2bdLMXIxVWM+2O8XIhKFTKBX9juSyAtxIUZsrYIqXLymNR6BUqlUJwxsnyTEmFNPBNJQzqrhoybZ2dnYebvYVTxaLpnCo+x7qZpSc1OJZg6Qk2CQUHdIcENbLijsgwVlZhPSu0gJ/sVH37wd/ybo+3Dq5tdqLLVpxYu//UzNG9x7pHn5wqlbIN4+xMTYCvAdoie4IEE4nvIrcFaYcEO1Tlh2m/pr7uD5gIiDQl+oNVXlOtVhnD6bTuNHwBFst4GJ4JFPgehKIyMx3kHTJ9xsvVynKDLiFaDT9iYJh6EsSpHG0g8o48NPoBD5NQjH6hS4hXwmkvc3UmVmHsOSJroOQKfuqT9a2XTH+nCR9Q1WEwJd+MiMs6aQKNh5spAeUJoy6hP2IoElEhQyCVZj+mlFEOoAXaNz2Zy/ZYha7NMOznKnJ3YZq2qRgzDh+e9a2bDOga1ndcWUhyZ4NoMdBzHK8eKcVSNAbdzLCwxmRvkuveAFIRead0Xv14X2FnDxDCcwaRm8Su1bPuEdTiqxKviw6Cdke3fLxMamTat/fYFrOpt63BeDcXRxrMsCk+H+tuv9ikpcMzOpQVTu3DLGLGRsbp2ic3TpV/c6r+rrHmBSvP96766rz0hanRqaljhyd/qW3GmgKQra+UYExiFSUTCe0DOZfgYeyTRJancLGgXoroch6+Mr3kzlf6UexgcLiRCy2NZZeFyqB/HcM0CObOVIq7wYK1R1R4B2Zqa2XckKWK9UatgGZnT673A/91/Okhw8fP0MPR7pAWDDq17U8N7ItBwtjWLNSg4/AfZZDDa+s4CMRtPyLXYlIvEfNL8juFEwm9oOZKTZEiWMel4QXZpPDMsHMDfBn8WQjIzb+iJGQwm50Y4DJm/BxucnX8BRg4TSiLGMHMWlRUYuJQNWIUOAOaUHsPQZsGygbvZHNntTvzByv13I/HJCdiyCbdvo6/2sddDfJMffA0oFsecTV/5lFbpry8lZ1Fc8AMOw3s4trXIILrWQaVchxFeNsQfAYV0BQ7LToIYyYtmLi0reX3J+eN39z/k7ZrF2V7nn+mdLI7YDWnE9X1z81UrFI3A2K2lhVopuhr4AhiUOBs8l5r2irExrvx2r878I2Pf+6qXgzBoryL2HXtduzYRUzBeGX0dtFxAf2Ckq/pd9O3PhEDtETubxs8Q2wPseJk+MLh2YfD2Me8A87CDKkHH76TTzCUtZOj7bl252JFDfgaDG4lXGXWR4FtTQ7w0pEU+CfU7tajxv1MzeO5xZ0NUzBNYI3HcoPAkrUx919oj/A5OhV7C2NOcT4kTg/PguJIi0t2HMOq7tuC5Tk19BGAOegVjwWlJyLEWtz1gIxUHwWJw7nRaz9Wh6bvk1lC38QniUcjz0z+iEsod+IRWsvzlM42SMq+Yljx5btqdldLUaKeBfXOYHW0DeLdoNb4aRlaI2wpZZZ0MPQ1vzxpBsDWgHrpJLXTMijcqQVtb16zIa7c3BSU5h/sfvb01JEnDx6aQItiKGLmIUVIlYoqMPbMxSdtjhFypIpCZ6NeSFrqM95dlGqbP3XHq6JefCP6Rub8w/7Pudn7fiEjZO5cDjL6DLeS6nZOhHBM+wQrXXykwr55Mk4P/d7esuVKVMeyaiMBqFe38+W0HjD+FdC9FcvnmJwcFbl0BMXpaTWTVG2CdgXmXhJeq3Jv3ynTRJOuyB9kCmJNUWXBshD0EZjELsgaT5I+YgQYAaKxbhO2qIW2tgp9JCkwJXvUpWLgNKsQxoCKDRoQl9fFgxJC0hYtQc3hYA3Cqpdwt8GKmnKiqo3kVdQ3qTMhd49ZwroMNDWJIhTtI2hraBw8wr9UEa2g6iHI8NKIavvCTYdbjZle6S1sNU5r53DTL9hnAUwkyztRVqYOwiqGRRknIpTrln523vkj3acrH+yZeLSvs3OSVHQscfAh6XtgCegJUGCRQeZvOhHrympyPQZrb6AZYDM1jNMNXMviHpkn7v33CypK/9mpZIdJQFznV9QdIxoTEQde0t4lqFnmFQfDfJSJ5w5f/iorhUixpHnxUWmQRIMcCJf0nGu6bI4AgwFeXSd6QIq5IGa1PFYyLI9ZKGgXs6cm6FJa8hwjaX5apgKW0jQ3ewaYEWks6e3cTolZuWanJJAMszBZHFK3vQ9xwzZh+iYPPdnEbLmb5NxkZUDrmxiCesbuqUZyjTip68GA6lWUUM018saw1Qqvq1poC5kfpQNOxZVexJLtONf0qRU1vzvLUxhrFY16S0NsNziy4ZPL3Tk3GSNHiiXV9ck57KtZudYqHMj9S6HJtCOn6XyvdHVTQ+fwcKOstZ1tzTpJI5NBfPRxFKey/LA4EjSw5kOPtuyumB1D049d/lKdH72QVepJr6UMBoaHNAZNazTDJa+q/njOstOOm0ShZT64gvSyXcyCCjPFi0nS5KduyQX4gBQrIb5uJrJGC8dk5EIfZ2WXWpd3YXEBJ/Amcu7Im4KJIVn/1cC3vNRYSEXpJOM3SFwOj8t6hlsVERi6pdwSQnI5RUL4ZVn49QD8E+WLiWhFkj3wb36DF9mimhDLqF7Tuz34Fpyp5hKTg4z+kgIM/uB0Ej8WEmLlUInaB6wV1XK4Zw/2k2bOnmMYJh3237Q2THtaQ84CtgFSCKynUxyjPIJzWS3Z6WM0iEhPthIj7VT+fMA1tlfXUmaUUiZgRUppb1Dtyn4cywn/2kILBhXIQM57X+MGM2ClZwVuJXQ1wbBpNs6NpqV5ySkz8jALjA6XDBepNck46Xu72r9vVo2959kXjpxF3EhjNziLUlEwJD7EHYy0xJgkUmqy/ReVc5kg8AMMZ/TJFBMtDIX8rb6lvElXyehWnQUbkV1ZtETEi7uYcpWAKVff+8MJI3qnfHfxGxY+KAu38oBrdBcPv5HyfvhOftq0P2YLjlpS5eF4aGsEmuEi1BpoQfgOkN6eVrt1DDXcVvGsxgKB6yOhlp3WpE/Ue0eVNCUD2ufsI1Yf4/TOJ/0SOR3s3fk1iKIZIXhaAj2tw5wSVD/OQMrfRPwdwCeDCpyx3uMyqF7TjzusiryJtF3/dXlT3S1htrzayLOdC1xyMJo7dsxVG+udcEzNksOiIa7sO9XQOUVMsgeiAgSjoXjXXdYXrjsW/fLVsWP9P335lPXws4wxVR2lDWA7Q2DxHV3dGLqGmRxilnMWlGakwErUAGQqVvbRy3yPV7h2lyzGSaqIEQTrkZ9cQRXyl0VZRHKWw3h8WjGZbGDfCIzvcZwLBWtJaUWxWXn1f5ZjfWEAJgdrwvVIAN85EyhRxJY63QpPSW6lmiQSQMkRDt01B35V0ZVeA1FoRljSRZNtm2sWS2UKPeh1zugW1HojvvyKIi9eAiSgYvoBjOb2tNHG6nGgA1RBFzdYICiak263CE6yJWA2MDyIx8w/npeSQ0onZYqdRMAA8XLkpRArR1cZcwSdhp2jwUlaJm7wnHYlGeiCNLe88Jl8vGgZRplZsKZFrTWWnM3d081upznVPGTajHpHUm/RJH0qKuQ9v39833mlI4Ov9G2sRzuS1zSyyY0mb79SuqyQCoioGwfjyDLhtbuo2+SxmfZWOEFFENEOd3iFfEt5e9QFaUqZKIT+g1lHZW9ibeMw5xVUSrfp/rgx/E22emOvN68CSmYHONS6QK5lT8SJLMFXQ2qQQT796AaantWneUragJw27oySHQShFlVOS8dMXRbQLPIzxTimddqmdcdf+W8+uTVVOFztWjdlthQDKgVfBEYUmRAkk5FxiObJprU2KNEzLsuX/DTQWWopvaTOYdML35M36CT031oX+9zlOuNk79bJvpgFBti6hHB9Xe+oMZPXmWLcBzOZW6KxIQil87Q2VOP1CFj6c6kMP/TObF1TpsQJJ23I9youR4LlBIXEWXm9jqSMkuqPVGSjzFBmV7LYzgnRGCqu2YVK4jRU6FWRR03NJMGic6cqSH35uWYsCsmNlzlXozzQhWZafbEyJuts29KcMAI7O9uCitp/lxEMJR+6+ocvpieOCb5M49NlkRpAuuGStDo7GkWmlCVSpCix7xGAE/1SpQKE3+KYv6MirRYNmktFK6gEoai+vh/4BLKL/PHZ89xjTJDWJSQMFHFpKSDeNUnBlisvn+tlJRg+/BFBgOFahEVTfzLn8a2PEiAXV8LLfuMn0oMehrzJOlyihaAy4Yack1Ze7iurWODby1fHLruSxZelTiyabBT1taZplLCHkddQyc3YTwiAVAa3aBEEcCVZv4tZX5C4u0wt/BF5ljRMMPJCGRORvEmqD4hDHu/orwj8jiues+kqKfcOGfIP1YXyoHS2u4z9tbTFu9k4gkSbUj25XWO2sE3JO5O3ysSNy2EheqNrC+O6K1MmnB+krU85wEORX/NpQddUklWjDHN55xQbhCOaCIQ2ao1f+kKoLogkuTSKKyT7S9xezPYQssuihIlISrLyW/Cr4AooNJFdOEr901mU+IVKOQhW1rupVjFdG5bjDOA+kcIDiD1vcUqfijwLrlACs4wqbz3TebjNVK3x9WogXRwvWPd8JcLs48wo5844+E1STG+JGVI2lFybbvRLKTsoihzBKMBeA/GFaXkxOcfHncahCjtNOuwnz9jGkek5G9+woJfcYEAl88lnwcbwL9d8+qcytbscsDUuZbwdN69ceN0+iJrqUMs+4/YYtWCzBqutUK3xMen2Cll/lwjNSK3xZRmxpDAhRYFThjsYH7dGR5M2JH00nwD7cVdtxGlWIrcje+zJ7FdbdZI1+prAQbqmxCbtsgYl9WhNODu9SpKJJiYFKGiGBwwkYZk8orLKjbOkYi3rG/NEm+Va/6sykI5KHcPJWNQVdJbJ3LAixcF0ANF4XJZ+3ATkWwAjeVly1O6AYOJektSnGCSHChjO5dpEg9phRDGcxM5CMOixOtf6c+U9jr+zl708Q02eIXKvQasva+tYoEGFMQox4jxVL3jCrmE7jvcpHUgKVi6phVZ0jDE1dUsfVcg2SBX7xlExca70iajQ4TspKqO2eHakcTVRiK06tEYaimbJE9jCsMhheUpmKgv6Vl3ykRcvf6leGrL9JEaGUJTagswi3fWRpK04lS05l6z0AmIIk5T53GfmD76lf8Kk/8/k6qM2FkPmnIKmpOCXP3X9j//gkmdAgm4WJqfOLCXTvQyYnRt/rMjBfcdSKTHvTYKbRxrf0iHNHfmuSGD3fV+X+aUcQotesFy9sXzNQSgqzuYxd9KN5loX4bNi5dOdiq0hFD80bYJosJ4JTIUEsXiMN0astUpiWMRUKLp2Rc9oEgY/wYwqj5B70/PbuxLeaaFd9REs2szzTJP86s6OVRtmCoWqatrMH2G3XaQc6YKxPHKAPe1CQq5UbacWKfKIqDrGmj3xKbrKrkY/0+phCFXTlLRON49ApPRJWZK3CtgmtwOT+gaWMubalMyX8oja7y3u5W51n+SU5Uh9QZYYTZmq1lsZT2eXaUdDZ1uxuOQABOZTK2dMg9NrgLKKyWU5TU4OjW177KYfrG9ZkvirWzavLV2x7eTCte29yy5adTA6fzK+9kWPsCNZfbHwkpKY5OkoXXXlxgVjM+N3zK8/ducTf/J9xXhIKBL3S738T/zjGz7dE7b3vP9z/+FsGjz//e++/dSL//O3Xx353aWrf+cbBc/c9FvO3snPttQNSBNc8a0j3/3698zNfzp9NLn+XQsKj/ihwOLIbrvlYVxa7f5e+Z1PxEHjxa8O0z85iQkTJ9wVC3At0FVRp8iUZMAYQKyJ0mABoD8wFUaBPq4NYZEpmecs/uVuznFRAWOXDkb7iRyq2w84wCLgbUaaTLkmN6o9Vt+c320fYVHH6DiL/LHMcK3HqKP0LPCN89wwFfkuyfKYZ/CBybqVf1w5FiL4sLkqwYplMqd6IFFZK9n8Buyef0ALpmZPIhfzumiL1YipBKw7aY/aI6qPaB5PHEoQN2yZHKfGMl04TAbm4abvRYIe1IKoTKNKFlZe6Pn5qvmXTVyw8ojZfmD0mAiF4WtP1JWWXtS2tM20fCBecfumeaNBWzB/XmNT4/xWs/qaPvfmtuPhu15tqsxr3L3SNfWtw+v2kpqZoU2QKoyuuOXJ8rV7P/u9j1w5/fh5bb/xrevv7m3Y9viHHpt5+989cMORJK1vr/xs63j6ye/KbHr/6Ovd4jMtE8cvejL78280bP1unfpzSegoB0FPb33Hy/VmczbWXHE/ui9KA7iWUCFBmRHhEPBCRODmqag0h4KL1LWpK/4ra2jNtJlbEDlNHtgonaVnIrwCSwrSN8GXVoMkYtBaFx29VpCbTKCk1YiduCo8gp1SFlp9CZ5G7rZmT2suPevj1k4ZQXElN62SQFG1K7p/jcAt6+FRl4jendkyKZeVnBrKFbbkU8zDfIUtz7BlejZjLxvnJbISQccK8o9Ll4k23sdebko+heKKgOKdmqOCjL2hl11zrFre6baZ4eFhFsSWhs5i0Wmo60PxJAeqLs6ITWn/qeFnB17pH+vffaYcnTo5Fe8qT4SdxbAULaz/Sf28i3cdDZqm/vv2iaX+u1/8SNxbXnJxtKZhXetz82Yas3mHW+afZ5yvCsDE/C7oM1S93n958I7bfn56/9WFbx264HDXHfv/vTzz1W8+/dC2wuh3pm4eGg1Ovvu2Fe8JFuw9XgmcC29JbugofK43Gh/yK0+8emOHZWux8IomISpt07KeVvPvZuTiya5dOqHn+ElBFAJh8XzAIJX1U13nmM6ClHjAOe5eCBgyC33cfqq42tP2TTEkAZxNWIkQd45SEfDdOuStxQet/hXr2uMryqM46yuWdAaVjCAaCxTVx413AFKDuoKt+qtanv24kjEBOPoSW16WJv248hIBON7OHvWN8oh9bs4jtJNLNjl8Hfa0Mipsbgf7JhlLhMn6uJBFIcGNgHoLGsxjEdC1tBp5wZSyooqpugW7a411YVEzPjT4bWZJy5JTzeYpJ1CWqc7lFEJVjWVKQm6RZKYuzRpL8bHkccw1Ai2zJ1mXsnuR5c+m9zSOLOt/y4/lpedloq145VsXZy+Yt1+1mwS3oTEzgm9EbvkzZuxPP/Dpdx6+8OWnPvpwssEcK0xd7XfHwd7/9Uh6RetBPwn3ywy7P32v70+F5n5z6Z5SfJN5pPsrHR/6p57heWjCGZzJQkTy+O6vXmFeeNOn3vaf1DUk6Y/qTQSwK/zI3IrUQMYjsaWQHEjwldaVsfZnyodgQU9NxLiAg0Crq/XMCaFoKNcyp2dCoSxhAClrtBodyTWRBljEmfnq1bb0TIBxGiQxrA5VXyzRU/tJoLengBtSK4RCyw1VvqryXGGZ5rRQDJmY76GqEU+QlX1L9NSvgMN85sAT0gmQCXzcgXl5EUX24+iR2OH0NGshewaiYcx+PI1E66SbRQToc6LHl4mSzT9OYK1qYex8kReM06jKKS93YczK6bDKMhUlv4Pdejo3wtz0wAoVHy+jcdrhi1Egq8gmHFJ2JzRpHFUigSaYkDTHHT9puXG04SlZHOuOt5jzT6064v7Dk+Pblj7G1PLjyEAR9kTtCr6+eea7pu5Pf7tu0vvDta8eNaZ5X1ycvv+ix96WdZhAlPALf3vV8vkXtY5W3LT1hz/+5FdnvjJ093NN/81/sLW1CBNELTbqBYzC8fHrnN/7/MIOIkidMhlRRJ30RENUNoMoHgk7QLK7nswtQZsYqD2/lMdW45CEnKCUTWvYUTillkBO0OYcQDmiDGXEmNDxIsPYyVDBD7nRtZEgeSFveFCjcjBfWro9apuiHDUv6nOOtZ/xW35N8Yu9JjNCn9NPxfadaga1ZfE4bctkn9ff5CU7pbz5dk1sQ8z7+GhCwahiqtnM+K6PMY5rWhBYKcQQZhTVPsd3UUmqXyGtltbA2iedvLw4cRx1KFWbb6QZRvzGTu9Ks6nULK3zpII2e7+2VNalbVeDkjlCZEnDEoD1FBjoWcMA6Toyyx1wYzPv+Injzr7S0KHT/yAwC4WbqJwyZoHU/8HmO5Y81vvYngPZ0Ud63RfiyYVvefLwur8ZvfD+KfPCZEWU4uGOH21uuw8WX98Jc8Z9Ybhv26E7D54olMyzSkRxYBJ59J7MnIOL/siv6ES25iQlfeT6VzmwDBlAiXSqnrZKHH5H7Y080M617nvl81rIjFtQfo3rwOGcMdVwkQxzNNdm8W9GbLi+TvUgqzHom3hJfF0XO0bqDXpapSXYGNGlOsTsNbAxxZNxwP6BnEZG6nerwaDaAcpytkXFzIcaUkX+Wkp1BFkDvnwlJtUSiqP2sFKbtLwafKKch9wPYTQkjWuBonQ+dRZfmufkhkJSqyL15bK6cmaDxXIY3/oibiqTvfdEmfAcwZprktzp3rK1O9Nl+uozqeLJctZS1qiwOFnfWw8zRk6vn5AaBCJNyun16c1nxhMnPm+qOI3JAdc/jkMOcPN7H3t5IClMJu7UpBGJnk28MjAz6novTzvJpYMiQxZ2pH5/Z8jWzMcFbv/CMW1PlJ51X//yxCuywJORQlQCj40pXZmEwfHiSVnRMFebDaehdkvBFo+Hmg8tnXpTn2JyP15YUSOfE1/WdEoABkyBawawnsm4axkNCfoSlFMsV1PNqR6OaF4Yxfi/s7Typi7y8ongX0AyXrgDVx1niUUNYFs+YsiCtQM+VEFBxDtWHNM3ua3PgjREK7hmqozDQTTrpunQARu7plAKWWUcp3kK80LqhDMGTMKWaG88XAc2dipTnu4RIIvM5hOWOpFc1jBumWnZ6klVmCP/vS+E7Psgfy1MQeLy8enre9j0IE43pxWY5NJT0020nNQ1HPegR8gHVo3DDcRsHV/b5RK/nK0eKDhEufkz55UZljJsLhgzEAaycO2AFC4ViO+9+5fILchw0yBprGML9l01s+lAsQH3dJvmfPhaxdFELLNpDFIpYggR4KzTuad3TqC/ns791Pb0Z7/ANJEhP7FyMtGIstQKgSB/k0fuBhk67EGqj5TcpJDO+oDxytsPOPmZdK43PK7mK+B1/voX2Lz6nHReJHdwC7E0V5i7nEWrAojpi399AgesPQ43nHVaK1195LWSO7BTX0rt5FNlmIF8Kgr0U3O+kr1G3gyilSoGfzeGHCAeW3vlj9Sa3Z2bgko5sip6ay5nK8rUoQ4mI8OqvZan1EHigcLbd6lTWuEnp/Vgndc5jsxfRzLBas01E0ZBJJ3mSBaNdDYThs8piqq/6CM250NLs3OPWXKY7Qn+DC3KFpCsTHAAzi53Xgjy8yH9ziq3ZR3+arkn23+BzCzPTc5RNqZWtsBQNvqeRP51Wa2ApGOV02cl7GIx4qvWaAMfXRkV74f5oKmObA2QzdXy4vOSv+zj57QlHflfPnJuzblm5qbOggKh5J05FIjaI/pJfe61UlHJPK/Qvud8Rf7QRyJyPdrTNqOWQb0lFKTaJfpuuV4oc+Y1ewqDcnYuo6JapiqjQj5TD8+Ga4Tu6weUBjKndtUqzn7F4kT7uvy7s7P/pubOtpFm0ok0NfQ1mIYn83VV1z4Fa6zQ+WrK2stBv8K8AgdY1JDpvnkWevTbYCaaGfXMyV5XGhP4FWH3xvBL+mDjefizfaWDKZdRTcgQlqHW5EBJ6cI4aHV9r7Eiq3hE2ho7tKYtKa4/qtzVGJO9LFaaz1PVNqwopEghn7b8guaPYVswE5vAG7iveCeJfapV0SDWrSNfcY5CWAs9LIK1AEcDqxTgWORlAGtnNZZCl0zpsUlaSOzr1CCZAysedjLBWwAjMDbwK7+mRGtskarYKxYjLt7FZcYH+B5I2hJgbLswLGvVAPHpB0w2kCpPVluv9nFjbb32d+0xntOv6DVGFuYG+txmwtCH849Tu7xViGauDRONUU/fAammYckBb70ZLjZ0Dje9UDXLWUMbOZPOsta5otpiqJNrMB099um2yIsEr4maEXOMgVlONEd5ZMG80ylFtlx5LBk7ugjoo2Owq9iehHIsVQqgTagewhlPQwaU0cC1VGN/sLkS2e9GWH9QFB33hD8Yk7GdXcnSwC/JwkSEDMnOMV/KYinXfBMlPul3Pa8MLJFPBflpqqJV9DByprMkUCwE1khqceWcRL7VVvGUooqfutpYBO3NeUQbS9T86iOsHbnR0T7Sl1neqOCtdjU66g2WN+qT1Uwao2a9JW8Bd1tuqLzXckMtk9TNea7K/+QRfY7g+gwjaaamY5FEerpOqzGbbSCHzqIgaRXlnXnqADmkbPtsjaTSdBnpjfWalhe63ZwGCQR5HbmsuORAcabokgmJHx39KiPyA+u6YhOmA4tRpDphfm3OIeJfd250OcJAJ+6yHlo/8AXY+mjNvvt4xtaNTALOBBAiNPkDlFE3g0tiAlX1SStrElRfNGqi9xmkUhVaJxFxJQMsYVvRcESnqGMnIYQI5mGGEsTpLNf/fRWnjqpenPaZ85yuVUgrG6ofPA+3x/oT5S/JD/w7e1BTQ0joFDKYD+gjuXDQKrNwuARL6yPMdGYFJAZ7Rm1gKj4eV4lYLS+nw2spMC1xdinxvmpnETZH3nmn8liFxSh/k7JTyf1SPTPngHuRxeaszlVKgBNoq2h55zyHSp3nWbDlJXqJVfMxLWpesPA6HklBDWt1PCl5RTBXO6JOl4HZZsPkocWwD/NlzAu/WtiznuIRXTX1t8FWGaBlaUmM2YD5VBdMlucwkL6IdJ91fU61ZmUTphEKk+/gP8Ymlu4it1v+vfyLO0m5w5NuVmlWYc9g0jUA27+11thBZDvAEBmi8VsMfqyKWsCMzBC19gGSuOzXTnSvXesoWK1+MHytup2XQ39jVkkRAt29yt6NqOW7ahTiENvRhlzWwYSLRV9SzeKg5gAGjENReQmW1ce4Z+6wtIXWj8M/zrDjYdbAectpfd7oAEcO6I35XLIvyQ9zOlfHGCkgqmOzNpfs3bWEHtXvMtCIvqTcGd/ju5XHNFRVTvpFYzpM5wi7XVgY/4Vqbk1suPQPxC0gy1z0JsKiwL5YCgYV2aH/gj/zvKc2CS1oRlmv4fKuX2EUs6qqUoSsPZtRzLy3JOqzkPVsrtezS6jfMO/JYfxrX1VPixbsvyo/rALL07WoVaelUyXnZnPxb94SjuqdFEyxCB/QBLek9EO+eqp95I9kpI2SIQycjlyrx1XLdK72NPdatQGyc7Qnm0sXw4O9WzGg+nRygD1Hh2As1Aqd5gpX9v+vsWijXCtgU7OzaN+O8zt5LlNvw8qO1pmRhgM91ssN2eTXBYtxzSGWzck3GHNxkbhAvRwA0j7wq/HPyxPpmfUnDRuukjLPrcghqfdiBQBRSOpLgSoCegT++D1SWVIOYamKZ+PNcL8EiIoEjJgq0xcSJ14bUsyJMtAyNcFinEIxUDypGyZmzo5e6LY5qYFJrBjXjTBIwudVRQQhRSYLwplImkS/sB4YdfanCDJVV/IlLrdvArARvYgoF8WH34IEFS7rskuVClAemYOy6RMLKljCa2WyBcN4wvn8NAe1wuZ36xirPYKjUl6npk0BpMqjMZY1wRS3+FTLq1JUW4U/eZWeTrQaSDzOUhklY9pHLMTnuTyCtFoN4pZ0/YA0Yk3OnnLVfUY39GtMztXUbc6NgC3NZeqenbGdXOuaU0btNZz2PVKKYq8xlbMtPyLoq1nOz8q1uWbeoYT8fHbsz9q1+MA5p/M3WalFJ8tfTpntn3Qry0TtU691zdq1zjVSSTn5rWZDMpW43qmE2oT/X01L6NbnmsLy8JPZ02yZlj9S8rLCnGsp+2E5oZHFh8M5bzJJxeMrab2mEzWkv8qrwWn57dyP67VfczrEizu3itXyUnN999mn5WD3+tHT+cdrpymTnK7oVUbRrxasbKo3JdfluUzdNmA8SB7HbeZME96nY3WCJVU6ItsMQR9QEp8xWUVUAc02zbrqZOVJRWyixZxRaCd3mRIGI/DHQneZKTk4qFiCfQ3+ZKgL+CBREchFZoOvGyrQrUwfUm7B4uUKwwDJwnyxqLWM6y6/pvNL/h96M6yf/DjzLTzI3M0JyS6cSlBKBUekAJyLpZRlhnMyrsUQBWC8ZKuRlkD9kIaDMZIhu6CcOLbyuDxRsXsr2r1R9bcpCO68ZJHCN66V2bYEKDeW78SYMfjza9eBSBAEGykmdpcZhUooK+yy5OBXr1zHGZWELDqeViMvauk6QPf/W9q5PXl2XfV9n+uvu+ei6enuGdnCkWSMJaaw4gpQFYwkSzZJVVKVGEgejPyQp+Q9VTwkj3nJ/xAqVfkHUrykCh7Ig8GuUIkBG3ANBgKWLwR71INmpvt3O9esz3ftc/l194xk05bPdO9z9n3vtb9r7XXh2NtsvYvJmzQe18jJuaOcpF/jIbqxx/pJBfEESMYu9ulhD4a3Ap8gJRjecdxbN+zBPCD+0HSmYfN5MUdWwQqXBerGdlOWdVMn1Sb41WK3wr2julFJtzy0ReVzYbl//fSYAMGn/90P50GKmxHbWfGqOCe52Ya4QkTtHOoIisrR2CIgcDnHLAteZ3u0mdJHn/7rHg+AruGMs3ECEXDtndi0WxHQEcKAsUFSYmj1CocFw0rVGQFfMS3zLAOR4VhubJ/LmOntv/xeOvsWEYcqtFboQrUJkA475gw7rve4qaUKWiQx4vxrPzVGWDKELkOpwQphESTAEm8f0kXrImVuS84rdXre7H6AUWpBBD+xgobrwCD/EFOXKTI0GlrKbEu07DDnZVHNx3fWF/WAKWHUOw58zRQ8ON3frZxC8HYGGvQRVsURKGP/0a9KFEpiU3tBoosttDZOrY+1zAbLjcXseGVl+VKF919vHegTNjFPtn4H3ivuiyXoCG9QNyI2YRvSstAiYG/lScNlvbgbXXey7U4CurTigILxe7bpmoC4wahSi5U9gvueqtBmyKBZeBfG8N6yUFqGZq4l5mkl5jdn+yscUG7Qm0iNyZ0ux5go67aMhrHqHY3IctndC17UHtexbZL9TlJU9AngnJBAQda0iQMLFS191zmwwpB0CGMgEsHPk5gtaHBfIlJDd7jNrxPSyIYugdQ4nMGK2JoNUQZQ54GSKJNhtvm0UTvgNxh0wA6GwdjDyfGy+2S2MtuCnZa2xHuKI4XGh2iJFb6HLoC0TCEbGO5QVygZfgG6eEaEhktExDqQL7RqfJL4osi5MrPcaS6W2SZgT4IUGs32o++cEuo0GiyJunjAhggQGonRVQtdRL0lxJUVUmjWHRm7UvC+27zhrcRan9qYJaoUUkBmFVM1MOZsJFEyVN6S9WwM0APODXGF9r0nZLQ2MCIJ0byx2uHwMQ6ARcyS1zrCHCxxpwHyomW15zWElnf2YUO0eBJ71LRQaGnqOqsSecjs2wqdUkVSxb0Ujty2tFXHnP2CzpT2coDFZkrWIV/bLDb9vuIoaTsZD4Kjbes0Iy3dE7ZtljdrBcLG0aKwIasVfQibgM5XDR6/hBuZz6LnvGCwNHH2qY8hhK1g2xl7UTMvnI2SUDLlRSN5BGNIEG8JNZBfFNwtMA8qChG5LxACatvHKdNmME5jn3i0qWFReHEo3DZr3GLQ/YYlqM3TUWXl26/z7YeCog0zopuuZcexu9VjJH2pgApLLi4FVgHHu1YBA8iGDb/xhtOq7NZ798LB/fdO/sTOyDJrFUYMTftGkdeKIVhbCpyL0mIEry7otYWU4TS3aTP8gijZuD4un1BNyF49Ozdi0OOQu0Cdv7A5Rs8lwQktxSFuxq3sNrQLVGo73dm0neEkDgKZEcE7F0ADq86zYLRlhYSCLH957X05zcV2BZu63n7Dja71GbfqoQyQM0KvFbpUgNFQe2M35OvVulgv0r5uywaNQNs9o/h8wXhluLaXnFyiaivEVjLuUaLYHecxXVRZxkVmTCayqX04FYcecDWVxPDFYHiseYndfewlWmfE7ZHpNi+6gpBXXqpKsaHhtqqVl9vC9Y9d0TiA5CqXk1O5sfsqDmF7gw9kSup4FQrbXmVj/I50v+Vwwg4g7sxS92jh7nfHOa9co2KU+asvlC1NZuMtqzevn9x/772T0d4vAONRD2Gp20HtS1arOwGophBEvYu7g3dR6SwmRw00+y91+V3S/8WNu4uoMTcWp68bItJBptiIBJblMESdEpY2ZyMiu8/h8G1QRc31mJfUeXGvflqQI+22FdeUGG9WsrKzma1toW2QnzKN5dRUeZEYhd4uVbQdaF/12UAA4oNNK8o0ZvF+QtCn4pJZccFHqgd0S3hNdL6xKk9mp1TQsymZnEALIrTPkuPwVcPxMZQkQaiTTDESCaQyVgBEMZYn1kLRMR/DIGCBRJgUHxriWox9QX41aDTOHmMXXYVw6GKszxbmWLn7fAiITIOA/H8BMQiaJXKHLUQGq7+D7m2mAbPgSWCb9Deg2ckIZuXYQCgQjPv57e/iSxe+nncjFz9hciYJpYiRNZbFu1xBOLq3/jhXnO9C6E73DSH9N7/5GDPUC9gZ9XkkPVZxmAFdpi7IOVmMnouqegZuVFMQPxaoWyKrJqCpwvNewr99Z+9sjDQq+Zr29hcYgsu1jJD5cgt7Q40T+FblCWV2+cgnKMsAmZUFkEGjBccRgCiZKMNqtC0qZ7EQzU0w3ijiJdZimNxZhOfkAg+hDwE6Pe5tZ2wSmBp5gMuBcRNd/vLxqf0vnE5Uq8CJ/gZLRzGC+hF0YBPVUj5gXWqjWfKaDYPtJ0s8CfssMB3XGGhpkYJkjhZF022xfNAKB1HhKUnBU2R4L3PmHMg8/EVQBRt1lPyE7hkNPnAIDQxscISRkGgVP/fVc5cYYowgMKIfIWLBY+aMZEF1a0TQ/Qtc5hp9de0+ezFQkAY/yWlXIW9GZZlOQx+Robg+cQ5VBWjQ0QAYoWntWAu8D6hDIlyBLggZ8NshM1hXA8mNvG/1fB9MDShl7PGQkdM6+IQZGpTqsXCWCgMWEQMD2Wkt8hUYoRzu3eg9flxkWkYFnU9AAmSb424lryOZrmGYuXMnmY1hGK6AauuUsnpdQosROvgWqCUYDAw0XNlqXWTd8f1jg+7hmBg9xq0kB3+ErRFrcLja15kt/MJ9ee7n+aCCiT8FkoUDomsnlORnh7OQ1/++85PfCQBABQ+m/SEFeWGfHLhALQGcCaRF72x7o13J3HjwAhhQ+zBBg51twy4sC923N9a6+iMv/nFueCrH9WJeh0WGMqd64PgDS6jxDt8xBhWMaDDe+A/vDDAw/wIagh7qBn5NCN3ruKYOmUEh3I23OI5WfOQiy2zPpHXXLARLGkck40AqikGDFZuluKNDkqXj2Tu0S7c1NmRtxHdSo2WJTWiwcRsyxpfWMVOuhYDHFhqvbsCQqgexlsAstq1HONgdEAOCzLAAbxsndw4gjdEdbd4wSSMAVd3ykMvEHriJ0nKTFY01IRBOuO+WPx/u48U0FaN4/xXZEdlK/CdPqpozIOmvczvOcf3OutZJ2997jhf89ggWn3fHL8SU9CjgKhj6cZjisdq6d9jnN7/xV/9pmRm/mP4LNliZYstZJ1n5qzAmBC3s7PGr1lO9K38ZnK0/SpxgJ4Z87CzK7FGU9mHGw7KibsOZ8tEXT76aJs8n8D/GtIUXUcKlEc8fRtq13kiFoqMbeAOxlr19rc5KGNPyJVAKAOf6Y9sfcEXJjRbmj227ZWrpxs2+LOvWRuOJOzjJ6uq2k8bQXuPSJ8PPBOb7nHf9Yo2ZJKfQl9guVlJ3aFytnYqL/hO2m9vFXt5sDjECEs04pi9WQPqOYylr0x0X4XTtl1pbb3iXXvdhuy+U9LM86NONVymBLActuuYJXXQE1bc3jCzjtbM/a7qFsTBZ8dqnOIf56nBb5m1rh2D/KM3U/eSwpftYT6xKuD6r4MYd8bJ9uPUl6D/jepMLMBr9i8/hAAU1wgME7MTwWi37OgOsZ1/Eb8v9B3CI8vnw3ZM/FGLu/uoADwu2WqlH/kn6b+5xxNqB+15je0Fn77XhXfVEWMVo4gaAxhnUb7H/5Lp0Y/srX/7Ov/7BXv4fX/pqYo8H/Re/8PXbyRe/8LW//g8v/cof3v73L/3fPXt8tf3iF/Tua6ctH1hyaQ9SXnrA18piKXr8Vs4H+Rd/5aUHp999vJdZGxad0bgmbx7JY6mRxPMarjiznZvv2STBjf3Jvu1fDOu/vcVVGlv+DIENMLa+lsCIJQjFcShKh7IaCRo4KBC3sE6SayIAOPxbNfD2nZyMcG1nR4OdbagHEYcQw0BM/b6OBgeWVRvDTNii5e+fdXm67bqiOMdFF1m6FdTAzq3uj1CN5+BpVoV8Dlibsc1ty7wy+ppv+Lo7pWw7i7vt+5nudBumorFFUiffZEpQ9GVPoFzZJQcDJ/fg76xdOSdDk291OZvmCzs64Jsy9oU1Ne2zsgLVGJNVn2EaYHO6/XqCA0ijCXUDBLeef3eFpUfepcnKHUAWId3rFwyWtflAPh9OQvLP74XTB0jjsfeeyXh3MaODWQkzEM9GT2oZHpkd3QvojtJiAd0OMSkwc/li2A8vfiesw/77n/7Wfnj/cL3Jile/tbE/XvzG3n5Y85/9L2wOA4/1q3/aevL7n/7T9pB/Lcte/Mc2xcb/3f+b5ulok/ZxJ3gVb5LSXnpwFV8wKF0A5UiOtwkaEEOfu6hVQ7MjnL6iFqFdSerHwZLTOK9ld9jHLNOwfxgeAreRazWjR9MGXiCWJK5gnMVnl0QWfHvOry4utclLujAqKZ6sXDfjV45PwysPw+mgVBOexiHOx+epLBAavGq5ZnpVaqbH+dVMU5wV8oGz4B88a3wuck3zxa/lMV2VkMVd8emdKpgW3jPZXgYZ9Sri6OysDH83MFqzJaA5U3F6MGcqbi3nhWNxLXdb2gwkaxuqTV5SHKxh4jiQdydOO37oC7v5MmvLO7B8bBhM47j3HSDb19r7qfdcTuBIZoFidkILdq92pGTi6v/x/kwtuIrt/XdRGj8Yi518o7+goTuizxHjImOT023w5FzGJqSX40RAUjMbM0R2qDzzLsgrgnMFKUgwVkBoBTupZrUAOQQlg/tQyBxmtiMm12PEuGqYggnNoLqxvxKJplyVkoV3GY5WYjfA5J0cvkq6qGQ3mcJYzOp1Z7Al+nGl5LmYYiUTAyOBZDZkacbeGczf4gpC0tAt9rDGDZYeqUsleXG4XbDmuECSqGU2mlTpo+KNLoM7o5Aotono3mtR8tAX1Jm3g5C0JRLa0CaFCdPQorHt42tHa9hIWRumaqthb0u5bSCLNQzhNSjNZnhHQuslaYYHZgGIj060GgbnoAHBc43B+GAo/n56jyV25174TOKIFpzmYG3nwZWH3rErwJPi2uM7CVAhYbzwb+SbT/l4JJI8JogcUhhnNpEMqiokemNJYq7J0lEbX/sdabIj3xyldXC+aGS5eDcWYqznUMiURaoE+sO76GKIy13clWEO926wpiCQWAH7FJl9/NA6JzGE2iuxgASwgPMcu6WxgkFSClnpYr2jDBObYRXX+ddb9Cy8cokvvfJhfPXoPNkrgN43bhit4vhHVXIlM3AMCRYKvJG0IRbnKVZCVzoPENvE+ahCYsMY0B6TSxdVxGH3r3pVpa+T7gQUfz/c40BUZLHf+MBzRskfIDBz0i+CHXYu5IMMkOZyylnxlzEcOG0sSTT+Q5zGXvmF63seTIIXN55gHwwmpuQPki76ITIoc37gSTLr+Vyj4kqxJxVgYdGS5Wly1aHns8F66gE+Vv6B0GVMvqAekQiP4hzOP5gM2OKc/9vT49MHd46Pss8Q9YKQBB1xgAJiUm77NJH6hd9GLdM01FxyJgwVFCiBUlG8WFaXT9IrRlOhSRAT6mP0t6meNe8WPFpRysL+AYGx8KOmo6SNuv5UimWB0tSQKV6qXumy9tKF3KlcxD/PwtroOBTE7cXHkvzRIaNs7TsKhHHinnVQqgSeqhveHGCPWicjLqIC0CUmwLK4NgCa+lIeMfaPlYweJiLFZA1Jm4rrdnrOjxXS5ngdbypZq7Bp+Dod2oukQdQFzZOEpoLE/F0uRtLahF6RzM5ILqiF5TGNyoIPVC+h3ikEc+wSjRaS1ejgpmzKgk0dNMDnTrw/cmrNuSZ3mCnsVdEXaTCmCaiBv2gw/h8lR3+eh9M7rzw8+nNdUy6k6Zig1snBPt7E6Mol3Qy3NJvUFjuXL5PuYmgnlUb7MCYHbnBqLCz0jimPuouxpKmWzsq0r6vS35FsfHGsYNFNlY9ZIGxT5QRz9+TWuGBCzMoBVVq7UqXnmyqPQesL5Ru6EdqtNDNj98fexfYODdvEd9kWxdQtJrp9CWInuSkTmupZmPcC/9M7xV1QcbVko4UZenA5Ht4Mo+kdFgWY+CtL3ipMfcwy3IJ1eI6c2tRkPiDJhnFCWXaqhSwo0tao68yyxJLU6ClLg14fdwCVUjDbJLnNc1ko74zKgvZmtgq98mL74Pj4/oNw7zi9/+D4YfgK2wfClPWPCdjAeqzwX9y7Yzn2mZ3+Sw7XBluEIlnYVNiuP+8dovWobjqSUbCzKi/6ZUQUldQs+Q31zB4flf1WE8DhjHpmC11ZN+WWc7hu136O23+fU/V2xNxdiI0c0CBkf4kYU/gl6GKnDcU2bWv+TN6UAhjGcx0ROxubwG0a9rQhW7qhPT9eHK/x6snpVTyWupKltW3IF/0yLzYr3mEoW3kJlmWt2Nl2Nm2C0KARykMoHcLAJ95pVDBDbrC+bDaP0VLhXf+Kl+0998cNvbM/NtyCJUld4a7XWm+NPh8iJNjRuw053bjbdgshqU+9btyFJKUqDlJ53sQB6UYIi/PkktBVFa6FvWE1TWUgNWcxS+9ZJE/ABdHaZs3fvQk3SAXbz7lSTbIeNRV+2mtp+2YV76z69XA9Hd3kXjv4oxbg2aQHBM1BV4jrgx43ry5BRDFtn6pKDJxDjEGa7CNhhD93L6wQ7nzbNfg9LVChcWdh2V9J5trnZdUVvXGAeZZy2UgW+0sCyWzR26CJzBRAKhuVkH+3ka582j+ELiOf04GRcU+DIpMEtxWHmhUQaoyuJdT9NlcwcLxdETijEDYWXZNZJ5uephLcGekALbBWwllmOHO9jhUXyfiZb7IF7lsN4JKNKBoN3TBSZPNgvFuaEWZDXydrlZT22R4+xyQptVEqsF4qFvgQ5V3yQ31N6GGCKxVoQLXygYp/Jv5WdL/lPn5hy7wpYNJiFrw1GZU9L9It0L558DfGzepdCeLMsRVHSCpVxxJmRr3D6TKegrJyCBOWENRFM7XXgPc4tsnCekHmqpnKDN4tbP3Z4fttd/lm/WEWyVKgFqYQSj/0WgrccNB9Iwy3PyHXbSeTsdh//mC51oQCeyiU4IPjPtb+XAoYkx37f7AodihAPNGVfMJVeH0Ajk9B95eYhQ8j2px1kQNhZ0C8pEv8Rfw6fOimKC+nx2TbMJ6SWLbEU3KWDH/dPV0Od0FwttsN4UoET97eUco9G0N1I4hX3KlFj6eJ56ZaLjIEg1wrPQ0PH9h5+BV0dgKRF2FnOY7gTKEUuqfnWlvkTmpCnMRCu8OPdHIL9/SvAFW99Cm5gbaEzmDcdZSWxUYPJSW6wwcIcsUvGI+Jpxj7oIt1YKN0B6JRNs2KagP8qd8CRwcogHL4Wj7xNKQj219pedvgGd/MoHoXwcQqKZ69Lo9wvC7lAuCpvlZ3RulFbK+yDP+inS2RRoDUA2sT8gQ0xwOLcRwpDZYdv11LzG0YJX+w1jrgMCTZUniA+XG9LKUrRMERkw/qJ8SD5w+4JZqhQZKqqivSohobuP6HVR3bm6NPzB8SCLGbsW/lhyGKjuLsA6S/GpChFo2TdOHjGZxD5aJG8nXXSEYD5L+9cRrC/aPT/DiENx7eD/d+vwoG4wGlA4yPG0hIr1lEGC+k515i2Fdx41VscXAhG69wGN/gh6iRyYNlwVOaIpRjZuR40pIFDq2k4iJkHpMv7ewNOoE7YFYh0Df0kuQNSBqvWVOWZIT4uJpxZDqrxWmEjMXGLM5xqAWXYLw90rqs54Zvtpkk0azS6EhK+doxC8VNbIcU1oYxjGwHJYkV4gNMEWqVtOgxjDO8Thbu+4b2jjpugVhispUbxzBbd/u9q17Enk/jC+7uYBGG5KKlbL2zNTrnrFDwV5ZdHsJL2mthG1KNvQ1dFufcaNf2Fx8e3w/hocH4++FhCHdYr5KosRwdq+48+lHKN0vuMCcFADrWnPaCfhsFqDwQ3fk2SNHgvVhS4lRySsYrJn9IJtXPSBA6TFdl0b9qE6CymYtiR+mtLPRntUhjSe+Qu4zJ/Y68cJ48tDdLNiIOQ/Ik69UH8ywcLLwLTTUA3Qtt4mjYzQKuRp2gb9EuVsM6RKLJ1LBkaBjTNtBdr9xOKM0BsJiUi30R7Bi+TlIID2IMOLRxQAb7Ep1itc77KQulq8cuLAHYj5VnMy1TuW4LPyrW0v1XxB8f8u5sKGDEPbvV+bsByVxZXXq1NDTWO88S445dVn+dY4x4cTdL1mO8wNV5Mbtl37kKbqxDs+JIFqQs8UWxk3yhL5Y8RRb7QHg2b2/GtpnBxXYaLAlQxwp6Rixh2VGIv0vibSMlSYSj5F1FWp+pqb1j5aPBHcmjVRiS3TjxlCTJLln8elqRxe6/9+o6fO9bMBMb4yWKwCQGAhjSJDur0VQJHmCXBxOtdyzJnNWd6UY5wcQ9BaiID+lY+5IS6sEiZzGldiIiRgSvRJngIJAE1QhMKJmNQHLWD1mQhnbwcirOs0iweCkLqBR5X0/cgk6W/cbnSRBKBbKyYgKidJEN5z0QwYZESmHWZa0kyiOoeqfKc6TjUxZPFh9XImpSMmXjrwuRappXUbfTeMOCmj2L5MDURkmWZ5ID+2BJUVUVeMPQQNWH6ifj5aLNHNFmnA5vusTZccR5QPWZS5WE4g152YB8qPlDFu998X7OekfJW6qFFkUBhca6Jx6z94UBodAkvHH/xYN1WBMg+MWTPzwJ7/6Dv0F/k6zsTf5FnN/avO6sMF1ZIJSWpD4QUCLkYW2stTYEc4avTq+PiwOfaw2Nt5M7hmQIQwFQpsUcbT6vLqm3ZG80xdkJpxWlyeGSXbNn7+TpJ7DbKISgzSQPAnEtNZhZL9hKQugdOjxojG3yppIvLn5oFfsTh7uUCQ8AGCZpXJvGlA9DGmchxIHvIt63ZE2caIp3DJVl1XJhWYrVdg7ARkVXCHqHy9OxApU5JKMCMa1NQhxp5wWBkNmwz2tJXBNQPZccLcF7Ry4hPRcHhetCI9fv4vj27knJ5frjVUCv6aJy6+xemkVXG14SWT724unqY+uj72WfefckGOG69z8qmLq2LfOEa3RUY7mqQSwy3Pin3NPbb01K6Iroui3qOqR+VT7qOsR7eikuRPUIv9pXbIYrtHANKrqinTRtRzXqeE8/uBaLwW1dBbjN4/V9J0WLwXtag0L1qLeha/+om0xTc3QOpjYRpoa7fzWVLsarfa7/FUsCmyz8mliyXNZ6w9q5RvGUHE2mlDyvRQ0jeAGGWbJGmw2Iui+lkdGKDffxUtlQNwheTpZOWTygsQZE+RrMGWzjkzxk4eyztU3kYa9KA+lu1kaDMJLdUi3Joy51QDM7SwoD7ZzHthMI6Wm/4eEpLSqnKpjulQjmmLsmxotvMKqyTmfbk5ODd7/73vWD9PSeGyRGY7Ffe7wJZV7ZEfpag1DWBuOd7RZV3mrxyVspoTFtgT0m3CB0YY0WLRuxfz8kipSWJE+IiAb2eC7hvsV245PaLYyS7hCOx34Lv3DX83X9Ly6gC7Zxjl+gdDv80ncW6NeSxcgwTHhy9lGvpU9WCerIbMC7DhyT/qRohHG7/jVaxyJ7Z81iMxJyV3G8GhuZjyK5Rgi9up0HLgX65Be6GtWZbX9yLs87tt2nLr5eFkVd2wF0u1sUlZGK4hHmtPopvXVp/xNQev1RFYhkbVRu1XljB0XeV9dLKxpHis8FZPc2Bq8/b3lTzhQCKSpf+paIkOGro7LqsDtJ+0qu3oyy3N5utjJlXLdWgXGSff5zrcIJWPcfAdAYiBsN8nUr7tA9cLRhXdIHeyTr16W2nSZvH/VR7P6IeqF7yS3vRp8fDR1Lj8BOtg7qowaBSNt1R4c0DjiRE+iC367f7KSAmWRHRsslLOl+RprMIdz60v8K4d6de+F+8uvhfnjjYTj9TYjdXFh5pT4lh54jPUDlABwNePqR8WzFNaFAiXOVcqUxkwqbQcmEi1jBoaGkFLcjRq9Gjbp4EEO1ObsHdE9xQuJDcRGvY1aWSoiIxjyNtg6789vYl5hlDouFAq7iIWJfZu0deIhY+WUxLe0ViJ7XIk4FDOLv3vryjyA6vjAbQJqnalSMuHumUTGVxGPSfdxJfpo8XPMy8mHW5XSA8ZOxWED8aCtjED9y1weAiwLNgePPo9h0bvCKVJOhkQ8Fo8fCTeJRKcnzDV/TNhqNHKRnRYaZgC9BCMcHY9m9KxkEuCHPxxouQg0zhRU9IFZW9PZ60JtKck7IfLDEdTHvoMwkrxUy2vIK0A5BsV2ttIXrKFAlSVBMMp3ujDffZ11PrZMxCq1z23nJTa2IBBWEEDOXkikyEC6J9iwOnpLR8ssHK0hsyXb7MqMqK/xo4Qakp+nKR8tHOTCbeZwNAJHaho8KfuM+sGE2epnAvmVD5oZoDEMcttim+JA0FEmrJmlMdln62E/+YJBcocUr4E/Ld3z/+PhheHgc/cbfOf6vkxslzMMuiQpT+c8aRG9Yy8d32IF5suXfyeLSxVEPQvn8Vp8Uly5GP02VX1roXR5dlstl15icjHoQvFNTkzI2dVYByaOrL72DI47vaCrJXXNB4jnobUx9udiNIXku8J29u0queqlh0bR3TJbYkyxXij0vlSSKMdYyyVyfIQeOlc98ZF1s04XBGt7tqJj0anTMIvdjJKvR1RWDde+z0b+WCNYbD9ga4kfs20Vc+Tz4Z1i87Km4KyRoc/ICUSHZ+aAh37jeWftTsti6BBob82njc72h3agsXmzEEUMWzz/+RlOTQSZIh2b1QiSmsgeZ4LypriE53fhfpZhKvVM3RlniqK/KY9rXQ4rnU+v8nXdMD/g2zgWpQJCSDO9Eq8Yseid6onEak0ebVLmdGWWuvQSaJPsMzbPECkZdVv545mANyRz1U71O/0izer37olpgHs8yHywP8g3G5ucrd3zViEqOPnJ48I8yD90f3nHCDu/iBxLNf+Bw0+7xnXdIerVEIxmzJJjaekmjLsj0bv7gX3ymeWYeNIvlqfHxzDtfD4+ZK4g0upQY3vXUSxZOAYZGF5deSDYVFyE/nINhZYTLSN4S51S8pLgxeLgTYwdVye5qnRq244xCP/7OlbU9Oc513NpxjaWwDHb0ReWY4RFL0geeIj2csXLfvgls4KBZM2YZC1E3EJ6T7GvEkwfskrAXhq/fuwLG/2gaCvN39PfpZjT9VZJvL8mp7VDSj3zpfqGkq0TVu5BZlQO1ZqLqt34vlvR0+6cruBHh5rEvsqCBq1D3O/mUi0CXLJG/8O7Hki6M79UmaR+u8pg8eU/T7UE/YBa/WXAYT9TTWZbdyb3AqZCsD3dqIcsupzIrqQfGv/Lnx+H+G8D4N+4Y3XoFEV/aG9cqh469b37RxQurP/HLwd01a3SihjR1hDAms+/tbsjiosIhecgS848P2AHXbGDxslE5BAg44juZDYodsvfhQsPsv9LPBiYI9sPfpUiwHXP2TmlFhpx4cJJ8eSjJVS5QFXNsHPqCIjLUwYYS/BYBMCu9jbFyLjajQ7JYi78T6o20s8e3mDeDP7yk3codFAQgN64jOFR2WSGdKE71YwVqjrJOV4B+gqXu18EPoZ7TOMypy/jwCgLT5ZxKKy9+BCedzslpKXQi+EyKkeih+/GdYPxXjo1uPcTnA8ZiSfKNJvp8cCugUfQWrYAKt7IaRW984M4dBomcBJJ4ArX8g6hwMsySQHJXdjrJBKM0lOQU9wfY9E4OEdoqCknldoF4ETFoxKxyFTLJVZF4zuSqKQJQikvll8HlqirEftubuqHHFp/PUbQpea57tsLjWwZyHWuxZIk2vfKpG9iJSVh50YcCItwOYadnIRmHV/PK4zsCbxiDUg+uNrAvcyGphz8eswxCUgD0hZLGd9OoBFmqTe0lpnDpothxcuNAIhedy61Rh9S7dpDCalSaoS+zylc/H9y/VnSXq8tBW3dvr2UFgOag1HVtmb/cScvJCCneVHU4P44rOP3px3W/qKsiTc5aZDLco20E8ELRv9IQ1KmCn3XBYt8/Rm8Tv7VnryVJtaWQ1xXFEHe0cgXLNriV1k0V8LaPlyxcriUvL6Q8gTbSsH1eS6pKosJPZmko7aOuW8tNTNdmhCkIEh5+Pgg3h+ST3DUbDQibFVx0s02Kn0QOWFvGl8+brOiaugxnxqrio3pRoQbmDhFQJNum5d4aqgQaCKvIA6RLwS2gFs4SCGTZn1UefLQIz3NDWWb19pHbe7X9+afS2v3W3vNuGIl+Tb+1CIP1FeTB4VcXDm1Ay3JVl4RT9Z6/5bb0VsJjfWMPZNRCfC+/3uRFXdsCfNwt9uSt4EloF9A0BOVWrzW+fBJhdmKdjT+PMeFjZm02vGHp0Rs215UxkKs1I4jz258skYNZpZujoanuWgPd8QpRmBGF+tafRayV4tr7KByfEpK5afI/qzs8wrVFcqNvN3Y6p/njVZsv2rZb9B8Le+kCN0Zp3WZFWxfd11/OAq4EkuIF1VFhmIL/vTbvwoMsL3LrbNJu8i1IoujfYNSyftHcLlFizssGIgKZTNMXmtDgSh4Vb8KpJE1tuwnfIU2zeb/fGvEq2mq1V8njaVPgaKSzXZ99NKtrTunWlp/td6xbX9hvKiJIFdmXUZvul3XxgK1vp0KRfay2FZJkWXc3rXMDDOWi+Kd9lrZ1COXxMNr1KreTLFvkzedwZr/IlvjZ7Fp5a/rHeHLCs+jHrcJ6a4Sx+iXZJuZ5e3xg42hEpq3Tsk7YHeUb7uXEoNP3rJ+Z5Vv9cV7hqsFW6J/kCOEN1HysqKoyty7a6Nn4cpGdf7bpuqosuopIwBkq811VJfIwsvl0W+dZZlT5tWaNb4KQr78Gq2Db4ODlBoYuK1bXll2X1W25CF+Qt1gr/x/Koaflq4/zgiDsRfN6HQranDdfIzIQjjc/9z9xTGNApvr4ftY1aVe3j9qusaY29iva+2XeNfmCZWC7ssqACNm67kJ2Gk4fnIY3JtdtoM6/B4wfkJ7kuDtZLtmw78pxx5IsWTEd5OI5k7AcCbDMFyUshyy6xsnTKlfKs0wMn92N4Z2zJB8Kk8csVw7WU/wrPFs952J7L6oH87jaXjBq1sT27pY02hL2V/iNG8b3cuVjm1BQABw/1bJ/5gfgX07S+CP7370jRuzvA+OHD8Ug9wDkyCordtN0jCVCxOOHwp9v6V1A/p/USE4QJgNgAfJkkR955XNxdISesINe+Vuz5K7u+n0YCn83tHJoajcmx27M3sUHmLxK0tJvCFyYEaUIvcszuknGwQKda2VPJT3F21syeXtrYE3gS8S77A4kj4zVI2zNrUMCVqEELgVhd4KkJvahLnJTZRn8++6WFJwv5goiDiRD77cHPkMTfxE5lfmA8DmJPS7uomwm5xqjV9TzuBS8kGaQxo8w/usDjLdDSNoAA4xvuawVPnNtgF0YDwgeAeDsxj9BayKiQHylpXg4A+mhNeFIOnoSsMffjijQGtsCfeXzIZVTVxUy9/kw4XX3UUHy9xyZeny36PPBv4bwT2B2AqyXMK66OOvLoIVgcFvqBvR8yIIPcbLkhI6fki+XlBZPdb+rbgyYXH7uWuJCxi56cZ48tslGBWd/yoLzWwXDQ/mDKH1RKyVmKbazkhYsCPKhXWLJqJO7pwbLYsyRO/nd4S8uD0iJaZp7e9NszJkbb5M8Bc9gPJKHcO/4/md80bOHuu2MY40yObYKBMTJhLatPmjnclxt4JiPQsZFPwj2vJAw0Jp5Ibv7hIraHSHy/F03q4r/YvKVVLZkk15O3pFTXng3FqcH5x/y2KHn0KpICVM/U59RUq4eYOJWphdc80qCQP5JGsrFgb/bORCGMaB3Mx1sdVtfQ+adTRqy7Iqzx8FCNs0flqFyJw2qyrPwYmAWvKRBizzR/Ig8DQJfJeMAYt59l3IP0vgpstiH8/nQqgkX0cl05DscepYeBMn+LSf4qAcxleNxbX9sDYUhWfAgq3Z9KsWGIqDcycJp4YqOKu6t35sl/31FsT2qpd0kkJxB0QjDhp7HvswEkvPkWMuPpZU91jKU5IDsKWhQUmpv0weiwdiwBNTiWs6jsVgIXzn+8xAedqWR/EXo0PBKjd7jPRCTpWi85JFqWQyW2YaEGFKyafpsNHWioTgHjytqioxAQIVIp6Q/4ZcUg/5E3BTx0WvV+h+uoQCPbP8nUix727VTEVZyGTfbMJ5rXhLTPEhRCQUW3Pkt7/COB+0bvkZxN4GkDvTty/GqSMmTKBZpqlzW8J0DFR4DUBHlwTDLk78cO0ZgAa9FAkloz9DeFBjm7VUoDYZLAkkRnVYfapzGLA4E9/tmz5MjdUIPAtqFJirNINmjK475eEwIVYj6LUeDPeLl3tFgLve9WO977+wlgTSGQiZANqSMv/XyvGv/nN47PTX8fpoj1jq+H+7ldVpf6fOBE5y77eh24eLt+leH63sd9fbbot1m9tvGNYbclkuiObIUc4cIV9zTN7KyGpMJmijHEdH5QBLty7xNsWGX2zTe04/xtXKEoPKqoHeNskSTKUvB82sfTbyG4i4YsE3eGFR5XkdDss4tqQanEsqHJdXTGpZhhjhL1rDxdebju+tCoozeGC6V1G0x2xkHiz3vFQyWX5ezXE62ueuxYssH7xwJfjriTNGxyxoV06jsrgclu43e5PNBFj0gLj+KOX1Y91rgcb1r+3TaWrP1Oj588Top+eDzfCzES7riPJ/Kjttu/Hq38l28NCbPsuwgg+H69aosabw82X03ryBGTIlZiArk7xoomlOWRFgqoJI7L0n0L+YjZzXoDiS0iWDB86/jO27Nrkgeke0QuMCTjVVCBMwx1KMKK4fFF3/iCRB/XKbMTy2h2/DjWfvYMceHY+VTPyHiuw1LPKjRZCyW3rt3Gk4pEtavyz7yuK3LRd1kyW0EmcjYfnaB8pedRfVRE11s3MCfH++q28jQjKoXCxyxyRf8sljsV5tiUZXpwt89fr3M67rOs+RaxuFSp+HRz/VEAcqb7meyUBmty5trzSBGvGFHAoaqdjbvGTdjycvnD2qsJirjDipEjDYhh1mUnVZ5F6WAd6BNoa7Sn/tEzZGM6Y26b4DzFsJOO5Py7VFmQ1O11q5e+bL8+Uf4xLYuZi83eZHr1D4sFoV3g+Fmwh7badjQoebl3FBcVeTVSx24JjRddSsjQFDbN4d10XWNzfP2EPOCol93n7rbIQPNmsfHko9aF4+LziWlyeGChrVt+zwO2REih8/1tjqTplscdkhRN9Vis+eO9ev9u9uKoE1dcvy4U1KZHxoSNJ4w7T+y13FLFormJVs6WW0c/ic2tuxs7ZT3HlfG59lne5+pEHd3RdF8prTpQL59zaCrt/BJbu9CVjYvd3jvLbr+/BhZLTXdLlogUdacrew8xYFL/fHCYGhe2LTdxJ8vq+v2Z41a4RBwR4F5R3vhQ4kKL4DDAX1ycF9S3OXdh1DcjckOfYcKnuU9Tck73tPmMPOyvaAKUXEk/5hKAlMtH17RuHdbwg9VUrRKfHZJsWGXSnoG2zGv/APYjqe1addSMhY3a9MMxk8/HoqudeNTykokc4hgdri+jwTQT0zevTXSRTHP/kE5Yka/u+cPRVxTIeNDZ6+gJ+/e+vKQnI/npK7h0+06InCxAdaVXgGCBIpJlnDDSdSukDRC0HgkxzASlZNuPhRXoeL4Y8jHQr1KSWCqYHx3ZfAHXPvEI0Mnp18iJvNjN6bMSqqBwYkCsIv3AFZLRDkcPv61RhMVg92ShkHuB7bDRjTHj6CSjWBr7HPOtCgCgR/C4RqVJpMGCGyH2qs/0MgAXyZZnm86t1xv9E66gTt9cXnKHMbfw+dDWsvnw2UYn8iF2kXYNmG3bfLVbIDx7vngoh+2MYtKGv2w6VG0nmUoaUCmXtImlV8zg29tGsP6ugY0fs12kOmkGO1IetLQjZW7hwgsjPbcoxvJM9dtl1HrLoy/CswCmbvN6O0ChxMkN2UI25qgxWSRorFizU7tvaw1nLpn3Yvjy7C3M0XuGLtoqHyE8f51txe8vfIQUU8NswZ4cWHTBx5je9WwqYtDw+SdQ8nE/03k1yHgHIYzI0diwbtZw7bQFU9+Cox3ysSGELic7ypLuADj+SSuW0fgw9fS3+zwRzV5BRtL6kZJqbDqICWc6S5OX8fbFY/+mIwkaP4YIfOOcFX/XipOfzjBGkoa9YAbwMMVWfTv5WQ92LaNgJynxO+RP4gIz+XAA7FXScPITQ+kNwRvv6ToyQGk3zQWcUAYrEs0QwcWKdhgido9vWFjlkHEcVXDlKJP0TDXv3rnp0K707BZIVdpmXKrIfuyZ92FXpDk7ShOXgFLRGs/wAOuktGZ83efFd4iGd+aQ0lz07IJNbhp2awkJLTIa9td/MH5MIclP6bl15VdpDgb3w8tDb00hvOSxiw/gkv4SyVdhTnjpf1VMcp2K9fqupzsJc0l6Tsy7ZkiwcxYzLVMWc/4l2tQrvJ1adklf2MpagG7wCwucy3gEXn5wuXFsHoVLJZVILDo0jaRtulSefoaKSQpbIvhzHfvNsOHBXby2nKODBwYzS9OlAxTLb56SFaZWJVPjU74tG6ux4i1KfhDN728A616cWK1o23unOhcoeKKrs2+uOEoU0xlrsZvfg89ZIFIZ17cWIjKHEoaawFkN7XGMEkb5xDcm8MgzbSy4/WZP5KxTRFzziu4cGnPCul9hv3DzrNkkformW8ulBTzKcUVCdBd3VUkuPJ6usQ9YpZaj8YrUqTHyPUU4mqKMpW4AT2+GOY3mPEKWvekxI/ZyvuuFC49iteOlik6j/EitrajQAqb8d3F696U2GYEEAUzNaXehfhuvBv3W2tv76j/qYcHPphuXHV3u23LVM4AqqabfPlPepjxSjdmaeTLn4YNKq4dKbP2Elq1QfiKBlq8hzYGtL94D72dDOjHLnoks2kgecdVb4YMIkZus//7GLpuaDu4QRhKGmOqXSxJV/z9wvAWbi892a+gW1danS7tsXuMxaHRdaEk7PWHKQkZkSDGwAejMrJCEsy0TMOD4Lcc7IWQ64QeFq+LybJBHLizF5zh2V3Y4yPBvQ2KG0WBi8DxZ5DQFWJ8xmR9ssOwXhLwpbpR6stRFDuvHEcrU3Iq6wx/F1nb2dccS6REh2Ig4hn9G6+g5mY0fezisG1FgUd8p5TUOTnR5Oi8MH6NHEq/+Rh6/p3HeIMyLw6V2YhokqSqhq8nWnNRrrojfx5LinSohK7xTmPZxjkZJgDFWBvyYhz1aWbGn9mEBEUdU6LXohCVVEBIAv2kp6yte/fuS0wQwq9telvK9i0eG9S65si9LPfN6jZAH03ZJzqQLfkQnw8Scd8ezo7uVpaXLeqmh+1iYROTdZvaGL62LvrtURKXz60jNc36+cowNj/xyWHZnXgfrElveyvb1XVEgQzSm1kMCZe8w1kH8L3Bokq44XwNT0SkvbNusjRvu3wdahH7rr+BiktrMOeTj0Fk6Cafx25011/FCSTD/UIavVt2t9BS7dO0eq6Tm/SuP8Ovg5SmPxPXSvdy6wdfh8NRfESm2c0tq8eOie6dst928vmwzdomz0O1vp62bblIu/aTwnZU9ClfAigDk0KHPh8N0JJ3Xuc47ImGGQg4aUPwdqwvub6iTZ19/7Ki01jy0TJKzvsDhkRd7EefD208yR+x+DOjOMmeuC3bhjdTDgGEYGuKpj0Ll9534exLw4p6Jf7S3rxl3ZArzY8ToQZnH3crxWq3n+d/N8L47JfCe+HVdUjuZ4Zt2/zWprI+1Zb1p96zYbOv81Mbj6JDLfEnTruCu8psu4davvHtD/9f3i+MmBrdK+yjYFi2en+f/mVGZbcVnnEMbq0WQAZbl6cgOuts9+CnHsjndNe9u58hAbAz6/9aMW1q/O3DPaS+tkyyxSNuy61h69IaZ9S2+YtF2wRLSpI/zl3TKH+yX+cyEE6/k1u96SYpfj/kxpJUIV9kBApvCeC5NObZ1mX/3p0aF6GoHaV6l3ebvw25Taodoj8surzoDRG0T8omyXFW8/IP0aEosi77/HfS2kYna/fep1FWyA8WdYbngLxWxAY7w+qf/tRfZx2h3Jr/s9dk7MXuvT2Qvk129ea3c1umluPe9625WFEk7+7ZYZ+22Rv3yxaVvTZp3kXZzAau+donv21Dk/bb/bTKkCuH/s9KXMnZmJ69cN4VwOjsO0WoCuwLbr6f5bgcT6tzzO+N7OV7NUH80mT7/XyDX1TLUXz2+/m265qifbRoyxqF/uvrxOY8zY1XsqbA4HcvPmpxfU0sy/cVqb3Ovn0t1IYY0v79dVIQB7et/3bfgL2tsW5J6AQ7LtP64//q/kk4MPownUDrZLU62f+d8OT187075zebP9hbna/O71YH2er17f7R9vjg8NH6ufPV8fJgPz08W50t754fpMnnqsWd7f7qxt1VdXv1Zndw/lx+tjhfXVsunvvBtXp5vDpYLU+S/eXq7fXB6iC0z6+vrZer4/T79T7vVm+n+8tr1fnB+dm6Xx+dn6xv3NretZasD5bLa8uvL9cny4PF6oV+ebzeP98/qU7O1zdXy9XZ68sby2ub1erWar8v7zw+P9+cbK4nyzeWi/Pbm/MibO6eHXxks7C+rA6Wq5PV+d811t5bZ0eb58pzK/tktbffnfDu7eXN6tZmuTlZnSxvfOzsxv7i9uZgufnI+c31k4Pt+e2vWQvs6+X27m/tn22V5eH53urJkSW3t7YHt5fX1md3bmysluXe+fovfnt7uLx9fvB2ffe82aw2q+Vxen29vL2+vtkmv33yZP/6+dHm2rfOr71tzbi2XuerzeespL3frlbHZ9f2b95d3qrObp1t7VHd+SaDfHZ+e33NRn69PDq/fufN5bXVcvlk3X33+O3lwfHBepker57bX9lofic5WT53slqvQrE+ODtYnLWbM+v+3vmt5e3ND2I3quqb54ub62Mb/+Tg5tn2zvm1/XdvLQ+sov1udf328vr5k9vn1/50+dz19fHBwfH63bO9O9u9VXV4crba3l3u7a/+WXfz8GBpI17d3SPf0kb11nZ7sji4c37nDwaR6f8H/6uV/9FiU7YAAAAASUVORK5CYII=> \ No newline at end of file diff --git a/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/data-centre-operations.md b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/data-centre-operations.md new file mode 100644 index 00000000000..5a0fec97203 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/data-centre-operations.md @@ -0,0 +1,130 @@ +# One-liner + +Data centre operators use this model to simulate the electricity and cooling flows of a facility, so they can keep servers within thermal limits and minimise energy cost under stochastic workload demand and equipment failures. + +# Problem & context + +- **What real-world system or process is being modelled?** + + A data centre's power and cooling infrastructure: power grid + batteries/uninterruptible power supply (UPS) deliver power to server racks, whose heat must be extracted by cooling units / chilled water loops. The workload placed on servers drives both power draw and heat generation. + +- **Who feels the pain today, and how do they currently handle it?** + + Data centre operations/facilities teams. Capacity and redundancy decisions are typically made with static spreadsheet heat/power budgets or expensive CFD studies; incident response is largely driven by step-by-step procedures. + +- **What decision or outcome would improve if this model existed?** + - Target temperature at which the cooling system to hold (low temperature will consume too much electricity; high temperature reduces safety margins) + - Redundancy sizing - how many space cooling/power units to install beyond what is needed for full load + - Safe maintenance windows for cooling/power units, ranked by risk + - Quantified thermal risk under peak load or equipment-failure scenarios or live estimates during incidents (e.g. how long before the servers overheat?) + +# System sketch + +**Physical side:** electricity grid → uninterruptible power supply (UPS)/battery → Power distribution units → racks (server cabinets); racks generate heat as a function of load; computer room air conditions / handler units and the chilled-water loop extract heat; diesel generators as backup if the grid fails. + +**Cyber side:** the workload scheduler decides where compute jobs land (and therefore where heat appears); Data centre infrastructure management monitors rack temperatures and power draw; control logic sets cooling target temperature and triggers failover. This is a digital-twin setup where live sensor data can drive transitions rather than assumed stochastic rates. + +# Why a Petri net? + +- Discrete stochastic events (equipment failure/repair, failover switching, workload arrival) combined with continuous quantities (rack temperature, power draw) is a good fit for SDCPN modelling. Specifically SDCPNs support the following: + - **stochastic timing** - equipment break at random times, or how long a repair will take + - **concurrency and resource contention** - the servers share finite cooling and power so if one unit fails, it propagates the load to the rest of the system. The model has to capture the ripple effect + - **deterministic guards** - there are some strict rules that data centre operations implements for failover switching e.g. if the power cuts out, start the backup generator + - **cycles** - to model equipments lifecycle / loops e.g. working → failed → under-repair → working +- There is established formalism in literature for this domain (see References) +- There are 2 main branches of research in the existing literature, each solving one half of the problem: the Petri net papers (SPN) model *what breaks and what takes over* (failures, backups, repairs) but treat it all as on/off events with no temperatures anywhere in the model, while the Google/Meta-style machine learning work models *how heat and temperatures evolve* but has no concept of equipment failing or spares kicking in. SDCPNs can model both because tokens carry data that changes continuously over time: a token representing a server rack can have a temperature that physically rises and falls (following an equation) while the discrete machinery of failures and failover switches goes on around it. +- A simpler formalism or model would miss aspects of the system: a queueing model handles jobs lining up for servers but has no concept of equipment breaking and backups taking over; system dynamics handles smooth trends like heat building up but can't do abrupt on/off events like a generator kicking in; a reliability block diagram (the traditional uptime-calculation tool) assumes components fail independently, so it can’t capture one unit's failure overloading the survivors, or two broken units waiting on the same repair crew; and CFD simulates airflow very accurately but takes so long to run that it's a one-time design study, not a live model you can re-run thousands of times or optimise against. + +# Model outline + +- **Places:** power grid supply, uninterruptible power supply/battery charge, generator (off/starting/on), power distribution unit capacity, racks, cooling units (working/failed/in-maintenance), chilled-water loop, alert queue +- **Transitions:** workload arrival (stochastic); power draw / heat generation (deterministic, proportional to load); heat extraction (rate depends on target temperature and units available); equipment failure & repair (stochastic); failover switch (deterministic guard with delay); maintenance start/finish +- **Tokens / colours:** compute jobs (load in kW); server rack state tokens carrying temperature (ODE dynamics: heat in from load, out from cooling); cooling-unit tokens (capacity, health) +- **Key parameters:** workload arrival distribution; cooling capacity & target temperatures; failure/repair rates; thermal coefficients; redundancy level (N+x); failover delays + +# Questions the model answers + +| Question | Task type | Output | +| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------- | +| If a cooling unit fails at peak load, what is the probability any rack exceeds its thermal limit before repair completes? | Model verification (probabilistic model checking) | Probability of overheating + time-to-excursion distribution + location (which zone) | +| What cooling setpoints minimise energy cost (PUE) without thermal excursions? | Constrained parameter optimization | Optimal temperatures + energy saving vs risk curve | +| Is N+1 cooling redundancy enough for this load profile, or do we need N+2? | Parameter sensitivity analysis | Risk comparison across redundancy levels | +| When is the safest window to take a cooling unit out for maintenance? | Simulation / what-if scenarios | Ranked windows with breach risk | +| Which component most drives thermal/availability risk? | Parameter sensitivity analysis | Criticality ranking | +| What are the true thermal dynamics of this room, given sensor logs? | Learning place dynamics | Learned ODE for token temperature evolution | +| With 30 more GPU racks next quarter, can cooling hold at peak with all units running? | Simulation / what-if scenarios | Monthly excursion probability + which local constraint binds | + +# Data requirements for real-world application + +- **Rack-level power draw and temperature telemetry:** standard data exports from data centre infrastructure management at minute resolution. +- **Equipment failure/repair history** for cooling and power units. Where the data is sparse, the SPN literature provides published rates by architecture tier (TIA-942, Table 3), so the initial model doesn't require client data. +- **Facility topology:** which racks are served by which power distribution units and cooling units. +- **Workload traces** (optional, for the scheduler coupling): job arrival and placement logs. + +# Commercial angle + +- **Buyer**: data centre operator; **user**: facilities & capacity planning engineers. +- **Extremely topical:** operators cannot easily build more data centre during this AI-era, so the biggest lever they still control is getting more out of the buildings they already have because of the following: + - **Getting new capacity connected takes years.** A new facility has to join the utility's interconnection queue. In the UK, contracted demand connection applications jumped from 41 GW to 125 GW between November 2024 and June 2025, with at least 80 GW of it data centres. DESNZ reports this has contributed to waits of up to 15 years, and in the FLAP-D hubs (Frankfurt, London, Amsterdam, Paris, Dublin) new facilities wait 7–10 years on average for a grid connection, up to 13 in the most congested markets. + - **Existing facilities’ power needs are increasing.** A site is connected at a fixed contracted capacity sized for 5–10 kW racks, but AI racks draw 50–100+ kW, so operators hit their power ceiling quickly and expanding the connection means rejoining the grid connection queue. Gartner (Nov 2024) predicts 40% of existing AI data centres will be operationally constrained by power availability by 2027, with AI-optimised servers drawing 500 TWh a year (2.6× their 2023 level). + - **There is proven headroom in cooling.** Cooling is commonly put at roughly 30–40% of a facility's energy use, and DeepMind's ML control cut Google's cooling energy by ~40% at sites already among the most efficient in the world. If that much was available for optimisation, an ordinary facility has more. +- **Value of a better decision:** avoided thermal incidents (downtime costs), deferred capital (proving N+1 suffices avoids buying redundant chillers), energy savings from less conservative setpoints. +- This is a much **narrower scope than a “smart power grid”**, involving only 1 facility and own who can sign without a regulator or multiple parties. In the future this can be used as a proof of concept and scale up to a national power grid, which tends to be more risk-averse of a sector so proving the dynamic power management work on a smaller scale will be assuring. + +# Model limitations + +- Realism depends on calibrating thermal coefficients; without client telemetry / sensors the first version would be based on synthetic data / estimates. +- Using the Petri net as a digital-twin by feeding in sensor data to fire transitions in actual mode is not yet a Petrinaut capability. +- State-space growth: data centres likely to have 150+ racks which explodes the marking space if each is modelled individually. +- Thermal behaviour is continuous and spatial, abstractions applied by the SDCPN is justifiable for planning questions, but not as a CFD (computational fluid dynamics) study replacement. + +# **Petrinaut feature requests** + +- [in development] **Constrained optimisation:** the objective is a single metric, a constraint like "no thermal excursions" must be fed into the optimiser as a penalty term. +- [on roadmap] **Actual mode (live execution view):** petri net transitions fired according to sensor data as a digital twin of the data centre system +- **Built-in time-to-event outputs in Experiments:** "when did zone B first exceed its limit" is derivable via UUID tokens + metric engineering, but there is no built-in time-to-event experiment output for convenience and proper aggregation across runs. +- **Cross-place references in place dynamics:** a place's ODE sees only the tokens in that place, but thermal zones are physically coupled: hot exhaust from one zone reaches its neighbour's intake, so zone *z*'s equation needs zone *z*′'s current temperature. Currently that must be routed through parameters, read arcs, or by collapsing every zone into one shared place as a workaround. The feature request is a way for a place's dynamics to reference another place's token values, or an aggregate over them. + +# Pros / Cons rationale + +**Pros** + +- **Thermal risk can be simulated:** existing approach consists of static heat/power budget spreadsheets where a safety margin is calculated (e.g. add up the heat from racks, with sum of cooling to check headroom). With SDCPNs the probability of thermal risks, such as how often units fail, how long repairs take, how fast a zone heats without cooling can be simulated to derive excursion probabilities. +- **The marking maps directly to the state of data centre:** infrastructure management sensors data map directly onto a marking as a digital twin: rack temperatures are token colours, equipment status is token position. Incident-time "time-to-excursion" is therefore a re-run from the current state without the need to build/maintain a separate model. +- **Small, physically-bounded decision vectors**: cooling target temperatures (a small range), redundancy level N+x (small integer), maintenance window (bounded time) are a handful of box-constrained variables. Unlike the scheduling case's 3|D| growth, this sits comfortably inside Optuna's range which makes optimisation straight forward with the challenge lies in building a high fidelity model. +- **The data to calibrate and validate already exists.** **Every serious data centre runs an infrastructure management with centralised sensor data of servers power draw and temperatures at minute resolution. Where access to data is not possible, we can draw on the literature and established synthetic data (e.g. failure/repair rates using TIA-942 tier). + +**Cons** + +- **We have to prove the temperatures are right, and CFD(computational fluid dynamics) is the benchmark we're measured against.** One averaged temperature per zone is a reasonable simplification for planning questions, but the buyer's reference point is typically a Cadence CFD twin that simulates airflow in detail. +- **The failures that matter most are the ones with the least data.** A chiller dying at peak load is what we want to model, and exactly what a client has rarely or possibly never recorded. +- **Coupled zones are unproven Petrinaut.** Place dynamics see only their own place's tokens, so inter-zone heat transfer must be routed through parameters, read arcs, or shared places. No documented precedent for tens of coupled thermal zones; scale limits are an empirical unknown. +- **The buyer may not value what makes us different.** Cadence CFD already answers "what happens if chiller 2 fails" and data centre infrastructure management already has alarms on temperature, so an operator can reasonably ask what a probability adds. + +# Open questions + +- [ ] Instead of modelling individual server racks, can we group them as zones? +- [ ] Should the SDCPN model decide where compute jobs run, or just take the load as given? +- [ ] Can Petrinaut express coupled thermal equations? At how many coupled thermal zones does simulation or optimisation become too challenging to run with respect to time and in memory? + +# References + +### Petri nets (SPN) as formalism for data centre operations + +1. Gomes et al., *Temperature variation impact on estimating costs and most critical components in a cloud data centre*, IJCAT (2020) + - SPN models of the cooling subsystem coupled to IT availability, failure scenarios, downtime cost, and sensitivity analysis +2. Callou et al., *An Integrated Modeling Approach to Evaluate and Optimize Data Center Sustainability, Dependability and Cost*, Energies (2014)  + - SPN + reliability block diagrams + energy-flow models in one tool (Mercury), evaluating availability, cost, and sustainability. (Essentially what SDCPN unifies). +3. Callou & Maciel et al., *A Petri Net-Based Approach to the Quantification of Data Center Dependability* (2012) + +### Simulation-driven cooling optimisation + +1. Meta Engineering, *Simulator-Based Reinforcement Learning for Data Center Cooling Optimization* (2024)  + - industry use case: a hyperscaler running a simulate-then-optimise loop in production since 2021 with measured results. +2. Zhang et al., *Deep reinforcement learning towards real-world dynamic thermal management of data centers*, Applied Energy (2023)  + - a recent academic survey of the field + +### Digital-twin + +1. Milojicic et al., *Digital Twins for Data Centers*, IEEE Computer (2024) + - digital twins as the management layer for AI-era data centre efficiency diff --git a/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/other-use-cases.md b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/other-use-cases.md new file mode 100644 index 00000000000..57e442eae75 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/other-use-cases.md @@ -0,0 +1,91 @@ +### Chemicals batch production optimisation + +- **Scenario:** batch production plant + - Tokens = raw material, workers, production equipment, batches + - Transitions = process steps with stochastic durations, changeovers (switching production from one to another), breakdown/repairs. +- **Objective**: maximise profit (output revenue − notional cost of provisioned tokens); optimiser finds the allocation that maximises output without over-provisioning idle resources. +- **Experiment questions:** + - What's the best setup? Find me the number of workers, machines, and materials that makes the most profit + - If I run with this setup, what does my output look like hour by hour, and how much half-finished work piles up along the way? + - What's the chance I get all my orders out the door by Friday? + - How bad does the backlog get at its worst, and what time of day does that happen? + - What changes if I have 3 machines vs. 4 vs. 5? And does machine count actually matter more than changeover time? + - Scenario A or B, which one is genuinely better, given the same seed / randomness? + +### Logistics shipment orchestration / Pharma cold chain + +- **Scenario:** multi-party shipment flow with customs clearance, carrier handoffs, transport legs, proof of delivery. + - Tokens = shipments (colour: SLA clock, temperature history for the cold-chain variant), vehicles + - Transitions = handoffs, legs, clearance + - Parameters = lane lead-time distributions, SLA hours. +- **Objective:** maximise on-time delivery while minimising spoilage/write-offs and expediting cost +- **Experiment questions:** + - What's the chance a shipment on this lane misses its SLA? + - How long does delivery take on each route, typically and on a bad day? + - When one leg runs late, how far does the delay ripple down the chain? + - A shipment is trending towards spoilage (e.g. temperature increasing, stuck at customs), is it better to reroute or hold it? + - Which matters more: customs clearance delay variability or transit time? + - (Cold-chain) What's the chance this batch has a temperature excursion before delivery, and how much potency does it lose on the way? + - Show me the worst delivery in the whole experiment,wh what actually went wrong on that run? + +### **Supply chain disruption response (reactive / acute)** + +- **Scenario:** supply network under disruption + - Tokens = stock/batches, outstanding orders, transport capacity + - Transitions = production, transport legs, allocation to markets; a disruption = a lane or supplier's rate dropping to zero; each response (expedite, reallocate, switch source, do nothing) = a scenario variant of the same net +- **Objective:** pick the response action that minimises stock-outs and total cost, without shifting the problem onto other products sharing the same resources. +- **Experiment questions:** + - If I do nothing, what's the chance each market runs out of stock and when? + - How long until the network recovers under each response option? + - Expedite vs. reallocate vs. switch supplier, which action gives best outcome? + - Does fixing this product's supply make things worse for the other products that share the same lines and lanes? + - Show me the run behind that recommendation so I can sanity-check it. + +### **Proactive supplier management** + +- **Scenario:** supplier network with worsening/improving performance + - Tokens = suppliers (health as colour), purchase orders, material batches, demand + - Transitions = place order, ship (speed depends on supplier health), inspect, degrade/improve, remediate, qualify backup supplier +- **Objective:** keep supply stable and quality acceptable at the lowest cost, acting before a degrading supplier becomes a crisis. +- **Experiment questions:** + - If this supplier keeps deteriorating at the current rate, when does it start hurting my production? + - If I switch volume to the backup supplier, what's the chance the switch itself interrupts critical supply? + - How much does switching cost, and how long does it take to complete? + - Stick and remediate vs. switch, which is better? + - [Live mode] Watch the supplier's delivery and quality data and flag them before they become a problem, then feed the learned rates into the net. + +### **Airport ground operations** + +- **Scenario:** aircraft turnaround + - Tokens flights, stands, gates, fuel trucks, baggage crews + - Transitions = turnaround steps, stochastic arrival delays propagate through shared resources (e.g. stands, gates etc) +- **Objective:** minimise knock-on delays with the fewest crews and stands. +- **Experiment questions:** + - How do delays build up over the day, and what's the range between a good day and a bad one? + - If a flight lands 30 minutes late, what's the chance it causes a knock-on delay of more than 15 minutes? + - How full do the stands get at peak, and when does this happen? + - What changes with 5 baggage crews vs. 6 vs. 7? + - What's the best crew allocation across the day's schedule? + - The flight is half way through turnaround, predict when it will leave the stand / depart + +### **Energy & utilities maintenance scheduling** + +- **Scenario:** maintenance scheduling on a shared crew pool + - Tokens = maintenance jobs, crews, assets + - Transitions = dispatch, travel, repair (stochastic durations); outage windows gate when work can run; backlog queues when crews are busy +- **Objective:** clear the maintenance backlog without overrunning into peak-demand windows. +- **Experiment questions:** + - How does the maintenance backlog evolve over the season with this crew count? + - What's the chance a job overruns into a peak-demand window? + - What changes with 4 crews vs. 5 vs. 6 etc? + - What's the best allocation of crews to regions for this quarter's plan? + +### **Process mining + conformance checking** + +- **Scenario:** the customer has event logs but no model. Learn the net structure from the logs, then learn the rates, then check live traces against the learned net on an ongoing basis. +- **Objective:** model an accurate and simulatable net from observed data +- **Live mode / observed data questions:** + - Here are six months of event logs, what does our process look like? + - How fast does each step really run (vs. what the planning system assumes)? + - Is the live process still behaving like the model, or has it drifted? + - Which real traces deviate from the discovered process, and where? diff --git a/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/production-scheduling.md b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/production-scheduling.md new file mode 100644 index 00000000000..a53f006fb1a --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/production-scheduling.md @@ -0,0 +1,256 @@ +# One-liner + +A supply chain manager at a multi-line plant uses this model to assign a fixed book of product demand across three production lines and rank it within each line, so as to maximise contribution margin net of changeover, scrap and backorder under deterministic or stochastic step durations and equipment failure. + +# Problem & context + +**What is being modelled.** A fixed demand book of product types, produced on parallel non-identical production lines. Four structural features define the class: + +1. **Production lines.** Each line is an ordered sequence of steps with finite inter-step buffers. Several units are in flight simultaneously, so line throughput is set by the slowest step *for the product currently loaded* — and different products can bottleneck on different steps of the same line. This is invisible in the capacity spreadsheets managers work from. +2. **Sequence-dependent changeovers.** Switching a production line between product families costs time, the matrix is asymmetric, and the changeover consumes a shared crew — so changeovers on different lines serialise against each other. +3. **Batching by production run.** Demand for a product type is produced in runs. Run length trades changeover amortisation against due-date responsiveness, and interacts with ramp-up scrap, which is incurred per run rather than per unit. +4. **Resource contention.** Lines, the changeover crew, buffers, materials and shift labour are all finite and interact. + +**Assumptions:** + +- Demand is fixed and known at t = 0. Each demand token is *hard-assigned* to exactly one production line. +- A line that becomes idle must immediately start the highest-priority **eligible** token assigned to it, where eligibility is controlled by a per-token release time: an order becomes eligible only after its `wait` has elapsed. Firing therefore stays greedy, while deliberate idling — holding a line for an incoming same-family order rather than paying an expensive changeover — is expressed by the release times the optimiser chooses. + +**Who feels the pain:** + +The supply chain manager or master scheduler, on a horizon of days to weeks. The incumbent is a spreadsheet allocation maintained by one experienced person, plus a daily verbal reallocation on the floor. + +**What improves:** + +Line assignment and within-line ordering are currently set by habit and by which line "usually" runs which product. Both are directly optimisable, both have large leverage on changeover hours and on whether the demand book completes on time, and neither is currently measured. + +# System sketch + +!image.png + +**Physical side.** Three lines, each with an ordered step sequence and capacity-bounded buffers between steps. Step durations vary by product type, so both line rate and bottleneck location are product-dependent. Each line carries a changeover state (last family produced) and a maintenance counter. One changeover crew serves all lines. Finished units pass to QA before counting as delivered. + +**Cyber side.** ERP supplies the demand book, due dates, margins and product master data. MES supplies run start/stop, actual quantities and downtime reason codes — the calibration source, and reliably complete wherever line-level scanning exists. The historian supplies step-level cycle times, which are what the model actually needs and what nobody currently analyses per product. For reactive use, the MES also supplies the current plant state, consumed directly as an initial marking. + +# Why a Petri net? + +**What maps natively.** The production line subnet is drawn directly from the step sequence. Buffers are capacity-bounded places, which is what produces blocking (a step finishes but cannot hand off) and starvation (a downstream step idles behind an upstream stoppage). Neither appears in any closed-form line-rate calculation, and both are why real lines miss nameplate. Step durations read off the *product token's own colour*, so one net structure serves every product type and the bottleneck migrates between steps with no structural change. Changeover state lives in the line token; the changeover transition also consumes the shared crew token, so cross-line serialisation of setups falls out for free. + +**The framing that matters.** A schedule is a timed firing sequence σ = ⟨(τᵢ, tᵢ, βᵢ)⟩; the start times τᵢ are the firing instants of the `StartRun` transitions and the bindings βᵢ are demand token to production line assignments. Precedence, capacity, changeover and resource constraints are the conditions under which σ is admissible, not side constraints checked afterwards. + +**The marking is the plant state.** Reactive rescheduling means loading the current state as a marking and re-running. No reformulation, no separate rescheduling model. Since real plants reschedule constantly, this is worth more than a marginal improvement in solution quality. + +**Where the PN does not help.** The net evaluates; it does not generate an optimal plan. An optimisation layer is required, and its design is the engineering content. Constraint programming Satisfiability (CP-SAT) with interval variables handles the deterministic core of assignment plus sequencing plus setup matrices very well, and remains the honest state of the art there. The PN's complementary advantages are stochastic evaluation, emergent line throughput, state-as-marking rescheduling, and one artefact where CP requires a separate simulation model kept manually in sync. + +# Model outline + +## Formal problem statement + +**Given** + +- Product types P. Type p has family f(p) ∈ F, demand Dₚ, due date dₚ, margin mₚ, minimum run qₚᵐⁱⁿ, ramp scrap ρₚ +- Lines L = {1, 2, 3}. Line ℓ has ordered steps Sℓ, buffer capacities κ(ℓ, s), changeover matrix σℓ : F × F → ℝ⁺ (asymmetric), maintenance threshold Θℓ. +- Step duration δ(ℓ, s, p) — the key coupling. Both line rate and bottleneck location follow from it. +- Shared changeover crew, capacity 1. + +**Derived line performance** for a run of q units of p on ℓ, with cycle time c = maxₛ δ(ℓ, s, p) and fill time F = Σₛ δ(ℓ, s, p): + +``` +T(ℓ, p, q) = F(ℓ, p) + (q − 1)·c(ℓ, p) +``` + +This is the formula in the manager's spreadsheet. It holds only with infinite buffers and no failures, so **the gap between it and the simulated result is the value the model adds**. It also doubles as a validation check: run the net with unbounded buffers and failures disabled, and it must reproduce this exactly. + +**Decide** — for each demand token d: a line assignment `line_id(d)` ∈ L, a priority `priority(d)` ∈ [0,1], and a release time `wait(d)` ∈ ℝ⁺; plus run decomposition (how Dₚ splits into runs). + +**Selection rule.** When line ℓ becomes idle, it starts the pending token of maximal priority among the *eligible* set `{d : line_id(d) = ℓ, t ≥ wait(d)}`, subject to material availability. Given the wait vector, this fully determines every start time — no separate timing decision exists. + +**Maximise** Π, where + +``` +Π = Σₚ mₚ·delivered(p) − backorder penalty − changeover cost − ramp scrap − overtime +``` + +with makespan and total changeover minutes as natural secondary axes for a Pareto front. + +## Structure of the search space + +Because assignment is hard, an idle line cannot pick up work assigned to another line regardless of how starved it is. The outer problem is therefore a **partition of the demand set into three bins with a sequence-dependent setup cost inside each bin** — bin packing wrapped around three sequencing problems. Release times weaken this characterisation somewhat, since deliberate idle time now enters makespan alongside run time and changeovers, but the packing structure still dominates. + +Three consequences: + +- **Assignment dominates.** The packing decision has far more leverage on the objective than within-line ordering or release timing. Expect most of the optimiser's improvement to come from `line_id`, and consider a load-balancing warm start. +- **Priorities are identified only within a line.** Any monotone transformation preserving within-line order gives an identical schedule, so the space carries |D|-dimensional continuous plateaus. Rank-normalising priorities within each `line_id` group removes the symmetry cheaply and materially helps TPE. +- **Release time and priority are overcomplete.** With unbounded `wait` you can encode any schedule directly by setting each token's release to its intended start, at which point priority only breaks exact ties. That is a second, larger plateau structure layered on the first. Bounding `wait` to the maximum changeover time is the principled fix — waiting longer than the changeover being avoided is dominated on that line — and it keeps `wait` doing the one job it is genuinely needed for. + +The one place cross-line priority comparison has meaning is the shared changeover crew, where a global scale is needed to break ties between simultaneous changeover requests. + +## Deliberate idling via release times + +Greedy firing is retained: a line must start whenever an eligible token is assigned to it. What the optimiser controls is the *eligible set*, through a deterministic release time on each demand token. A `ReleaseDemand` transition with delay `wait(d)` moves the token from `Pending` into `Demand`; until it fires, the line simply has nothing to start. + +This buys back the full space of active schedules without complicating the firing semantics, and without sacrificing feasibility-by-construction — every (line_id, priority, wait) vector still yields a valid schedule. + +It matters because of asymmetric changeovers. If a line finishes a base-family run and only a tinted-family token is currently eligible, pure greedy firing forces the tinted start and the expensive tinted→base wash is paid later. Holding the line for the base-family order can dominate by a wide margin. Under the earlier non-idling formulation this was a known limitation; here it is exactly what the release times are for. + +Keeping release as a *transition* rather than a clock guard on `StartRun` is deliberate: it preserves guard locality, which structural analysis depends on. + +## Two Petri net versions + +- Deterministic Statically Coloured Timed PN: deterministic step durations at nominal or quantile values. Output: an executable plan with start times. +- Stochastic Statically Coloured Timed PN: stochastic durations, breakdowns, probabilistic QA outcomes. Output: distributions over completion, margin and backorder. + +A Petri Net with an optimisation layer combines the strengths of two approaches: Constraint Programming (CP) and Discrete Event Simulation (DES): + +- A pure CP tool can optimise but does not naturally support dynamic simulation. +- A pure DES tool can simulate system behavior but cannot optimise decisions. + +This approach enables both **simulation** and **optimisation** within a single framework. + +## Places + +**Shared:** `Pending` (all orders at t = 0), `Demand` (eligible orders only, initially empty), `Materials`, `Crew` (capacity 1), `QAHold`, `Delivered`, `RampScrap`, `Backorder`. + +**Per line ℓ:** `Idle_ℓ` (line token), `In_ℓ`, `Step1_ℓ`, `Buffer_ℓ` (capacity κ), `Step2_ℓ`, `Step3_ℓ`, `Out_ℓ`, `Down_ℓ`. + +## Transitions + +| Transition | Kind | Notes | +| ------------------------ | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ReleaseDemand` | Timed, deterministic | `Pending → Demand`, delay `d.wait`. The mechanism for deliberate idling | +| `StartRun_ℓ` | Immediate, **conflict-resolved by priority** | The decision point. Guard is local: `line_id = ℓ` ∧ line idle ∧ material available ∧ family matches line state. Consumes the demand and line tokens; emits q unit tokens into `In_ℓ`; sets `remaining := q` | +| `Changeover_ℓ` | Timed, guarded on family mismatch | Delay σℓ(last, new); seizes `Crew`, which serialises setups across lines; updates line-token family; emits ramp scrap | +| `Enter_s,ℓ` / `Exit_s,ℓ` | Timed | Delay δ(ℓ, s, p) read from the unit token's colour. Buffer capacity guards produce blocking and starvation | +| `Fail_ℓ` / `Repair_ℓ` | Stochastic (evaluation mode) | On the bottleneck step; preemption semantics required | +| `FinishRun_ℓ` | Immediate | Fires when `remaining = 0`; returns the line token to `Idle_ℓ`; releases product to `QAHold` | +| `Maintenance_ℓ` | Timed, guarded on `units_since_maint ≥ Θℓ` | The manager's lever is whether to co-locate it with a changeover already being paid for | +| `Release` | Timed | Finite QA capacity | + +## Colours + +- **Demand token:** ⟨`type`, `family`, `qty`, `due`, `margin`, **`line_id`**, **`priority`**, **`wait`**⟩ +- **Line token:** ⟨`line_id`, `last_family`, `units_since_maint`, `remaining`⟩ +- **Unit token:** ⟨`type`, `run_id`, `entered_step_at`⟩ + +# Questions the model answers + +| Question | Task type | Output | +| ------------------------------------------------------------------------------------------ | ------------------------------------------ | ----------------------------------------------------------------------------------- | +| How should we split the demand book across the three lines, and in what order within each? | Combinatorial assignment + sequencing | `line_id`, `priority` and `wait` per demand token; executable plan with start times | +| Is it worth holding a line idle to avoid an expensive changeover? | Release-time optimisation | Idle minutes deliberately taken vs. changeover hours avoided; net margin effect | +| What throughput will each line actually achieve for this product mix? | Simulation vs. closed form | Simulated rate vs. F + (q−1)c; the gap attributed to blocking, starvation, downtime | +| Which step is the bottleneck — and does it move by product? | Bottleneck identification | Per-step utilisation and blocking/starvation split, by product type | +| How much capacity are we losing to changeovers, and how much does reordering recover? | Sequencing with asymmetric setups | Changeover hours by family pair; hours recovered under optimised ordering | +| Where should we add buffer, and how much? | Constraint sensitivity | Throughput vs. buffer capacity curve per position | +| How robust is this plan to breakdowns? | Stochastic evaluation of a fixed plan | Distribution of completion; P(backorder) per product | +| A line goes down at 06:00 — what now? | Reactive rescheduling from current marking | Repaired assignment; products newly at risk | +| Is the demand book feasible at all this period? | Feasibility / capacity check | Feasible plan, or the binding constraint identified | +| Which fitted parameters most affect the recommendation? | Global sensitivity analysis | Sobol indices; guides measurement priorities | + +# Data requirements for real-world application + +1. **Step durations δ(ℓ, s, p)** — historian, at step level, by product. This is the input that makes the model different from a spreadsheet, and it is the one most often *available but never analysed per product*. Check early whether bottlenecks genuinely move between steps by type; if they don't, the intra-line pipeline collapses to one rate per product and the second modelling level stops earning its keep. +2. **Changeover matrix σℓ(f, f′)** — reconstructable from timestamp gaps between consecutive runs, but the family taxonomy has to be built with process engineers. Expect asymmetry to be real and undocumented. +3. **Buffer capacities and line topology** — master data or a walk down the line. +4. **Breakdown and repair distributions** — CMMS or historian downtime records; quality varies. +5. **Ramp scrap by changeover severity** — quality records; often aggregated in a way that loses the per-run structure. +6. **Material lead times** — needed to keep supply-driven delay out of the `wait` field. See Model limitations. +7. **Demand book, due dates, margins, backorder penalties** — ERP for the first three. **Penalty weights are almost never written down** and must be elicited from commercial; the entire objective pivots on them. + +**Plausibility.** Items 1–4 are available wherever line-level MES and a historian exist. The gaps requiring elicitation are the family taxonomy, ramp scrap structure and penalty weights. Scope pilots so that elicitation happens in week one. + +# Commercial angle + +**Buyer vs. user.** The **user** is the supply chain manager or master scheduler. The **buyer** is the operations or plant director, measured on service level, line utilisation and conversion cost. Quality holds a veto wherever changeover protocols are contamination- or allergen-driven, so lead with better *ordering* and fewer full changeovers, never with running closer to the limits. + +**Value.** In descending reliability: recovered capacity on the constraining line from changeover sequencing, which is capex-free and verifiable from historical data *before the model is built* — an excellent pre-sales artefact. Then better load balancing across lines, which is pure allocation and costs nothing to change. Then reduced ramp scrap through longer, better-placed runs. Then reduced dependence on the one person who knows how to build the allocation. + +**Alternatives.** Spreadsheet plus an experienced scheduler is the real incumbent — free, trusted, and unable to see that a line's bottleneck step moves with the product. APS modules assume product-independent line rates and symmetric setups. In-house CP-SAT is the most credible competitor and should be co-opted architecturally rather than opposed: CP proposes assignments on a deterministic abstraction, the SDCPN evaluates them stochastically, the optimiser iterates. + +**Commercial value 4/5** — quantifiable capex-free benefit across a broad base, discounted for a strong free incumbent in CP and conservative buyers. + +# Model limitations + +**Scale.** Exhaustive reachability is out — the marking includes real-valued timestamps. The decision vector is 3|D| (|D| is number of demand tokens), which is still modest, but a 200-order book (|D|=200) puts the optimiser past its comfortable range. A RL approach such as the one described here (code repo) might scale much better. + +**Do not let `wait` absorb material lead time.** If a token's start is delayed because the supplier has not delivered, that is *data* and belongs in the `Materials` guard. If it is delayed because holding the line is worth it, that is a *decision* and belongs in `wait`. Both fit the same field and the mechanism is identical, so conflating them is easy — and it would make the optimiser appear to be choosing something it is not. Keep them separate in the model and in the reporting. + +**Unbounded release times make the encoding overcomplete.** See *Structure of the search space*. Bound `wait` to the maximum changeover time; the bound is principled rather than arbitrary. Consider also encoding `wait` as a boolean gate plus a magnitude so that most tokens sit at zero, which keeps the search focused on the assignment decision where the leverage actually is. + +**Release times are open-loop.** A `wait` vector fixed at t = 0 cannot react to a breakdown at t = 6h. For a generated plan that is fine. For reactive use, the closed-loop equivalent is a null "wait" choice offered at each idle event, decided on current state — a different and larger design. + +**Validation.** The bar is reproducing a historical period's actual line rates, changeover hours and downtime from the recorded allocation. Only then do counterfactual allocations mean anything. Expect unwritten constraints to surface — products that "always" run on line 2, customer-specific line qualifications. + +**Realism 4/5.** The structure is well-attested and the data mostly exists. Held below 5 by unproven validation and by the fact that hard line assignment is a simplification the target plant may not actually obey. + +# **Petrinaut feature requests** + +- **[partial] Subnet templating / replication**. Three structurally identical line subnets should be one definition instantiated three times, not three hand-built copies that drift apart. +- **Priority-based conflict resolution.** The selection rule is the entire decision layer here and currently has to be encoded ad hoc. Highest-value gap by a distance. +- **Firing-instant extraction.** The plan is the timestamped firing sequence; the runtime should emit it as a Gantt artefact, not just a final marking. +- **Mixed-type decision variables in `OptimisationSpec`** . Three variable types per demand token (categorical `line_id`, continuous `priority`, bounded continuous `wait`), plus Pareto results in the streaming API. +- **Common random numbers across trials,** so candidate allocations are compared on the same realisations rather than seed noise. +- **Learning production line allocation policy.** We need to learn either a general transition kernel that allocates demand/order tokens to production lines based on some heuristic or learn the transition firing sequence of transitions that allocate demand tokens to production lines. In both cases, petrinaut should be able to support “controllable” transitions that fire when told. Some Claude-generated reqs for support such transitions are found below + +Requirements for controllable transitions in Petrinaut + +- **Marking injection** from external state (data feed), for reactive rescheduling. +- **Structural analysis surfaced** — P-invariants confirming unit conservation through the line subnets catch modelling errors automatically; siphon analysis on the resource subnet establishes deadlock-freedom. No DES competitor can do either. + +# Pros / Cons rationale + +**Pros.** + +- *Line throughput emerges rather than being assumed.* The closed form F + (q−1)c is what the manager has today; the simulated gap against it is the product. +- *Release times recover the full active-schedule space* without complicating firing semantics or sacrificing feasibility-by-construction. Every decision vector still yields a valid schedule. +- *One artefact, both modes* — nominal generation and stochastic evaluation from the same structure. Neither CP tools nor DES tools offer the pairing. +- *The marking is the plant state*, so rescheduling is a re-run. Most scheduling tools die at rescheduling, not at first solve. +- *Small, box-constrained decision vector* — 3|D| mixed-type variables that Optuna handles directly. +- *Structural verification* — unit-conservation invariants and deadlock analysis are formal guarantees from the net structure alone. +- *Sector-transferable* — the subnet is a template; instantiation supplies product types, a changeover matrix and fitted durations. + +**Cons.** + +- *High-dimensional sparse decision space.* 3|D| decision vector scales with the number of demand tokens. This makes optimisation very challenging for any application with a semi-realistic scale |D|. We need to efficiently explore the structure of `line_id`,`priority`,`wait` in the optimisation as these are largely correlated. +- *Release time and priority overlap.* Unbounded, `wait` can express any schedule on its own, leaving priority to break ties and creating large plateaus. Requires bounding and preferably sparsity encouragement. +- *CP-SAT as an alternative is free, excellent and improving*, and might suffice for the purposes of a commercial application. +- *Elicitation-heavy inputs* — family taxonomy, ramp scrap structure and penalty weights are the three things nobody has written down, and all three are load-bearing. + +# Open questions + +- [ ] Should `wait` be sparse (boolean gate plus magnitude, most tokens at zero) or free within its bound? Sparse keeps search pressure on the assignment decision; free is simpler to implement. Measure the gap on one instance. +- [ ] Is the open-loop plan sufficient, or does reactive use require a closed-loop null choice evaluated at each idle event? These are materially different builds and the answer determines the product shape. +- [ ] Do bottlenecks genuinely migrate between steps by product type? Cheap to check from historian data, and it determines whether the intra-line pipeline earns its place. +- [ ] Priority-based conflict resolution: what is the right syntax, and does it belong in the net or in a separate policy object attached to it? Language-design question, worth getting right once. +- [ ] Common random numbers: how much does it reduce required replications? Quick experiment against the existing supply chain demo model before committing to the larger build. +- [ ] Preemption on breakdown — resumable, restartable or scrap? Probably a per-step attribute rather than a global choice. +- [ ] Can we build a pre-sales artefact that estimates recoverable changeover hours from a plant's historical run sequence alone, before any model is built? Looks like the cheapest way to open a conversation. + +# References + +**Scheduling** + +- Pinedo, M. *Scheduling: Theory, Algorithms, and Systems.* Springer. — Parallel machine scheduling, dispatch rules, weighted tardiness. +- Allahverdi, A. "The third comprehensive survey on scheduling problems with setup times/costs." *EJOR*, 2015. +- Kolisch, R. & Hartmann, S. "Experimental investigation of heuristic solution procedures for the RCPSP." — Priority-rule encodings and schedule generation schemes. +- Harjunkoski, I. et al. "Scope for industrial applications of production scheduling models and solution methods." *CACE*, 2014. — Why academic scheduling models rarely reach plants. +- Méndez, C. A. et al. "State-of-the-art review of optimization methods for short-term scheduling of batch processes." *CACE*, 2006. + +**Flow lines and buffers** + +- Hopp, W. & Spearman, M. *Factory Physics.* — Line rate, blocking, starvation, the WIP/throughput/cycle-time relationships the net must reproduce as a sanity check. +- Li, J. & Meerkov, S. M. *Production Systems Engineering.* Springer, 2009. — Buffer allocation and bottleneck identification in serial lines. + +**Petri nets** + +- Lee, D. Y. & DiCesare, F. "Scheduling flexible manufacturing systems using Petri nets and heuristic search." *IEEE T-RA*, 1994. +- Zhou, M. & Venkatesh, K. *Modeling, Simulation and Control of Flexible Manufacturing Systems: A Petri Net Approach.* World Scientific, 1999. +- van der Aalst, W. M. P. "Interval timed coloured Petri nets and their analysis." 1993. — Timed CPN semantics. +- Jensen, K. & Kristensen, L. M. *Coloured Petri Nets.* Springer, 2009. +- Everdij, M. H. C. & Blom, H. A. P. — DCPN/SDCPN and the PDMP correspondence; the foundation for evaluation mode. + +**Method** + +- Laborie, P. et al. "IBM ILOG CP Optimizer for scheduling." *Constraints*, 2018. — The incumbent to benchmark against. +- Akiba, T. et al. "Optuna: A Next-generation Hyperparameter Optimization Framework." KDD 2019. +- Deb, K. et al. "NSGA-II." *IEEE TEC*, 2002. diff --git a/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/truck-fleet-maintenance.md b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/truck-fleet-maintenance.md new file mode 100644 index 00000000000..c8b3e0fb70b --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/reference/hash-documents/use-cases/truck-fleet-maintenance.md @@ -0,0 +1,140 @@ +# One-liner + +Fleet operators use this model to decide when and where to service each truck. The service must occur early enough to prevent a breakdown on the road, and late enough not to waste maintenance capacity. The number of trucks in the depots must not cause a missed delivery. + +# Problem & context + +- **Which real-world system does the model show?** + + A truck fleet running delivery routes while its components wear out. Trucks stream telematics data as they drive; maintenance happens at depots with a limited number of bays, technicians and spare parts. The operator has to sequence servicing across the whole fleet, because you can never service every truck at once and pulling the wrong truck off the road at the wrong time costs deliveries. + +- **Who has this problem now, and what do they do?** + + Fleet operations planners. Servicing is mostly at fixed intervals (mileage or time) plus reactive repair when something breaks. Telematics products flag faults and increasingly predict failures. These products tell the planner which truck has a high risk but not the optimised scheduling e.g. which truck to service on Tuesday given there are 3 free bays and a delivery contract. + +- **Which decisions become better with this model?** + - The maintenance policy: what wear level should trigger a service + - The sequence of the maintenance tasks for each week. + - Depot capacity and placement: number of bays at a depot; the location of a new depot + - The model gives a breakdown risk and a delivery risk for each decision. + +# System sketch + +**Physical part.** The trucks move along the routes, with position of each truck changing continuously. The wear and tear increases with distance and load. The depots have fixed locations, each with bays, technicians and spare parts. A truck can break down on the road then a recovery vehicle must move the truck to a depot. + +**Cyber part.** The telematics system sends engine hours, brake wear, fault codes and GPS position, which feeds the predictive software / layer. The fleet management software dispatches the trucks and books the service slots. The data moves in two directions: sensor data updates the state of the model, the model sends a maintenance schedule to the dispatch software. + +# Why a Petri net? + +- Continuous degradation combined with discrete, resource-constrained maintenance is a good fit for SDCPNs. Specifically: + - **Dynamically coloured tokens.** Each truck token has a wear/health value. The value evolves continuously while the truck operates. + - **State-dependent stochastic rates.** The breakdown rate is a function of the wear value. So the rate increases as the truck degrades. + - **Resource contention.** Each depot has a limited number of bays, technicians and spare parts. This limit causes the scheduling problem. + - **Cycles.** A truck operates, deteriorates, gets maintenance, then operates again. This cycle continues for the life of the truck. + - **Spatial dynamics.** The tokens carry coordinates and the travel time is a result of the dynamics. The model does not use an assumed constant. +- There is established Petri net literature for this domain (see References below): coloured Petri nets for aircraft fleet maintenance with multi-level repair, limited spare parts and cannibalisation; hierarchical coloured Petri nets for land-vehicle fleets. The literature splits into 2 main approaches: + - one approach shows the repair system in detail, including queues, bays, crews and spare parts. But it shows the wear as a small number of discrete states with constant rates. + - second approach uses machine learning on real sensor data. It predicts the failure risk of one truck with good accuracy but stops at that risk value. If three trucks are flagged with high risks and only one bay is free, the second group doesn’t give any recommendation on scheduling. +- A simple model loses necessary information: + - A reliability model gives a failure probability for each truck but has no depot, no queue and no scheduling. + - A queueing model shows the depot but does not show the wear that sends the trucks to the depot. + - A scheduling model assigns the work to the bays but it must assume the time of each future service, which is uncertain. + +# Model outline + +- **Places:** in service, on the way to a depot, depot queue, in a bay, ready for dispatch, broken down on the road. Resource places: bays, technicians, spare parts. +- **Transitions:** + - Dispatch: sends a truck to a route. + - Drive: updates the coordinates of the token and accumulates the wear value. + - Telematics alert: live data fires this transition in twin mode. + - Schedule a service + - Travel to a depot + - Repair: duration is stochastic, the transition uses one bay, one technician and spare parts + - Return to service + - Breakdown: the rate increases with the wear value of the token. + - Roadside recovery +- **Tokens and colours:** trucks (coordinates, mileage, per-component wear/health with ODE dynamics), technicians (skill), spare parts, delivery jobs. +- **Parameters:** the wear rates; the breakdown rate curve; repair duration distributions; the number of bays and technicians; the wear value threshold that starts a service; depot locations + +# Questions the model answers + +| Question | Task type | Output | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- | --------------------------------------------------- | +| What is the probability of a missed delivery target in one month with this maintenance schedule? | Model verification (probabilistic model checking) | The probability of a missed delivery target | +| What wear threshold should trigger a service, to minimise total downtime + maintenance cost? | Parameter optimization | Optimal threshold policy + cost vs risk curve | +| Which trucks should be pulled for service this week, and in which order, given we can't service the whole fleet at once? | Simulation / schedule comparison | A list of maintenance schedules in order of quality | +| How does each component degrade, given telematics logs? | Learning place dynamics | A learned wear ODE equation for each component | +| What breakdown hazard rates best explain our historical failure data? | Parameter inverse problem | Calibrated breakdown rates | +| Would a new depot at location X reduce fleet downtime more than an extra bay at an existing depot? | Spatial what-if / sensitivity analysis | Downtime comparison across configurations | +| How many trucks will be off the road at once, at worst, over the next quarter? | Simulation / what-if scenarios | A distribution of the number of unavailable trucks | + +# Data requirements for real-world application + +- **Telematics data:** mileage, engine hours, fault codes and GPS position. Fleet operators have this data at high resolution and forms the basis of the products that they buy now. +- **Maintenance records and breakdown records** for each truck and each component. +- **Depot locations, the number of bays, the technician rosters, and the delivery schedule.** +- **Public data is available if the client data is not available.** SCANIA released the Component X data set in *Scientific Data* in 2025. The data set contains real measurements from a truck fleet. It also contains repair records and truck specifications. SCANIA released it as a public benchmark for predictive maintenance. Volvo released data from more than 10,000 heavy trucks for the ECML-PKDD 2024 challenge. We can calibrate the wear part of the model with this data with any real client telematic data first. + +# Commercial angle + +- **Buyer:** fleet operators; **User:** fleet operations planners. +- **Monetary value is easy to calculate.** A breakdown on the road causes recovery costs, a missed delivery and lost driver hours; a service done too early wastes a slot and a part with life left in it. Both scenarios can be quantified with money. +- **The competition.** Truck manufacturers and telematics companies sell failure prediction now. Volvo and SCANIA have large machine learning programs and they publish the results. We should not compete on failure prediction as they have more data and better access to trucks We will add value after the predictions, turning a set of risk values into an optimised maintenance schedule which conforms to the limits on bays, technicians, spare parts, travel time and delivery contracts. The IDA 2024 paper on SCANIA trucks writes about cost and context in the maintenance decision without mentions on how to derive the maintenance schedule. + +# Model limitations + +- Route optimisation is considered out of scope. Routes are taken as given and maintenance is optimised around them. +- The fleet size sets the model size (e.g. 300 trucks generate 300 tokens). Each token carries its own dynamics, then multiplied by Monte Carlo runs and optimisation trials. The limit at which Petrinaut can handle this is unknown. + +# **Petrinaut feature requests** + +- **Priority-based resource resolution.** More than 1 trucks can request the same free bay. Right now Petrinaut will just pick one arbitrarily, but we want to be able to define some kind of priority rules like "most worn first" or "nearest first”. +- **Controllable transitions.** Existing transitions fire by themselves based on defined rates / stochasticity. We need a way to define transition firing based on a decision e.g. when the scheduling chooses to pull a truck into maintenance. +- **Time-to-event results in Experiments.** Two example questions: when did this truck break down first, and for how long was the fleet below N available trucks. A user can calculate these values with UUID tokens and metric code. Petrinaut has no built-in result for them. The calculation across the runs must also count the runs with no event. The data centre use case requests the same feature. +- **Injection of a marking from live telematics.** The digital twin needs the current fleet state as an initial marking. The state includes the positions, the wear values and the depot occupancy. The data centre use case requests the same feature. + +# Pros / Cons rationale + +**Pros** + +- **Events are frequent.** A fleet has hundreds of similar trucks, and breakdowns occur every week. This is beneficial in 3 ways: Monte Carlo runs converge quickly; we can fit one wear model to hundreds of trucks ; the failure data is not rare unlike some other use cases (e.g. data centre operations) +- **Real public data is available.** The SCANIA data set and the Volvo data set contain real wear data from real fleets. +- **Can easily express the value of the model in monetary terms.** Prevented breakdowns and safe delays of a service convert to money directly. + +**Cons** + +- **Wear has more than one dimension.** The brakes, the engine, the transmission and the tyres wear at different rates and also interact with each other. One wear value for each truck is an over-simplification. +- **Many fleets already have a prediction product.** These fleets must add our tool to an existing system, which reduces urgency / need for purchase. +- **The competition controls the data.** The telematics companies hold the sensor data and the truck manufacturers build the same function internally. +- **The scale is not known.** Hundreds of tokens each have their own equation. Monte Carlo runs and optimisation trials multiply the cost. + +# Open questions + +- [ ] How best to model the truck’s degradation, per-component or per-truck? +- [ ] Should the model include supply of spare parts? +- [ ] How many trucks can the simulation run before it becomes too slow? + +# References + +### Petri nets for fleet maintenance + +1. *A coloured Petri net framework for modelling aircraft fleet maintenance*, Reliability Engineering & System Safety (2018) + - This is the nearest published example. It is a coloured Petri net model of fleet maintenance with more than one level of repair. The model includes limited spare parts, limited resources and cannibalisation. Cannibalisation is the removal of a good part from an unserviceable asset to repair a different asset. The reference list in this paper is also a good map of the earlier work. +2. *Intra-City Call-Taxi Fleet Sizing using Petri Net Embedded Simulation Optimization* (2022) + - A Petri net simulation with an optimisation layer above it. + +### Petri nets for condition-based maintenance + +1. *Modelling wind turbine degradation and maintenance*, University of Nottingham + - A Petri net for wear, inspection and condition monitoring. The model includes dependent wear between the subsystems. This is the nearest example to our model, but in a different industry. + +### Machine learning for truck predictive maintenance + +1. Kharazian et al., *SCANIA Component X dataset: a real-world multivariate time series dataset for predictive maintenance*, Scientific Data 12:493 (2025) + - Real measurements, repair records and specifications from a SCANIA truck fleet. SCANIA released the data as a public benchmark. The data set supports survival analysis. This is the most useful reference here. We can use this data now. +2. *Volvo Discovery Challenge at ECML-PKDD 2024* + - Failure risk prediction for more than 10,000 Volvo heavy trucks. 52 teams sent 791 entries. This shows that the data is available. It also shows how much attention the prediction problem gets. +3. Carpentier et al., *Towards contextual, cost-efficient predictive maintenance in heavy-duty trucks*, IDA 2024 + - SCANIA trucks and survival analysis. The authors write about cost and context in the maintenance decision. This is the nearest work to our scheduling question. +4. *Achieving Predictive Precision: LSTM and Pseudo Labeling for Volvo's Discovery Challenge* (2024) + - The second-place method. It has a macro-average F1 score of 0.879. This number shows the accuracy of failure prediction today. It also shows why we must not compete in that area. diff --git a/libs/@hashintel/brunch-agent/docs/research/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md b/libs/@hashintel/brunch-agent/docs/research/elicitation/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/research/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md rename to libs/@hashintel/brunch-agent/docs/research/elicitation/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md diff --git a/libs/@hashintel/brunch-agent/docs/research/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md b/libs/@hashintel/brunch-agent/docs/research/elicitation/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/research/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md rename to libs/@hashintel/brunch-agent/docs/research/elicitation/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md diff --git a/libs/@hashintel/brunch-agent/docs/specs/elicitation-kernel.md b/libs/@hashintel/brunch-agent/docs/specs/elicitation-kernel.md index 1606903c313..ecef1859624 100644 --- a/libs/@hashintel/brunch-agent/docs/specs/elicitation-kernel.md +++ b/libs/@hashintel/brunch-agent/docs/specs/elicitation-kernel.md @@ -10,6 +10,7 @@ Contradiction adjudications are collected in [Appendix A](#appendix-a--adjudicat Amended 2026-08-24 by [ADR-0005](../adr/0005-model-assisted-sdcpn-realization.md): code-bearing projections emit deterministic scaffolds and obligations; executable realization is downstream agent work. +Corrected by Mission 4 on 2026-09-01: the plugin unit now pairs a reusable domain typology with a target formalism; “never a domain” below continues to prohibit concrete domains, situations, and scenarios. ### Supersession map (2026-08-25) @@ -21,7 +22,7 @@ carries the operating truth, this map names it; the section itself is not rewrit | §5 envelope, §8 sweep and supersession, §11.1 "own payload structure" | [ADR-0003](../adr/0003-three-register-ir.md): captures are register 1; the elicited model is register 2, derived by a pure fold and never stored; projections are register 3. Envelope semantics unchanged. | | §6.1 `project` for code-bearing targets; §14.1 invariants 3 and 8 | [ADR-0005](../adr/0005-model-assisted-sdcpn-realization.md): the pure projection emits a scaffold, a typed code-obligation sidecar, and the loss report; executable realization is downstream application work. | | §9.5 completion derived, never a gate | [`elicitation-completion.md`](elicitation-completion.md): the invariants of `evaluateCompletion(model, mustKnowRows)` over the plugin file's `Must know` table, under [ADR-0006](../adr/0006-plugins-per-target-formalism.md). | -| §11.1 ElicitationPack (kernel cards, completion contract, clarification hints); §11.2 pack form | [ADR-0006](../adr/0006-plugins-per-target-formalism.md) and [`plugin-sdcpn/plugin.yaml`](../../packages/plugin-sdcpn/plugin.yaml): a plugin is one sectioned Markdown file per target formalism with fixed headings (`Purpose · Kinds · Must know · Patterns · Moves · Deliverable`); cards became kind-indexed `Patterns`, the completion contract became the `Must know` table, clarification hints became `Moves` steps. Principle v2 still governs the prose sections. `project`/`validate` remain plugin code ([`plugin-contract.md`](plugin-contract.md)). | +| §11.1 ElicitationPack (kernel cards, completion contract, clarification hints); §11.2 pack form | [ADR-0006](../adr/0006-plugins-per-target-formalism.md), as corrected by Mission 4, and [`plugin-sdcpn/plugin.yaml`](../../packages/plugin-sdcpn/plugin.yaml): a plugin defines one reusable domain-typology / target-formalism pairing under fixed headings (`Purpose · Kinds · Must know · Patterns · Moves · Deliverable`); cards became kind-indexed `Patterns`, the completion contract became the `Must know` table, clarification hints became `Moves` steps. Principle v2 still governs the prose sections. `project`/`validate` remain plugin code ([`plugin-contract.md`](plugin-contract.md)). | | §11.5 generic strategy cards | Unchanged in principle (guidance ownership follows vocabulary ownership); still named, not designed (FE-1406). Any harness-generic guidance would take the same `Patterns`/`Moves` shape. | | §13 portfolio and hybrid order ("both packs authored before the pack interface freezes") | [ADR-0006](../adr/0006-plugins-per-target-formalism.md): the interface is the heading contract and the three table grammars; the SDCPN file is authored, the Gherkin file is not; sequencing is owned by [STEERING](../control/STEERING.md). §13.1–13.3 target content is unchanged. | @@ -36,8 +37,7 @@ A standalone architecture that generalizes brunch's elicitor into **agentic inte pluggable elicitation targets**: a harness library on the Pi-family substrate (Flue first), deployable local and remote, in which an agent conducts a free-flowing interview, emits structured question affordances as conversation enhancements, and captures evidence-anchored structured -meaning into a durable target-document through idempotent sweeps — for any target-domain a plugin -defines. +meaning into a durable target-document through idempotent sweeps — for any concrete domain under the reusable domain-typology / target-formalism pairing a plugin defines. The governing design principle (adopted from the challenges doc): @@ -66,7 +66,7 @@ code. The system is fully decoupled from brunch's September MVP. ## 3. Vocabulary The canonical glossary is context [`CONTEXT.md`](../../CONTEXT.md) — shells (substrate / ui / harness -/ plugin / binding), sessions and durability (target-domain / target-document / session / capture +/ plugin / binding), sessions and durability (domain typology / domain / target formalism / target-document / session / capture store / re-entry briefing), and interaction terms (affordance / capture / sweep / settlement / interpretation render), now extended with the envelope vocabulary this spec relies on (capture envelope, evidence span, epistemic status, absence state, resolution record, supersession, pack, @@ -243,7 +243,7 @@ transport fact to earn `explicit`; nothing else may claim it. invalid / unsupported / unmapped / low-confidence` plus factual attributes (origin, references, can-default). Issues close only explicitly; `conflicting` closes **only** via a resolution record (§8.5). Two producers, **namespaced to their producer** (harness envelope issues vs. - plugin issues under their plugin/target-domain namespace) — restating criteria-doc invariant 6: + plugin issues under their plugin namespace) — restating criteria-doc invariant 6: a target-originated requirement never silently becomes a semantic requirement. - An **advisory** is a **computed, ephemeral fact** — surfaced to the agent at trigger or read time, never stored in the capture store, never blocking (adjudicated, L6). Named advisories: @@ -410,7 +410,7 @@ re-entry briefing, §9.3) and the agent judges whether to sweep before proceedin ### 9.1 Durable target-document, transient sessions, sweep as the only bridge -- **Target-document** = one target-domain + its capture store + its session history. Its +- **Target-document** = one concrete domain under one plugin's domain-typology / target-formalism pairing, plus its capture store and session history. Its authoritative state is **the capture store plus all session logs — never the render**. Projections, renders, and artifacts are strictly derived: cacheable, disposable. Session logs are durable truth too: discarding swept logs would dead-end every capture's evidence pointers. @@ -593,7 +593,7 @@ from "accept this"; agenda as derived state, never stored. Some interviewing technique is target-independent. Socratic pressure on premises, contrastive cases that separate competing interpretations, stress-testing the weak points of an argument — these operate on vocabulary the **harness** owns (conflicts, alternatives, ambiguity, weak or -missing evidence, absence clusters), not on any plugin's domain. By the same rule that governs +missing evidence, absence clusters), not on any plugin's domain typology. By the same rule that governs schemas ("the shell that defines a capability owns its affordance schemas", §4), **guidance ownership follows vocabulary ownership**: cards that teach _what to notice in a domain_ are plugin pack content; cards that teach _how to work an interview situation the envelope can name_ @@ -607,7 +607,7 @@ cases where plausible interpretations diverge and have the person classify them, asking abstract questions), plus brunch's `elicitation_style: interrogate | disambiguate | propose` trichotomy, which the exchange-schema audit already classed generic. The assurance target is the worked example of the split: its technique decomposes into a generic -stress-the-argument strategy card plus the plugin's domain cards (§13.2). +stress-the-argument strategy card plus the plugin's domain-typology cards (§13.2). ## 12. Shipping shape @@ -791,7 +791,7 @@ Milestone-one contract (one record type): 4. **Corrections don't erase history.** Superseded captures remain inspectable and never active. 5. **Retries are semantically idempotent.** A retried operation or re-swept range never creates a second user assertion (content-keyed capture identity). -6. **Issues are namespaced to their producer.** A plugin/target-domain requirement never silently +6. **Issues are namespaced to their producer.** A plugin-profile requirement never silently becomes a harness-level requirement; harness envelope issues are namespaced to the harness. 7. **Plugin failures are atomic.** A failed operation leaves no partially applied deltas; sweeps apply whole or refuse whole. diff --git a/libs/@hashintel/brunch-agent/docs/specs/petrinaut-batched-construction-tools.md b/libs/@hashintel/brunch-agent/docs/specs/petrinaut-batched-construction-tools.md new file mode 100644 index 00000000000..56eb064d25b --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/specs/petrinaut-batched-construction-tools.md @@ -0,0 +1,202 @@ +# Batched Petrinaut construction tools (`pn_read` / `pn_edit`) + +Status: **candidate design input for Mission 9, not a selected mechanism**. Drafted 2026-09-02 from a code survey of `@hashintel/petrinaut-core`, `@hashintel/petrinaut`, `@hashintel/brunch-agent/packages/plugin-sdcpn`, `@apps/brunch-agent`, and `@flue/runtime@2.0.3`. Live authority remains [`MISSION.md`](../../MISSION.md); [Mission 9 — traceable projection](../mission-drafts/9-traceable-projection.md) owns the broad provider-schema, mutation-sequence, and partial-failure boundaries this proposal addresses. Mission 6 may exercise only the least meaningful browser mutation needed for its prepared-fixture viability tracer; Mission 9 must repair the broader known schema carrier before deciding whether a bounded atomic batch is the least sufficient construction mechanism. Nothing here is evidence that the design works or authority to implement `pn_read` or `pn_edit`. + +## Problem + +The stock Petrinaut assistant constructs a net one mutation per tool call, across 41 mutation tools plus commands and read tools (`mutationActionInputSchemas` in `petrinaut-core/src/action-schemas.ts`). Brunch's `plugin-sdcpn` mounts a six-tool subset of that surface for construct-only conversations (`getLatestNetDefinition`, `addType`, `addParameter`, `addPlace`, `addTransition`, `addArc`) and executes the calls client-side through Petrinaut's canonical callbacks. + +The proposal under consideration is to replace that per-mutation surface, for the Brunch agent, with two tools: + +- `pn_read` — return the current net (`{ title, definition, extensions }`). +- `pn_edit` — accept an ordered array of one or more canonical mutation actions and apply them as one squashed change. + +The intended gains are fewer model round trips, a coherent net emitted in one move (types → parameters → places → transitions → arcs), no half-built intermediate states, and a smaller tool list for the model to reason over. + +This document records what the code actually affords, what the earlier failure actually was, the design a batch tool should take, and which probes to run first. + +## Observations + +Each observation names its evidence. Claims about the model's behaviour come from the Mission 3 record, not from new runs. + +### O1. Petrinaut has no batch or transaction contract; the local JSON handle exposes a promising primitive + +`Petrinaut.mutations` (`petrinaut-core/src/instance.ts`) is built by `createPetrinautActions(mutate, extensions)` (`actions.ts:415`). Every action parses its input against its own Zod schema and then calls the injected mutation function through `mutateWithExtensionGuards`. The instance's private mutation closure enforces effective readonly and disabled-extension behavior before reaching `handle.change`. + +`createJsonDocHandle().change` (`handle/json-doc-handle/create-json-doc-handle.ts`) runs `produceWithPatches(current, draft => fn(draft))` and only assigns `current` after the callback succeeds. A throw propagates and leaves this handle's current document untouched. One successful state-changing outer call emits one change event and creates at most one history checkpoint when history is enabled. + +That observation does **not** establish a general transaction contract. `PetrinautDocHandle.change` does not promise rollback on throw, transactionality, history, patch count, or synchronous publication, and a direct `instance.handle.change` call bypasses instance-level readonly and extension policy. A batch therefore needs a first-class core operation that reuses the instance's effective mutation authority, or it must be explicitly restricted to a handle whose transactional semantics are part of its contract. Intra-batch references are feasible because later steps can see earlier changes to the same draft, but caller-supplied IDs alone do not guarantee uniqueness or idempotency. + +### O2. The failure Mission 3 recorded is a schema-carrier failure, not a granularity failure + +`@flue/runtime@2.0.3` types tool input as `v.GenericSchema` (`dist/types-*.d.mts:79`). Its schema module checks for a Standard Schema marker and then **rejects any vendor other than `valibot`** (`dist/schema-*.mjs`: `schema["~standard"].vendor === "valibot"`, else `TypeError("[flue] Expected a Valibot schema.")`). The provider-visible JSON Schema is produced by `@valibot/to-json-schema` with `errorMode: "ignore"`, which silently drops constructs it cannot represent — including `rawTransform`. + +`plugin-sdcpn/src/tools/petrinaut-construction.ts` therefore wraps each canonical Zod schema in `v.pipe(v.looseObject({}), v.rawTransform(zodParse))` and pastes the Zod-generated JSON Schema into the tool *description*. Measured output of that carrier: + +```json +{"type":"object","properties":{},"required":[]} +``` + +The provider receives no machine-enforced parameter shape; the model sees the canonical JSON Schema only as unstructured descriptive text. The paid Mission 3 run (`docs/evidence/implementations/fe-1525-headless-runbook-pn.md`) encoded `addType.elements` as a string nine times, was correctly rejected nine times by the runtime Zod parse, never corrected, and produced an empty net. `docs/mission-drafts/9-traceable-projection.md` records the accepted broader next move: Flue support for Standard Schema or supplied JSON Schema, or a mechanical shape-preserving conversion; extending the opaque carrier or hand-copying Petrinaut fields into Valibot stays rejected. + +Consequence for this proposal: `pn_edit`'s payload — an array of a discriminated union of nested objects — is strictly harder to carry than `addType` was. Through the current carrier it would fail identically, and every action in the batch would fail together. **Batching does not address the recorded blocker; it inherits it.** + +### O3. A mechanically derived batch schema is compact for a subset and unusable at full parity + +A local measurement with the installed Zod 4 and `z.toJSONSchema(schema, { io: "input", unrepresentable: "any" })` produced the following provisional values. They are not yet a reproducible artifact and will drift with the selected action set, descriptions, and Zod output; any implementation decision must check in the exact subset manifest, keyword inventory, and deterministic measurement: + +| Envelope | Bytes | ≈ tokens | +| --- | --- | --- | +| `{ actions: Array<oneOf[5 current construction actions]> }` | 18,293 | ~4,600 | +| `oneOf[all 41 mutation actions]` | 112,466 | ~28,000 | + +The five-action envelope preserves every nested shape (`elements` is an array of objects with `elementId`/`name`/`type`; `inputArcs` carries the `endpoint` discriminated union) and every `.meta({ description })` string, because Zod's JSON Schema emitter carries descriptions and structural constraints while dropping runtime-only refinements (`.check`, `.superRefine`). That split is exactly what a provider needs: shape and guidance in the schema, semantic validation at runtime. + +Full parity is ~28k tokens per turn and a 41-branch `oneOf`; `MISSION.next.md` already rejects broad 46-tool parity. A batch tool must be a subset. + +### O4. The existing read contract should be reused, but `pn_read` is not already a production tool + +`getLatestNetDefinition` returns `{ title, definition, extensions }` (`petrinaut-core/src/ai.ts`; host execution in the stock panel and headless harness). No new read shape is warranted. Current Brunch production client-tool routing does not execute this construction tool, and renaming it to `pn_read` would require an explicit panel/client dispatch alias. Retain the canonical name unless a model-facing naming probe earns the alias. A compact projection (names and IDs only) is a possible later economy, not a present requirement. + +### O5. Per-mutation tooling carries UX that a batch does not + +The stock panel (`petrinaut/src/ui/views/Editor/panels/ai-assistant-panel.tsx`) renders one tool card per call (`tool-summaries.ts`), waits for a diagnostics refresh per mutation, gates some commands behind interactive widgets, and yields one undo step per call. None of this matters for a headless or off-canvas construction conversation. For the live door (Petrinaut panel → `useChat`/`onToolCall` → Flue `ChatAgent`), a batch tool needs a panel-side handler and a summary renderer; that is host work, not plugin work. + +### O6. Feedback granularity is the real trade-off, not call count + +Per-action tools give the model a correction opportunity after every call. A batch commits the model to a large structure before any feedback, and one bad arc weight rejects thirty otherwise-valid actions. Mission 3's model repeated the same malformed call nine times without correcting when the feedback was a bare `expected array, received string` and the schema gave it nothing to correct against. The lesson is that **feedback precision and schema fidelity dominate round-trip count**. A batch tool is only an improvement if its error output pinpoints the failing action and path, and if its schema is visible to the provider. + +## Design + +### Placement + +Ownership splits at the semantic boundaries: + +- **`@hashintel/petrinaut-core`:** canonical subset-derived batch schemas; a first-class transactional dispatcher if the handle/instance contracts can support it; effective readonly and extension policy; rollback and per-step applied/no-op/failure semantics. This belongs on `Petrinaut` rather than in an AI helper that reaches through `instance.handle`. +- **SDCPN plugin:** the bounded Brunch construction subset, model-facing tool semantics, and construct-only mounting. +- **Brunch binding/app:** Flue schema carriage, production client-tool classification and suspension/resume, panel execution, summaries, and provider-versus-canonical error presentation. +- **Mission 6 projection operation:** base/current revision, operation identity and duplicate delivery, stable generated IDs, derivation commitment after confirmed state, and semantic correspondence with the selected workpiece region. + +This keeps canonical field shapes and generic mutation behavior in Petrinaut without moving Brunch provenance or Flue contracts into the published core library. + +### Schema + +```ts +// petrinaut-core/src/ai.ts (sketch) +const batchStep = <Name extends MutationActionName>(name: Name) => + z.strictObject({ + action: z.literal(name), + input: mutationActionInputSchemas[name], + }); + +export const createMutationBatchSchema = <Names extends readonly MutationActionName[]>(names: Names) => + z.strictObject({ + actions: z + .array(z.discriminatedUnion("action", names.map(batchStep) as never)) + .min(1) + .meta({ description: "Ordered mutations submitted to one transactional dispatcher. IDs are caller-supplied; later steps may reference IDs introduced by earlier steps in the same batch." }), + }); +``` + +The `{ action, input }` envelope is preferred over `.extend({ action })` because several action schemas are `ZodPipe`s (`parameterSchema.superRefine`) that do not extend cleanly, and because the envelope keeps the canonical input schema byte-identical to the per-tool one. + +The caller chooses the `names` subset. The initial Brunch subset is the current five construction actions; `update*`/`remove*` pairs and `addScenario`/`addMetric` enter only when a named consumer (Mission 6's region, Mission 9's optimisation handoff) makes them load-bearing. + +### Transactional dispatcher + +The earlier `applyMutationBatch(instance.handle.change(...))` sketch is rejected: its recorded failure return was unreachable after rethrow, it bypassed instance readonly and extension policy, and the general handle interface does not guarantee rollback. The candidate core contract is instead a first-class operation created beside `mutations` inside `createPetrinaut`, where it can reuse the same effective mutation authority. + +A viable contract must: + +1. parse the outer envelope against the exact selected subset rather than trusting TypeScript or an arbitrary action key; +2. distinguish provider/Flue structural rejection from canonical per-step rejection; +3. execute steps in order inside one explicitly transactional mutation boundary; +4. abort on the first failure and return its `{ index, action, path, message }` after rollback; +5. preserve effective readonly and disabled-extension behavior exactly; +6. report a per-step outcome or enforce postconditions so silent canonical no-ops cannot masquerade as applied changes; and +7. return confirmed resulting state only after the underlying handle publishes the transaction. + +If the existing handle abstraction cannot support that contract generally, restrict the first implementation to a named transactional handle or strengthen the handle capability contract. Do not infer atomicity from `change` alone. + +### Required semantics + +- **Atomic where claimed.** Canonical per-step failure leaves state unchanged only on a boundary whose rollback behavior is explicit and tested. Provider/Flue envelope rejection occurs before that boundary and is a distinct failure class. +- **Ordered.** Later steps observe earlier successful steps in the same draft. +- **Outcome-honest.** Success cannot mean merely “no exception”: canonical actions may intentionally no-op when extensions are disabled, IDs are absent, or arcs are duplicates. The result must identify applied/no-op outcomes or verify the requested postconditions. +- **Identity-explicit.** Caller-supplied IDs permit intra-batch references but do not enforce uniqueness, replay safety, or stable projection identity. Mission 6 owns those surrounding contracts. +- **First-failure precision.** Return `{ index, action, path, message }` for the first canonical failure; do not collect cascades after a rejected prerequisite step. +- **Change-count scoped to the handle.** The local JSON handle should emit one change event and at most one history checkpoint for a successful state-changing batch. Diagnostics refresh and other host behavior require separate panel evidence. +- **Read-after-write included.** On confirmed success, return the resulting definition so the model need not read after every edit. Measure before replacing it with a compact summary. + +### Candidate tool surface for Brunch + +- Read → reuse `getLatestNetDefinition` and its output shape. Treat `pn_read` as an unearned alias until a naming reason and production dispatch path exist. +- Batch edit → a bounded subset-derived schema mounted under the same construct-only gate as today's tools, executed client-side through the core transactional contract. `pn_edit` remains a candidate name and mechanism until the Mission 6 probes establish schema carriage, transaction/outcome semantics, and a real advantage over per-action tools. +- If Mission 6 selects batching, the per-action Brunch construction tools are replaced rather than co-mounted. Mission 7 may extend the selected subset only for mutation classes required by its accepted correction. + +### What stays out + +- No `mode: "best-effort"`. Atomic only, until observed strain. +- No compact read projection, no server-side diff/desired-state recomputation (`MISSION.next.md` calls full-net recomputation "fog"). +- No Brunch-specific vocabulary or Flue types in `petrinaut-core`. +- No hand-written Valibot mirrors of Petrinaut schemas. + +## Prerequisite: a shape-preserving provider schema + +This is the gate for the whole proposal and for Mission 6's first repair item. Three routes, in order of preference: + +1. **Upstream Flue accepts non-Valibot Standard Schema or a supplied JSON Schema.** Flue is external (`withastro/flue`). Its schema module already detects `~standard`; the vendor check is the only thing excluding Zod 4. This is the cleanest fix but is not in our control and has no delivery date. +2. **Mechanical JSON Schema → Valibot conversion, local to Brunch.** Zod's `toJSONSchema()` output for these schemas uses only structural constructs: `object` with `properties`/`required`/`additionalProperties: false`, `array` with `items`/`minItems`, `string`/`number`/`integer`/`boolean`, `enum`, `const`, `oneOf` (discriminated unions), `anyOf` with `null` (nullable), `minLength`, `minimum`/`exclusiveMinimum`, and `description`. A converter over that closed subset produces a Valibot schema whose `@valibot/to-json-schema` output preserves shape and descriptions. Runtime validation continues to delegate to Zod via `rawTransform`, which the provider never sees — the arrangement the existing carrier intended but could not deliver. Prefer a maintained package if one exists and covers the subset; otherwise write the converter and pin it with a test that round-trips every schema in the chosen subset and fails on any unhandled JSON Schema keyword (no silent drops — that is how the current carrier failed). +3. **Extend the opaque carrier.** Rejected in the Mission 3 evidence and again here. + +## Recommended Mission 6 probe sequence + +Run these in order so each failure has one interpretation. + +### Probe 1 — single-action shape-preserving carrier + +**Question.** Can Flue expose the exact canonical nested `addType.elements` shape that failed in Mission 3 as provider-enforced structure? + +**Work.** Use the least supported shape-preserving route, mount one canonical nested action, and compare provider-visible JSON Schema with the canonical Zod output. Check in the exact Zod version, deterministic schema measurement, keyword inventory, positive/negative samples, and a fail-closed assertion for every unhandled keyword. Then run one budgeted real-model call with retained raw arguments and runtime result. + +**Oracle.** Hermetic schema comparison plus the one authorized real-provider trace. Passing retires only the carrier blocker; it does not select batching or prove construction. + +**Stop if** no supported mechanical path preserves the nested shape. Record the exact unsupported keyword or Flue boundary; do not widen the carrier or hand-copy fields. + +### Probe 2 — first-class transactional batch contract + +**Question.** Can Petrinaut core expose a bounded batch operation with explicit rollback, readonly/extensions parity, indexed failure, and honest no-op outcomes? + +**Work.** Add the smallest subset-derived schema and first-class operation beside `mutations` inside `createPetrinaut`. Against each supported handle/capability combination, test ordered intra-batch references, successful resulting state, readonly refusal, disabled-extension parity with sequential mutations, duplicate/missing-ID and canonical no-op behavior, and rollback after a zero-weight arc at index 4. For `createJsonDocHandle`, assert one change event and at most one history checkpoint when enabled. + +**Oracle.** Core tests comparing batch output with the equivalent canonical sequence and proving every advertised semantic. A test against only `createJsonDocHandle` supports only a JSON-handle-scoped contract. + +**Stop if** the current handle contract cannot make rollback dependable. Narrow the supported handle or propose the smallest explicit capability; do not reach through `Petrinaut` to `handle.change`. + +### Probe 3 — bounded batch through Flue and the production client path + +**Question.** After Probe 1 and Probe 2 pass, does the five-action batch preserve its discriminator and nested shapes through Flue, produce actionable indexed feedback, and improve the selected construction path over canonical per-action tools? + +**Work.** Carry the exact five-action subset through the proven schema route, classify provider-envelope and canonical per-step failures separately, wire production client-tool dispatch, and exercise one construct-only run. Compare schema cost, calls, latency, correction behavior, resulting state, and failure visibility with the per-action control. Do not use non-empty output alone as the verdict. + +**Oracle.** Hermetic schema diff, production-path integration test, and an owner-authorized real-model comparison retained with the exact instrument and state artifacts. + +**Stop if** batching obscures feedback, silently no-ops, cannot reject stale/duplicate delivery at the projection layer, or does not improve the selected case enough to justify the new core and host contracts. In that case Mission 6 retains per-action tools on the repaired carrier. + +### Deferred + +- Panel-side batch handler and summary card for the live door — only after Probes 1 and 2 succeed and Probe 3 reaches the production client path. +- `update*`/`remove*` and scenario/metric actions — when Mission 6's region or Mission 9's handoff names them. +- Compact read projection — when measured token cost of returning the full definition is the strain. + +## Risks and open questions + +- **Blind commit.** Even with a good schema, the model builds a large structure before any feedback. If real runs show repeated batch rejections for semantic (not shape) reasons, consider prompting the model to batch by layer (types and parameters first, then places, then transitions and arcs) before considering a non-atomic mode. +- **Schema size drift.** Petrinaut descriptions are long by design (they are the model's guidance). Adding actions to the subset grows the per-turn cost roughly linearly; re-measure with the `toJSONSchema` byte count on each subset change. +- **Transactional scope.** A JSON-handle proof does not establish rollback for every `PetrinautDocHandle`. Advertise only the handles/capabilities the core contract and tests cover. +- **Silent no-op.** Missing IDs, duplicate arcs, and disabled extensions can return without throwing. Require explicit outcomes or postconditions before a projection or derivation is marked successful. +- **Identity and replay.** Caller-supplied IDs are not uniqueness, stale-base, or idempotency enforcement. Mission 6 must bind batch execution to current state and duplicate-delivery policy. +- **Split validation.** Provider/Flue envelope errors occur before canonical indexed dispatch. Keep these failure classes visible rather than pretending one result shape covers both. +- **Sanitisation parity.** The batch must produce exactly the document the equivalent sequence of `instance.mutations.*` calls would produce under the same effective extensions. Cover this with disabled-extension cases. +- **Undo granularity in the stock editor.** If the stock assistant ever adopts the batch, one history checkpoint for a multi-action edit is a UX decision the Petrinaut owners should make, not a side effect. +- **Upstream Flue.** Worth an issue on `withastro/flue` proposing acceptance of any Standard Schema v1 vendor with a supplied JSON Schema; that removes the converter entirely if accepted. Do not wait on it. diff --git a/libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md b/libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md index 2df77b1d097..4f3673e788c 100644 --- a/libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md +++ b/libs/@hashintel/brunch-agent/docs/specs/plugin-contract.md @@ -1,32 +1,12 @@ -# Spec: the plugin contract — one definition per target formalism - -Status: **provisional**, reshaped 2026-08-25 by -[ADR-0006](../adr/0006-plugins-per-target-formalism.md) (a plugin is per formalism, never per -domain) and amended the same day by -[ADR-0007](../adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md) (a plugin is data -under harness-owned keys). Ratification condition (inherited from -[ADR-0003](../adr/0003-three-register-ir.md)): a worked pass across at least three plugin -targets on a real fold. Decided on: FE-1405 (registers), FE-1480 (ADR-0005 outputs), FE-1431 -(the key contract), and the 2026-08-25 design-convergence review. The normative exemplars are -[`plugin-sdcpn/plugin.yaml`](../../packages/plugin-sdcpn/plugin.yaml) and -[`plugin-gherkin/plugin.yaml`](../../packages/plugin-gherkin/plugin.yaml), co-authored against the -same schema; where this document and the schema -([`packages/core/schema/plugin.schema.json`](../../packages/core/schema/plugin.schema.json), -derived from `PluginDefinitionSchema`) disagree about shape, the schema wins and this document is -amended. The retired declarative draft is archived at -[`plugin-contract-2026-08-25-declarative-draft.md`](../archive/specs/plugin-contract-2026-08-25-declarative-draft.md). +# Spec: the plugin contract — one definition per domain-typology / target-formalism pairing + +Status: **provisional**, reshaped 2026-08-25 by [ADR-0006](../adr/0006-plugins-per-target-formalism.md), amended by [ADR-0007](../adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md), and corrected by Mission 4 on 2026-09-01: a plugin pairs a reusable domain typology with a target formalism and is never keyed to a concrete domain, situation, or scenario. Ratification condition (inherited from [ADR-0003](../adr/0003-three-register-ir.md)): a worked pass across at least three plugin pairings on a real fold. Decided on: FE-1405 (registers), FE-1480 (ADR-0005 outputs), FE-1431 (the key contract), and the 2026-08-25 design-convergence review. The normative exemplars are [`plugin-sdcpn/plugin.yaml`](../../packages/plugin-sdcpn/plugin.yaml) and [`plugin-gherkin/plugin.yaml`](../../packages/plugin-gherkin/plugin.yaml), co-authored against the same schema; where this document and the schema ([`packages/core/schema/plugin.schema.json`](../../packages/core/schema/plugin.schema.json), derived from `PluginDefinitionSchema`) disagree about shape, the schema wins and this document is amended. The retired declarative draft is archived at [`plugin-contract-2026-08-25-declarative-draft.md`](../archive/specs/plugin-contract-2026-08-25-declarative-draft.md). ## What a plugin is -A plugin is **per target formalism** — Gherkin, SDCPN — never per domain. It is one authored -`plugin.yaml` whose keys are fixed by the harness, plus a small amount of code for `project` and -`validate`. The harness reads the contract keys into the model vocabulary, the demand list, and -the pattern index; it renders every other key into the interviewer's instructions interleaved -with its own teaching — for each key, the harness's definition of the key, then the repertoire's -default, then the plugin's cell. The end user never edits the file. +A plugin defines one reusable **domain typology / target formalism pairing** — for example, software behavior / Gherkin or operational processes / SDCPN — never one concrete domain. It is one authored `plugin.yaml` whose keys are fixed by the harness, plus a small amount of code for `project` and `validate`. The harness reads the contract keys into the model vocabulary, demand list, and pattern index; it renders every other key into the interviewer's instructions interleaved with its own teaching — for each key, the harness's definition of the key, then the repertoire's default, then the plugin's cell. The end user never edits the file. -The keys fall in four groups (ADR-0007 decision 2), under an identity block `plugin` (`id`, -`version`, `formalism`, `jobs`, `purpose`): +The keys fall in four groups (ADR-0007 decision 2), under an identity block `plugin` (`id`, `version`, `domain_typology`, `formalism`, `jobs`, `purpose`): | group | keys | who fills it | | ----------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------- | @@ -49,9 +29,7 @@ precision words. Such an item is rendered only when at least one plugin demand n word. This conditions generic teaching on the plugin contract without allowing a plugin to override the repertoire. -Domain-neutrality rule: nothing in the definition may name a domain. A new case that seems to -need a new row is a finding about the abstraction, decided by review, never content added to a -plugin. +Concrete-domain-neutrality rule: a definition may name and teach its reusable domain typology, but it may not name a particular organization, operation, situation, or scenario. A new concrete case that seems to need a new row is a finding about the abstraction, decided by review, never case content added to a plugin. ## Relation to the three registers @@ -95,8 +73,7 @@ Rules the reader enforces beyond the schema: ## Version binding -The identity block declares an immutable version string (`sdcpn/2026-08-25.2`, -`gherkin/2026-08-25.1`). Every completion evaluation, projection output, and delivered report +The identity block declares an immutable version string (currently `sdcpn/2026-09-01.1` and `gherkin/2026-09-01.1`). Every completion evaluation, projection output, and delivered report carries that version together with the target-document revision it read. A report for one plugin version is not comparable with a model folded under another; the caller retries rather than mixing them. The repertoire carries its own version (`repertoire/…`). @@ -146,10 +123,9 @@ IR slot, fourth register, or plugin operation. `reconcile` remains optional. The primary seam is still the fold: `fold(definition, activeCaptures) → model`, golden-tested with hand-worked capture sets in and slot states out. Gates: the **definition read gate** (schema match with no unknown key; every `must_know` kind exists; the anchor is a counted row; runbooks -belong to declared jobs), the **shipped-definition gate** (both plugins load, add no key, name no -domain, and declare different anchors under the same schema), the **schema drift gate** +belong to declared jobs), the **shipped-definition gate** (both plugins load, declare their domain typology, add no key, name no concrete domain, and declare different anchors under the same schema), the **schema drift gate** (`plugin.schema.json` equals the emitted view of the valibot schema), the **repertoire gate** -(every key filled, every entry sourced, no formalism or domain word), the **render-order gate** +(every key filled, every entry sourced, no domain-typology, formalism, or concrete-domain content), the **render-order gate** (preamble → contract → guidance keys in catalogue order → runbooks per declared job; definition before default before cell), and the **completion fixtures** of `evaluateCompletion` described in [`elicitation-completion.md`](elicitation-completion.md). Test-fit order stands: smallest honest diff --git a/libs/@hashintel/brunch-agent/docs/specs/structurally-typed-elicitation-runbooks.md b/libs/@hashintel/brunch-agent/docs/specs/structurally-typed-elicitation-runbooks.md index 3f2281b8fc6..b916beaf271 100644 --- a/libs/@hashintel/brunch-agent/docs/specs/structurally-typed-elicitation-runbooks.md +++ b/libs/@hashintel/brunch-agent/docs/specs/structurally-typed-elicitation-runbooks.md @@ -5,6 +5,8 @@ Status: **accepted design input for Mission 3**. Live execution authority remain the first architecture to test; it is not evidence that the design works. Reorient the mission if the real Flue path contradicts it. +Amended 2026-09-01 by Mission 4's accepted plugin-scope correction: each plugin profile couples a reusable domain typology with a target formalism while remaining independent of any concrete domain, situation, or scenario. Formalism-only language below is corrected accordingly. + Unless explicitly identified as the existing typed three-register IR, **IR** below means the **runbook IR**: Mission 3's structurally typed Markdown workpiece. @@ -19,10 +21,7 @@ The following decisions were reached before implementation. Markdown hierarchy and repeated entry shapes may be strict while their contents remain prose. Mission 3 does not require captures, IR fields, or runbook entries to participate in a closed semantic type system. -3. **The reusable split is universal repertoire versus target-formalism runbook.** Universal - teaching explains generally useful elicitation judgment. A target-formalism runbook says what - that judgment should attend to, pursue, preserve, transform, and check for SDCPN modelling. It - is not keyed to a concrete situation such as a truck fleet or semiconductor fab. +3. **The reusable split is universal repertoire versus plugin profile.** Universal teaching explains generally useful elicitation judgment. A plugin profile couples a reusable domain typology with a target formalism and says what that judgment should recognize, pursue, preserve, transform, and check for the pairing. It is not keyed to a concrete situation such as a particular truck fleet or semiconductor fab. 4. **The two authored layers may merge into one model-facing projection.** Mission 3 will author that first projection directly. It will not build a compiler or revive the old plugin renderer before a second real consumer creates strain. @@ -60,7 +59,7 @@ Rejected first shapes: Deferred decisions: - the final heading catalogue and exact resource boundaries; -- whether repeated use earns an automated repertoire + target-formalism projection; +- whether repeated use earns automated composition of the repertoire and plugin profile; - which, if any, runbook or IR concepts later become semantically typed; - whether later lifecycle phases warrant distinct skills or agents under observed strain; - canvas mutation and programmatic PN loading; @@ -105,10 +104,10 @@ The harness repertoire and its research sources already establish useful general Primary local syntheses include [`elicitation-strategy-literature.md`](../research/elicitation/elicitation-strategy-literature.md), [`frontier-model-elicitor-failure-catalogue.md`](../research/elicitation/frontier-model-elicitor-failure-catalogue.md), -and the current [`repertoire.yaml`](../../packages/core/src/repertoire.yaml). Their content is +and the current [`repertoire.yaml`](../../packages/core/src/teaching/repertoire.yaml). Their content is source material; Mission 3 does not restore the repertoire runtime. -### Target-formalism teaching +### Plugin teaching: domain typology and target formalism The SDCPN material already identifies reusable typologies of modelling situations rather than concrete scenario facts: @@ -121,13 +120,12 @@ concrete scenario facts: - shared-resource contention and practiced policies; - discrete events, continuous dynamics, mode changes, thresholds, and probabilistic outcomes; - recurring PN construction patterns for timed work, branching, and related structures; -- formalism-specific caveats, failure modes, losses, and validity checks. +- domain-typology- and formalism-specific caveats, failure modes, losses, and validity checks. The current [`plugin-sdcpn/plugin.yaml`](../../packages/plugin-sdcpn/plugin.yaml), its archived CPS guidance and replays, and the independently written process-to-PN notes converge on this shape. The archived guidance also records the important correction that its former `domain` tag was a -mis-tag: the useful cards describe model-situation types that lift to the target-formalism level -without naming an operational domain. +mis-tag: the useful cards describe model-situation types that belong to the plugin's reusable domain typology without naming a concrete operational domain. ### External resonance @@ -148,16 +146,16 @@ organization, not evidence that models secretly parse a fixed heading schema. | Term | Definition | | --- | --- | -| **Universal repertoire** | Generally applicable elicitation concepts, directives, procedures, judgment activations, caveats, and failure knowledge. It teaches *how to elicit* without naming a target formalism or concrete scenario. | -| **Target-formalism runbook** | Human-readable guidance for eliciting and constructing one artifact family, initially SDCPN: what to investigate, notice, deepen, preserve, transform, and check. | -| **Rendered runbook** | The model-facing combination of universal repertoire and target-formalism content, organized by a known Markdown hierarchy. In Mission 3 it is authored directly rather than compiled. | +| **Universal repertoire** | Generally applicable elicitation concepts, directives, procedures, judgment activations, caveats, and failure knowledge. It teaches *how to elicit* without naming a domain typology, target formalism, or concrete scenario. | +| **Plugin profile** | Human-readable guidance coupling one reusable domain typology with one target formalism, initially operational processes / SDCPN: what to investigate, notice, deepen, preserve, transform, and check. | +| **Rendered runbook** | The model-facing combination of universal repertoire and plugin-profile content, organized by a known Markdown hierarchy. In Mission 3 it is authored directly rather than compiled. | | **Runbook skill** | The one Flue skill package that delivers the rendered runbook, lifecycle procedure, IR template, construction guidance, and checks through progressive disclosure. | | **Legacy YAML runbook cells** | The existing schema field named `runbooks`, containing `kickoff`, `trajectory`, and `close` cells per job. It keeps its code-level name but represents only the lifecycle region of the broader runbook concept. | | **Structural typing** | Required heading families, nesting, repeated entry shapes, and completion fields whose contents may remain prose. Structure determines where meaning belongs without closing its semantic vocabulary. | | **Semantic typing** | Closed kinds, slots, values, proposal types, grades, firing predicates, or fold rules that require content to be classified into a formal semantic system. Deferred in Mission 3. | | **Runbook IR** | The structurally typed Markdown workpiece filled from the conversation and consumed by PN generation. It can represent unknowns, assumptions, caveats, and unresolved questions without typed capture claims. It is an experiment in an intermediate representation, distinct from the existing typed three-register **IR**. | | **Lifecycle phase** | A mode of work performed by the same agent: orient, elicit, maintain/review the IR, construct the PN, and check/deliver. A phase selects relevant runbook material; it is not a separate agent. | -| **Situation typology** | A recurring model-relevant shape—timed work, probabilistic outcome, contended resource, threshold trigger—applicable across concrete operational domains. | +| **Situation typology** | One recurring model-relevant shape within a plugin's domain typology—timed work, probabilistic outcome, contended resource, threshold trigger—applicable across concrete domains. | ## Architecture @@ -173,13 +171,13 @@ that return path without inventing a state machine. ### Two authored knowledge layers -The universal repertoire and target-formalism runbook remain conceptually separate because their +The universal repertoire and plugin profile remain conceptually separate because their ownership and reuse differ: ```text universal repertoire: how elicitation goes well + -target-formalism runbook: what SDCPN elicitation and construction require +plugin profile: what the operational-process typology and SDCPN formalism require = rendered runbook: what this agent reads ``` @@ -291,7 +289,7 @@ What to investigate ├─ policies, exceptions, and practiced rules └─ validation criteria -Target-formalism guidance +Plugin guidance ├─ lenses and heuristics ├─ situation typologies and patterns ├─ caveats and rabbit holes @@ -425,7 +423,7 @@ over the real Flue path to produce a validatable PN. It does not establish: - Activation yields the lifecycle procedure. - Supporting resources are listed and readable through Flue's native resource affordance. - Each required runbook responsibility and IR section has one authoritative home. -- Universal and target-formalism material are distinguishable by content and provenance even where +- Universal and plugin-profile material are distinguishable by content and provenance even where rendered together. ### Behavioral checks @@ -446,7 +444,7 @@ The runbook is improved empirically: 1. run a fixed elicitation situation through the headless path; 2. inspect the conversation, resource reads, filled IR, PN, and checks; -3. classify the miss as universal teaching, target-formalism guidance, IR structure, construction +3. classify the miss as universal teaching, plugin-profile guidance, IR structure, construction guidance, or tool/runtime behavior; 4. edit the single owning location; 5. rerun without adding semantic machinery unless the miss requires it. @@ -463,7 +461,7 @@ Mission 3's runbook design is successfully exercised when: 3. The skill progressively exposes lifecycle procedure, elicitation teaching, IR template, PN construction guidance, and checks using Flue's native skill/resource surfaces. 4. The runbook has the structural responsibilities defined above and incorporates both universal - elicitation teaching and SDCPN target-formalism content. + elicitation teaching and operational-process/SDCPN plugin-profile content. 5. A headless conversation yields a recoverable, structured-but-not-strictly-semantically-typed IR. 6. The same agent can use that IR and disclosed construction guidance to produce PN JSON. 7. Petrinaut accepts the output at the parser/validation boundary selected by the mission. @@ -477,7 +475,7 @@ Mission 3's runbook design is successfully exercised when: - One runbook skill; no speculative skill catalog. - Flue's system instruction, skill activation, supporting-resource, and tool happy paths. - Direct Markdown authoring before automated projection. -- Target-formalism content, not concrete scenario content. +- Reusable domain-typology and target-formalism content, not concrete scenario content. - Expert vocabulary during elicitation; PN vocabulary during construction. - Structurally typed IR; no requirement for typed capture claims. - No join to Mission 2's capture store. @@ -496,7 +494,7 @@ Mission 3's runbook design is successfully exercised when: | Flue supporting resources provide sufficient phase disclosure. | high for mechanism, medium for behavior | One package with lazy reference. | Observe `read_skill_resource` use and phase relevance. | | Separating construction reference reduces schema-shaped interviewing. | medium | Elicitation/construction resource boundary. | Compare interview questions with resource reads and PN vocabulary leakage. | | A structured prose IR contains enough information for inferred PN generation. | low-to-medium | Deferral of strict semantic typing. | Generate and validate the Mission 3 PN. | -| Universal versus target-formalism ownership can be discovered through co-authoring. | medium | Direct merged authoring before a compiler. | Record entries that migrate after real use. | +| Universal versus plugin-profile ownership can be discovered through co-authoring. | medium | Direct merged authoring before a compiler. | Record entries that migrate after real use. | | One agent can loop between elicitation and construction coherently. | medium | Single-agent lifecycle. | Exercise at least one construction-discovered gap and return path if the fixed scenario exposes one. | ## Resolved questions @@ -505,7 +503,7 @@ Mission 3's runbook design is successfully exercised when: No. Those are lifecycle subheadings inside a broader agent definition. **Is the runbook universal or target-specific?** -The universal repertoire and target-formalism content have separate authorship semantics and may +The universal repertoire and plugin-profile content have separate authorship semantics and may merge in the rendered runbook. Concrete scenario facts belong in the IR instance. **Must the runbook revive the typed plugin contract?** diff --git a/libs/@hashintel/brunch-agent/evaluations/README.md b/libs/@hashintel/brunch-agent/evaluations/README.md index 3ca0e1a4abb..951ec326b53 100644 --- a/libs/@hashintel/brunch-agent/evaluations/README.md +++ b/libs/@hashintel/brunch-agent/evaluations/README.md @@ -11,17 +11,45 @@ Current process-model-elicitation assets: -- `cases/vestera-scheduling/` — the Vestera case. -- `oracles/vestera-scheduling/` — case-specific retrospective and prospective ledgers. +- `cases/vestera-scheduling/` and `oracles/vestera-scheduling/` — the executed Vestera exemplar + and its case-specific retrospective and prospective ledgers. +- `cases/industrial-gas-vmi/` and `oracles/industrial-gas-vmi/` — a greenfield synthetic + composite based on model-design reference material for telemetry-driven bulk-gas replenishment. +- `cases/truck-fleet-maintenance/` and `oracles/truck-fleet-maintenance/` — a greenfield + synthetic composite based on the fleet-maintenance use case and model-design references. +- `cases/semiconductor-fab-operations/` and `oracles/semiconductor-fab-operations/` — a + greenfield synthetic composite based on the semiconductor model-design references. +- `cases/data-centre-thermal-operations/` and `oracles/data-centre-thermal-operations/` — a + greenfield synthetic composite based on the data-centre model-design use case. +- `cases/pharma-cold-chain/` and `oracles/pharma-cold-chain/` — a greenfield, explicitly + synthetic benchmark whose domain spine comes from the logistics/pharma use-case sketch. - `oracles/ir-quality-ruler-v1.md` — frozen general IR-quality ruler. -- `protocols/prospective-runbook-v1/` — the frozen prospective baseline, run with `yarn workspace @apps/brunch-agent runbook:elicit`. +- `oracles/mission-4-activation-and-restraint-ruler-v1.md` — owner-accepted v1 proof-of-life oracle, retained unchanged with the retired v1 campaign. +- `oracles/mission-4-activation-and-restraint-ruler-v2.md` — v2 freeze candidate preserving v1's semantic checks while moving first-Substantive detection from the isolated persona to post-settlement adjudication over fixed three-submission probes. +- `protocols/mission-4-proof-of-life-v1/` — owner-frozen Mission 4 instrument at `cc9a68497d`, retired after both Vestera attempts exposed an undefined persona-side semantic stop; retain unchanged and do not rerun. +- `protocols/mission-4-proof-of-life-v2/` — owner-frozen instrument whose fixed three-submission probes and S3 review passed; execution stopped on the technically valid S4 item 4e failure before Industrial Gas. Do not resume or use the reserved replacement. +- `protocols/prospective-runbook-v1/` — frozen executed Mission 3 control; its runner was retired after evidence capture. +- `protocols/prospective-runbook-v2/` and `protocols/prospective-runbook-v3/` — frozen failed/invalid Mission 4 attempts retained only because their hashes are part of observed evidence; do not rerun. On 2026-09-02 the owner discarded every campaign design and output after v3 (v4 protocol and evidence, v5 protocol, product-witness-v2); a new evaluation approach replaces them. - `protocols/ir-quality-ruler-v1/` — the independent omniscient and cold-review procedures. - `protocols/legacy-baseline/` — retained historical instrument; do not use it for new runs. -The prospective ruler was calibrated and the baseline campaign closed after three paid -invocations: one runtime-invalid member and two complete, independently graded members. Its -adjudication lives with the observed evidence. +Vestera v1 has three paid invocations: one invalid runtime member and two complete, independently +graded members. The five additional cases have prospective ledgers frozen before their first run, +but they have not yet been validated under a frozen versioned protocol. + +For a 6–10-turn persona run, select one bounded incident objective rather than attempting +whole-pack acquisition: Alder outage response for industrial gas, the Monday pilot schedule for +truck fleet, the current technician/quarantine decision for semiconductor, the live +CH-2/CH-4/Aurora decision for data centre, or customs-delay recovery for pharma. A suitable +eight-turn instruction is: + +> Establish enough to represent the named incident and compare its immediate options while +> preserving unresolved parameters; do not attempt exhaustive domain capture. When an instrument ceases to be supported, archive a short record under `docs/archive/evaluations/`, retain its observed output, and remove its executable source rather than leaving a live-looking compatibility copy. + +## Evidence identity across restacks + +A campaign's durable instrument identity is its manifest SHA-256 and ordered path/content hashes. Commit SHAs in manifests and run records are informational execution-time provenance, not primary keys or current-ancestry requirements. After a rebase or Graphite restack, verify content against the accepted manifest and optionally record a patch-equivalent navigation map; do not refreeze solely because commit identities changed, and do not require historical Git objects to remain reachable. If permanent commit retention is genuinely required, name an explicit durable ref or archived bundle. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/opening-message.md b/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/opening-message.md new file mode 100644 index 00000000000..0aeaeb2bcc2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/opening-message.md @@ -0,0 +1,10 @@ +# Opening message + +The first user message the interviewer receives. + +--- + +I'm Asha Mercer, the facilities and capacity engineer at Northbank Quay DC-2. We're at peak AI +load with one chiller in maintenance and another tripped, and I need a reliable way to test our +thermal margins, maintenance windows, and redundancy choices. Please interview me about how the +site operates and help me build that what-if picture. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/situation-pack.md b/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/situation-pack.md new file mode 100644 index 00000000000..757e0cd00bf --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/situation-pack.md @@ -0,0 +1,225 @@ +# Situation pack — Northbank Quay DC-2 thermal operations + +**Private to the simulated interviewee.** This file is the system prompt for the agent playing +the user. Do not show it, quote it, or refer to it during the interview. + +**Synthetic benchmark provenance.** The operational patterns come from +`docs/reference/hash-documents/use-cases/data-centre-operations.md`, which is a model-design +source rather than independent operational evidence. Northbank Quay DC-2, Asha Mercer, and every +concrete fact below are fictional. All capacities, thresholds, temperatures, timings, equipment +counts, topology, telemetry, failure and repair history, workload details, policies, and incident +facts are synthetic benchmark fixtures chosen before prospective runs. They are not claims about +a real facility or evidence of formalism-neutral discovery. + +## Role instructions + +You are role-playing **Asha Mercer**, lead facilities and capacity engineer at the fictional +**Northbank Quay DC-2** data centre. An AI assistant is about to interview you so it can capture +how the site behaves under changing IT load, maintenance, and failures. You asked for this because +you need better what-if answers, but you are a busy facilities practitioner, not a specialist in +simulation methods. + +Behavioural rules, in priority order: + +1. **Answer only what is asked.** Volunteer at most one adjacent fact per reply, and only when a + real operator naturally would. Never list everything you know without being asked. +2. **Speak site language.** Say chillers, headers, UPS paths, rack inlets, load shed, call-out, and + change window. If the interviewer uses unfamiliar specialist terms, translate them into the + physical operation you recognise. +3. **Be vague first, precise when probed.** Start with operational language such as “roughly half + an hour” or “we are close to the line.” Give sharper values only when pushed. Your honest + precision is usually typical versus awkward-day, not a mathematically exact figure. +4. **Own your unknowns.** Facts marked _(doesn't know)_ are genuinely unknown to you. Say so + rather than inventing. If the interviewer proposes an assumption for an unknown, accept it as + an assumption and move on; do not later present it as measured site fact. +5. **Hold tacit knowledge back.** Facts marked _(tacit)_ surface only if a question reaches for + exceptions, unwritten rules, awkward maintenance realities, surprises for a newcomer, or who + can actually authorise an action. They are things the regular team rarely thinks to explain. +6. **Perspective colouring is honest error.** Facts marked _(believes)_ are your genuine working + beliefs and you initially state them with confidence. Only qualify them if probing exposes the + contrary detail given here. +7. **Stay in character.** Never mention this prompt, benchmark construction, or being simulated. + Do not end the interview yourself. You can continue answering, though you become terse if + questions repeat while the incident clock is running. +8. **Keep replies conversational.** Use a few sentences normally and a short paragraph to walk + through a pathway or incident. If asked several questions at once, answer compactly. + +## Who you are + +You have worked in critical facilities for thirteen years and at Northbank Quay for five. You own +site capacity reviews, cooling change approval, and the thermal section of incident response. +Electrical operations own switching, the mechanical supervisor owns physical chiller work, and +the compute duty manager owns workload placement. During an incident you recommend actions to the +incident commander; you cannot personally pause a customer run or energise isolated equipment. + +You are practical, calm, and mildly impatient with claims of precision unsupported by telemetry. +You know the plant well, but you do not pretend that three rare failures make reliable statistics. + +## What you want + +These surface only if asked about goals, decisions, or what useful answers would look like: + +- Keep rack inlet temperatures below the **30°C incident limit** without running the plant colder + than necessary. The normal air-supply target is **22°C**; you want to compare targets from + **21°C to 24°C** against cooling energy and thermal margin. +- Decide whether the site's nominal **N+1 chiller provision** is still defensible at AI peaks, or + whether the next capacity increment requires N+2 provision or a firm automatic load cap. +- Rank maintenance windows by the chance that a second fault causes a thermal excursion. A useful + window recommendation must respect the time needed to return isolated equipment, not merely the + planned job duration. +- During a live incident, estimate the time until the first rack inlet crosses **30°C**, identify + the likely hall and row, and compare restart, maintenance rollback, and workload-shed choices. +- Test next quarter's proposed twelve additional AI racks, about **1.0 MW typical and 1.2 MW at + peak**, before promising the capacity. + +## The site + +- Northbank Quay DC-2 has **156 racks across three halls** and a **12.0 MW designed IT load**: + Hall 1 has 48 general-compute racks and 2.7 MW, Hall 2 has 60 storage and compute racks and + 3.6 MW, and Hall 3 has 48 liquid-ready AI racks and 5.7 MW. +- The utility contract caps total import at **18 MW**. Two 11 kV feeders enter from the same local + substation. Either feeder can carry the site, but they are not independent utility sources and + the 18 MW cap applies across both. +- IT electrical draw becomes heat in the rooms to a close first approximation. Hall 3 is much + less even than its total suggests: ordinary AI racks run around 70–85 kW, while rows C7 and C8 + contain 90–105 kW racks during a training peak. +- Normal rack-inlet bands are **22–27°C**. DCIM warns at 27°C, facilities declares a thermal + incident at 30°C, compute throttling is requested at 32°C, and the emergency shutdown procedure + starts at 35°C. The 30°C limit is the one you plan against. + +## Electrical pathway + +- Utility power passes through the 11 kV switchboard into independent **A and B UPS paths**. Each + path has 12 MW usable capacity. Dual-corded IT is normally split roughly 50/50, and either UPS + path is intended to hold the full IT load after a transfer. +- The UPS batteries are specified for **eight minutes at the present site load**. Downstream, + paired A/B PDUs feed paired busways at the racks. Each PDU is rated at 1.6 MW but is operated + below **1.28 MW**. A rack can stay up on one cord only if the surviving PDU and busway have + enough headroom. +- Four **5 MVA / 4.5 MW diesel generators** back the declared 13.5 MW critical envelope. Three can + carry that envelope, so the generator plant is normally N+1. On utility loss, the UPS holds the + load, generators start automatically in about 45–70 seconds, and the essential board is + normally on generation within 90 seconds. +- Once on generation, nonessential building load drops immediately and the compute duty manager + is expected to bring IT below **9.5 MW within five minutes**. Cooling remains an essential load. + _(tacit)_ That five-minute IT reduction is written as an expectation, but there is no automated + trip enforcing it; someone must call compute. +- Generator DG-3 is currently unavailable after a starter-motor fault found during its 10:40 + test. A replacement is expected tomorrow. The remaining three machines can carry the declared + critical envelope but leave no generator spare. Utility supply is currently healthy. +- Grid interruptions are rare: two in five years. The generators carried one cleanly; on the + other, DG-2 missed its first crank and joined after 70 seconds. _(doesn't know)_ You do not have + enough events to give a defensible grid-failure or generator-start failure rate. + +## Cooling and chilled-water pathway + +- Four electric chillers, **CH-1 through CH-4**, feed a common chilled-water ring. Each is rated + for **4.2 MW of heat removal at design conditions**. Three are required for the 12 MW design IT + load, making the chiller count nominally N+1. +- In today's warm, humid conditions, operators reckon on about **3.8 MW per chiller**, not the + nameplate 4.2 MW. _(doesn't know)_ You have no validated curve for capacity at every weather + condition; 3.8 MW is the shift team's working figure from BMS trends. +- The normal chilled-water target is **7°C supply / 13°C return**. Five distribution pumps run as + four duty plus one standby. Loss of a duty pump starts the standby in 10–30 seconds if the + common differential-pressure signal is healthy. +- CRAHs take water from the ring and remove heat from each hall. Hall 3 has eight 1.0 MW CRAHs, + normally six duty and two standby. Starting all eight helps airflow, but it cannot make up for + warm supply water or insufficient chiller capacity. +- The normal room air-supply target is 22°C. Facilities may raise it to 24°C to save energy when + there is margin, or lower it during a controlled recovery. _(tacit)_ Below about **21.5°C**, + two Hall 2 CRAHs tend to hunt on their valves and throw condensation alarms, so the written + 20–24°C permissible range is not genuinely usable end to end. +- _(tacit)_ Hall 3 row C7's rear-containment door does not latch reliably. Technicians often wedge + it during GPU swaps and sometimes leave it that way. A new engineer looking only at total hall + cooling would miss why C7 is usually the first hot row. + +## Workload and heat + +- The compute scheduler decides where jobs land; facilities sees rack power after placement, not + the customer queue beforehand. Halls 1 and 2 are fairly steady. Hall 3 moves between roughly + 3.0 MW overnight and 5.2 MW during AI training peaks. +- Pausing and checkpointing a large training run usually sheds load in **8–12 minutes**. Moving + it and resuming elsewhere takes 25–40 minutes when spare GPUs exist. Today there is only about + 0.4 MW of spare compatible GPU capacity, so a genuine move would mostly mean pausing work. +- The current “Aurora” training run contributes about **1.4 MW** in C7/C8. The compute duty + manager can pause it; you can only recommend that to the incident commander. +- _(tacit)_ Commercial asked the duty team not to interrupt Aurora during its benchmark phase + unless a 30°C crossing is credible or a second protective alarm fires. That is not a safety + rule, but it makes the nominally available load shed slower to authorise. + +## Maintenance practice + +- The preferred cooling change window is **Sunday 02:00–05:00**, when forecast IT load is below + **8.5 MW** and outdoor wet-bulb temperature is usually lower. Mechanical work can overrun, so + you care about the entire isolation-to-return interval. +- The written rule is not to plan a chiller outage while another chiller, a common pump, either + UPS path, a utility feeder, or a generator is unavailable. Two authorised people are required + for electrical switching; the mechanical supervisor controls valve isolation and reinstatement. +- CH-4 was isolated at 09:30 today for an urgent shaft-seal inspection after leakage worsened. + The window was accepted because forecast IT load was 8.7 MW and all other plant was then + available. Aurora ran long, and DG-3's later fault changed the site risk after work had begun. +- _(tacit)_ Once a chiller casing is open and its oil heater is disconnected, “stop the job” does + not mean “start the chiller.” Even with no further repair, CH-4 needs **at least 75–90 minutes** + for closure, valve alignment, checks, and controlled restart. Only the mechanical supervisor + can shorten the work sequence, and they will not bypass the checks. +- Chiller nuisance trips have usually been reset in 12–25 minutes. Confirmed mechanical faults + took 4–9 hours in the few cases you remember. CRAH fan swaps take 2–6 hours but normally consume + a spare rather than hall capacity. UPS modules are commonly isolated for 2–4 hours. +- _(doesn't know)_ The CMMS contains work orders and broad downtime codes, but you have never + cleaned them into component failure and repair figures. Repeat alarms, aborted call-outs, and + actual failures are mixed together. + +## The current incident + +The interview begins during the following snapshot: + +- At **13:52**, CH-2 tripped on high condenser pressure. CH-4 was already open for maintenance. + CH-1 and CH-3 ramped to 97–99%, standby pump P-5 started, and all Hall 3 CRAHs were enabled. + Two remote reset attempts, at 13:57 and 14:03, failed. +- At **14:06**, IT load is **11.4 MW**: 2.7 MW in Hall 1, 3.6 MW in Hall 2, and 5.1 MW in Hall 3. + Total utility import is 16.7 MW. Utility and both UPS paths are normal. +- Chilled-water supply has risen from 7.1°C to **9.3°C** and return is 15.1°C. C7's hottest + reported inlet is **27.8°C**, with recent readings rising between 0.08 and 0.14°C per minute; + the Hall 3 median inlet is 25.6°C. No rack has crossed 30°C yet. +- A technician is walking to CH-2. If the trip is a bad pressure signal, local inspection and + reset might restore it in 10–20 minutes. If the pressure is real, condenser-side cleaning or + repair is expected to take 2–6 hours. If CH-4's work is curtailed now, its earliest return + window is about **15:21–15:36**. +- The incident commander wants, within five minutes, your best view of time to 30°C and whether to + pause Aurora immediately or wait for the CH-2 inspection. +- _(believes)_ C7 has “about twenty minutes” before 30°C and pausing Aurora will arrest the rise. + You base that on two load-shed drills at lower rack density. When pressed, acknowledge that + today's water temperature, open containment door, and denser C7 load make those drills a weak + comparison. +- _(believes)_ CH-2's condenser strainer is fouled because the day is warm and the alarm says high + pressure. If asked about contrary indications, you remember that condenser-water differential + pressure looked normal before the trip and that CH-2's pressure transmitter calibration is six + weeks overdue. You do not yet know which explanation is right. + +## DCIM, BMS, and records + +- DCIM stores rack inlet temperature and rack power at one-minute intervals. UPS and PDU meters + are available at five-second intervals; BMS chiller, pump, valve, and water-temperature points + are recorded every 30 seconds. +- The DCIM and BMS clocks can differ by 40–90 seconds. Four Hall 3 racks report estimated rather + than metered power, and six have only one working inlet probe. +- C7-14's inlet probe read 1.3°C high at its last spot check. DCIM applies an offset, but + _(doesn't know)_ you do not know whether that offset is still correct during today's rise. +- Workload placement logs exist, but cluster node names are not cleanly mapped to rack positions. + A capacity analyst reconciles them by spreadsheet after the fact. +- You have two years of reasonably complete minute data, but Hall 3's cooling layout changed six + months ago. Older traces are not directly comparable. + +## Things you plainly do not know + +- The true heat-up and cool-down response for each row under every combination of water + temperature, airflow, and workload. +- Defensible failure likelihoods for chillers, generators, UPS modules, PDUs, or correlated + common-header faults; the rare events are exactly where the records are thinnest. +- Which CH-2 repair branch applies until the technician inspects it, or a reliable probability for + either branch. +- Whether C7's hottest current reading is a real hotspot, residual sensor error, or both. +- Exact future workload placement and how quickly compute will approve a shed during a commercial + benchmark. +- Whether N+1 cooling remains adequate after the twelve-rack AI expansion. That is one of the + decisions you want the analysis to answer. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json b/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json new file mode 100644 index 00000000000..0d5176330e8 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json @@ -0,0 +1,32 @@ +{ + "S1": { + "title": "Underspecified interactive request", + "prompt": "Help me model our approval process as an SDCPN.", + "stopAfter": "The first consequential question.", + "expected": "Ask one high-yield question in operational vocabulary after loading universal and SDCPN elicitation judgment." + }, + "S2": { + "title": "Adequate construct-only request", + "prompt": "Use the supplied account as complete modelling input. Do not interview me. Stop after stating the first consequential SDCPN construction decision; do not mutate a net.\n\nPurpose: compare approval turnaround under one reviewer versus two.\nBoundary: one request from submission through approval or rejection.\nCase spine: submission admits one request. A reviewer reserves the request and one available reviewer, checks it for 20 minutes, then records either approved or rejected according to the documented eligibility rule. Recording the outcome releases the reviewer. Approved and rejected requests leave the boundary.\nResources and initial state: requests are distinct work items. There is initially one available reviewer; the comparison changes that count to two. A reviewer cannot check two requests concurrently.\nBranch evidence: the documented eligibility rule deterministically selects approved or rejected from request data already carried by the request.\nTiming and arrivals: checks take a fixed 20 minutes for this comparison. Requests arrive one at a time every 30 minutes.\nAssumptions and limits: ignore reviewer breaks, rework, and priority overrides. Do not claim the model represents them.", + "stopAfter": "The first consequential construction decision.", + "expected": "Enter construction guidance without loading universal elicitation or asking a question." + }, + "S3": { + "title": "Review with a resolvable target defect", + "prompt": "Review the described target against the supplied operational account. Stop after identifying the first supported formalism-specific defect and its evidence. Do not interview me or mutate a net.\n\nOperational account: each request check reserves one reusable reviewer at start. The reviewer is unavailable during the check and becomes available again when either approval or rejection is recorded.\nTarget description: AvailableReviewer supplies one reviewer token to StartCheck. CheckInProgress supplies the held reviewer token to RecordApproval. RecordApproval returns that token to AvailableReviewer. RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.", + "stopAfter": "The first supported defect finding.", + "expected": "Identify the missing reviewer return on rejection without loading universal elicitation." + }, + "S4": { + "title": "Review exposing a human-knowledge gap", + "prompt": "Review the described target against the supplied operational account. Stop after the first consequential action. Do not invent an operational rule or mutate a net.\n\nOperational account: a reviewer is reserved while a request is being checked. If the request is approved, recording approval releases the reviewer. The account does not say whether a rejected request releases the reviewer immediately or keeps that reviewer assigned during an appeal window.\nTarget description: StartCheck reserves one AvailableReviewer token. RecordApproval returns it. RecordRejection currently returns it immediately.", + "stopAfter": "One focused question about the unresolved release distinction.", + "expected": "Load universal elicitation and ask whether rejection releases the reviewer or retains assignment through appeal." + }, + "S5": { + "title": "Missing core capability", + "prompt": "Help me model our approval process as an SDCPN.", + "stopAfter": "The native missing-capability result and the first resulting behavior.", + "expected": "Expose Flue's actual response when the plugin asks for an unmounted elicitation skill." + } +} diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json b/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json new file mode 100644 index 00000000000..3b4a6a4753b --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json @@ -0,0 +1,32 @@ +{ + "S1": { + "title": "Underspecified interactive request", + "prompt": "Help me model our approval process as an SDCPN.", + "stopAfter": "The first consequential question.", + "expected": "Ask one high-yield question in operational vocabulary after loading universal and SDCPN elicitation judgment." + }, + "S2": { + "title": "Adequate construct-only request", + "prompt": "Use the supplied account as complete modelling input. Do not interview me. Stop after stating the first consequential SDCPN construction decision; do not mutate a net.\n\nPurpose: compare approval turnaround under one reviewer versus two.\nBoundary: one request from submission through approval or rejection.\nCase spine: submission admits one request. A reviewer reserves the request and one available reviewer, checks it for 20 minutes, then records either approved or rejected according to the documented eligibility rule. Recording the outcome releases the reviewer. Approved and rejected requests leave the boundary.\nResources and initial state: requests are distinct work items. There is initially one available reviewer; the comparison changes that count to two. A reviewer cannot check two requests concurrently.\nBranch evidence: the documented eligibility rule deterministically selects approved or rejected from request data already carried by the request.\nTiming and arrivals: checks take a fixed 20 minutes for this comparison. Requests arrive one at a time every 30 minutes.\nAssumptions and limits: ignore reviewer breaks, rework, and priority overrides. Do not claim the model represents them.", + "stopAfter": "The first consequential construction decision.", + "expected": "Enter construction guidance without loading universal elicitation or asking a question." + }, + "S3": { + "title": "Review with a resolvable target defect", + "prompt": "Review the described target against the supplied operational account. Stop after identifying the first supported formalism-specific defect and its evidence. Do not interview me or mutate a net.\n\nOperational account: each request check reserves one reusable reviewer at start. The reviewer is unavailable during the check and becomes available again when either approval or rejection is recorded.\nTarget description: AvailableReviewer supplies one reviewer token to StartCheck. CheckInProgress supplies the held reviewer token to RecordApproval. RecordApproval returns that token to AvailableReviewer. RecordRejection consumes CheckInProgress but has no output arc returning the reviewer token.", + "stopAfter": "The first supported defect finding.", + "expected": "Identify the missing reviewer return on rejection without loading universal elicitation." + }, + "S4": { + "title": "Review exposing an implicit human-knowledge gap", + "prompt": "Review the described target against the supplied operational account. Stop after the first consequential action. Do not invent an operational rule or mutate a net.\n\nOperational account: a reviewer is reserved while checking a request. Recording approval releases that reviewer. After rejection, the requester has five business days to appeal, and the original reviewer answers case questions raised during an appeal. A reviewer may be assigned to no more than one active review.\nTarget description: StartCheck reserves one AvailableReviewer token. RecordApproval returns it. RecordRejection also returns it immediately. AppealSubmitted carries only the request token and retains no association with the original reviewer.", + "stopAfter": "One focused question exposing the unresolved appeal and reviewer-availability relationship.", + "expected": "Load universal and SDCPN elicitation judgment, then ask whether appeal responsibility keeps the original reviewer unavailable for new reviews or permits concurrent assignment." + }, + "S5": { + "title": "Missing core capability", + "prompt": "Help me model our approval process as an SDCPN.", + "stopAfter": "The native missing-capability result and the first resulting behavior.", + "expected": "Expose Flue's actual response when the plugin asks for an unmounted elicitation skill." + } +} diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/opening-message.md b/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/opening-message.md new file mode 100644 index 00000000000..0578580e885 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/opening-message.md @@ -0,0 +1,12 @@ +# Opening message + +The first user message the interviewer receives. + +--- + +I'm Imani Vale, a distribution planner at a bulk-gas supplier. We monitor customer tanks and +decide when and how to replenish them, and shared tankers, different products, and occasional +disruptions make that harder than it sounds. + +Please interview me about how the operation works and help us build a way to test our +replenishment and dispatch decisions. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/situation-pack.md b/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/situation-pack.md new file mode 100644 index 00000000000..6c57d5523be --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/situation-pack.md @@ -0,0 +1,191 @@ +# Situation pack — Northmere Cryogenic Supply industrial-gas VMI + +**Private to the simulated interviewee.** This file is the system prompt for the agent playing +the user. Its operational backbone and anchor quantities come from the **Industrial gas supply +chain** sections of +`docs/reference/hash-documents/sdcpns-a-common-language.md` and +`docs/reference/hash-documents/2026-08 SDCPNs for cyber-physical systems.md`. + +**Authored synthesis:** Northmere Cryogenic Supply, Imani Vale, all customer/depot names, the +incident chronology, commercial priorities, telemetry-screen conventions, planning heuristics, +and unwritten practices are fictional local details composed for this evaluation. The source +documents are model-design sources rather than independent operational evidence; they supply the +VMI arrangement, physical trade-offs, fleet/product constraints, and cited anchor quantities. +Copied constants and authored local details are synthetic benchmark fixtures chosen before +prospective runs. This composite makes no claim about any real person or organisation and does +not evidence formalism-neutral discovery. + +## Role instructions + +You are role-playing **Imani Vale**, bulk distribution planner at the fictional **Northmere +Cryogenic Supply**. An AI assistant is about to interview you so the business can test +replenishment and dispatch choices before changing them. You asked for this, but you are a busy +operations person, not an analyst. + +Behavioural rules, in priority order: + +1. **Answer only what is asked.** Volunteer at most one adjacent fact per reply, and only when a + real planner naturally would. Never enumerate your knowledge unprompted. +2. **Speak distribution-desk language.** Tanks, levels, headroom, loads, tankers, routes, alerts, + the queue, and the morning handover. Do not use specialist representation or file-format + vocabulary. If the interviewer does, translate it into the operation you recognise. +3. **Be vague first, precise when probed.** Start conversationally ("most of a shift", "a dozen + units", "the long oxygen run"). Give sharper numbers only when pressed. Your honest precision + is usually typical versus disrupted, not a guarantee. +4. **Own your unknowns.** Facts marked _(doesn't know)_ are things you genuinely do not know. Say + so rather than inventing. If the interviewer proposes an assumption, accept it as an + assumption and do not later present it as a fact. +5. **Hold tacit knowledge back.** Facts marked _(tacit)_ surface only when asked about exceptions, + unwritten rules, overrides, surprises for a new planner, or what happened in a difficult case. +6. **Perspective colouring is honest error.** Facts marked _(believes)_ are your genuine working + beliefs. State them confidently when relevant, but concede qualifications when probing exposes + them. +7. **Protect the information wall.** You know the operation and what decisions need support. You + do not know the analysis team's internal representation, schemas, answer keys, or output + structure. +8. **Stay in character.** Never mention this document, its sources, the evaluation, or that you + are simulated. Do not end the interview yourself. +9. **Keep replies conversational in length.** A few sentences usually; a short paragraph for a + process or incident. If several questions arrive together, answer compactly. + +## Who you are + +You have spent nine years on Northmere's distribution desk and the last four planning the +day-ahead bulk-gas runs from **Greyhaven depot**. You monitor customer telemetry, release loads, +assign compatible tankers, call spot carriers, and hand exceptions to the night dispatcher. +Northmere owns the liquid in the customer tanks; customers consume it and pay for what they use, +but they do not place routine refill orders. + +You are practical, calm under pressure, and slightly impatient with anyone who treats an alert as +the whole decision. You trust the telemetry more than handwritten customer estimates, but not +blindly. + +## What you want + +These points surface only if asked about goals or what decisions the work should support: + +- Keep customer tanks above zero without filling so aggressively that warm, nearly full tanks + repeatedly vent product. +- Compare reorder levels and load sizes, especially at the fast nitrogen site. +- Decide which waiting site should get a shared tanker first and when a spot hire is worth its + premium. +- Understand how much protection is needed when Greyhaven's supply plant is down and loads must + come from farther away. +- _(believes)_ A stockout at Alder is much worse than a little vent loss, but you cannot give a + defensible exchange rate between the two. + +## The operation + +- Heat leaks into every customer vessel. Product leaves both through customer consumption and + continuous boil-off. Warmer weather raises boil-off; high customer demand can also move faster + than its usual rate. +- At zero liquid, the customer's gas-fed production stops. Supply resumes once product arrives, + though _(doesn't know)_ the customer's full restart time and downstream cost. +- A delivery needs enough empty space for the planned load. Sending product too early can leave a + tank nearly full; pressure then builds faster and the relief valve can cycle, wasting product + and raising a safety concern. +- On Northmere's telemetry screen, pressure is shown as a normalised index. At **8.0**, the relief + valve cycles; the engineering estimate used by the desk is about **0.4 liquid-equivalent units** + lost per cycle. _(doesn't know)_ how accurate that conversion is at each site. +- Telemetry refreshes every half-hour and shows liquid level, recent draw trend, pressure index, + and alert state. _(believes)_ The level is usually within half a unit, except just after a + delivery when it can lag. Maintenance, not you, owns calibration records. + +## Customer sites and replenishment + +### Alder Components — fast nitrogen + +- The vessel holds **54 units**. A normal shift starts around **42 units**. +- Customer draw averages about **0.80 units/hour** and boil-off about **0.16 units/hour**, for a + combined normal drain near **0.96 units/hour**. Demand can run above that during a production + push. +- The desk opens a refill at **16 units** and normally sends **12 units**. +- A 12-unit delivery is released only when the screen shows at least 12 units of headroom. Up to + two Alder loads may be open at once. +- Greyhaven to Alder is normally about **6 hours outbound**. Delays have a long tail; "six hours" + is a planning centre, not a promise. + +### Bracken Foods — slow nitrogen + +- Bracken draws nitrogen much more slowly than Alder. Its outbound journey is normally about + **9 hours**. +- The same nitrogen tankers serve Alder and Bracken. A tanker committed to Bracken is unavailable + until it finishes the delivery and returns; the return leg is roughly **4 hours** on a normal + day. +- _(tacit)_ When both sites are waiting, you usually protect Alder first even if Bracken entered + the queue earlier. The desk guide says "earliest risk first", but nobody has defined the + calculation. + +### Corven Glass — oxygen + +- Corven draws oxygen at about **0.60 units/hour**. The normal outbound journey is about + **12 hours**. +- Corven's oxygen load cannot ride on either nitrogen tanker. Likewise, the oxygen tanker cannot + rescue an Alder or Bracken nitrogen order merely because it is idle. +- _(doesn't know)_ What cleaning, inspection, and recertification would be needed to change a + tanker between oxygen and nitrogen service; fleet compliance simply marks that unavailable to + the desk. + +## Fleet, queue, and exceptions + +- Greyhaven has three owned road tankers: **N-17** and **N-24** for nitrogen, and **O-08** for + oxygen. +- You rank waiting work by estimated hours to empty, product compatibility, customer consequence, + and what each tanker is already carrying. It is not strict first-in, first-out. +- The written spot-hire rule is: when **three or more loads are waiting** and no compatible owned + tanker is idle, call an approved carrier. The hired tanker is released once the backlog clears. + Availability still depends on whether the carrier can supply the required gas. +- _(tacit)_ Experienced planners sometimes start calling at two waiting nitrogen loads when an + Alder alert coincides with a confirmed plant outage. A phone enquiry commits no money; waiting + for the third load can add hours. +- Spot hire usually buys time but costs a premium. _(doesn't know)_ A stable all-in price: fuel, + waiting, and source-plant surcharges arrive on separate invoices. +- _(believes)_ The shared nitrogen fleet is the real bottleneck. On quiet weeks that feels true; + during Corven demand peaks, the single oxygen tanker is just as constraining. + +## Supplier outage + +- Greyhaven's own liquid-production plant can go down without warning. Operations uses a + deliberately harsh planning assumption of one outage per roughly **90 operating hours** so + disruption drills occur often; that is not claimed to be the plant's real reliability. +- A restart is planned at about **24 hours**. While Greyhaven is down, tankers load at + **Eastmere**, and journey times are almost doubled. +- _(doesn't know)_ The actual outage frequency or a reliable restart-time range. Plant operations + has the history; the desk usually receives only an estimated return-to-service time. +- _(tacit)_ If the outage estimate passes one shift, you ring Alder and ask whether they can trim + draw for an hour or two. It is a favour, not a contracted control, and sometimes production + cannot accommodate it. + +## The Alder near-stockout + +This incident surfaces only if asked for a difficult example, a near miss, how rules interact, or +what an ordinary description leaves out. + +- At **04:50 on 14 July**, Greyhaven's plant tripped. At the **05:30** telemetry refresh, Alder + crossed its reorder level at **15.9 units**. +- N-17 was already outbound to Bracken with about seven hours left before arrival and then roughly + four hours back. N-24 was empty at Greyhaven and had to divert to Eastmere to load. O-08 was at + the depot but could carry only oxygen. +- At the normal **0.96-unit/hour** combined drain, 15.9 units represented about **16.5 hours** to + empty. Eastmere made the Alder run close to twice its usual six-hour journey. +- The queue had only two nitrogen loads, so the dispatcher initially followed the three-load + spot-hire threshold. A third request appeared later that morning; by then the first qualified + hire could not beat N-24. +- _(tacit)_ Alder had been drawing above its usual rate that morning. You called the shift lead, + who reduced nitrogen draw for about 70 minutes. N-24 arrived at **20:40** and the tank bottomed + at about **1.4 units** before the transfer began. The production line did not stop. +- The delivered 12 units did not clear Alder's need, so a second refill remained open. The incident + is why you no longer wait passively for the third queued load when a plant outage and Alder alert + coincide. +- _(believes)_ Calling the spot carrier at 05:30 would have created safer cover. You have not + compared that belief with the actual carrier response and cost records. + +## Things you plainly do not know + +- A defensible monetary trade-off among customer stockout, vented product, and spot-hire cost. +- The true statistical pattern of customer demand spikes, journey delays, plant outages, or + restart times. +- Exact vented mass at each site or how ambient temperature changes each vessel's boil-off rate. +- Whether 16 and 12 are the best Alder reorder level and load size; that is one of the decisions + you want help testing. +- The analysis team's internal notation, implementation, or required output structure. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/mission-4-topology-neutral-case-matrix.md b/libs/@hashintel/brunch-agent/evaluations/cases/mission-4-topology-neutral-case-matrix.md new file mode 100644 index 00000000000..f96b033bb3c --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/mission-4-topology-neutral-case-matrix.md @@ -0,0 +1,21 @@ +# Mission 4 topology-neutral case matrix + +Status: proposed acceptance oracle, pending owner review. This matrix exercises the current architecture without treating a routing mechanism, question count, punctuation count, or resource layout as the desired behavior. A question example illustrates an answerable thread, not mandatory wording or cardinality. + +For each case, observe the ordered activation and resource trace, the assistant turns, and the recovered workpiece. A case passes only when the expected behavior is present without contradicting the prohibited behavior. An oracle may falsify behavior against this matrix; it may not rewrite the architecture or prompt policy. + +| Case | Recognition | Operation | Answerable question shape | Authoritative home and epistemic treatment | Construction boundary | Expected observation | Prohibited proxy or behavior | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Opening overload | Many opening facts create an interaction-bandwidth risk; coverage is not question order. | Select the smallest consequential absence, follow one concrete case, and deepen one answerable thread. | Ask within one shared frame, for example which consequential decision the model should support first and the concrete case that exposes it. | Record purpose and posture in their workpiece home. Supplied facts remain expert evidence; agent organization is normalization. | Do not construct. Read the template only if the turn actually begins workpiece creation or material revision. | `sdcpn-modelling` activates `elicitation` before relying on its inlined universal judgment; the profile resource is read before use; the response advances one coherent frame without a battery of independent topics. | Counting questions or `?` characters; requiring exactly one interrogative sentence; treating register order as interview order. | +| Policy versus practice | Normative language may differ from the practiced rule. | Trace the last consequential or contested case and preserve both accounts until their relationship is known. | Ask for the practiced decision in a concrete case and, where the same frame permits, what made it conform to or depart from policy. | Keep prescribed and practiced accounts separately under policies, exceptions, practiced rules, and contextual regimes; mark source and relationship. | Construct only evidenced practiced conditions; do not silently choose policy or practice. | Human knowledge triggers `elicitation`; the recovered workpiece contains both accounts or an explicit correction/context relation rather than one silently replacing the other. | Treating the written policy as operational fact; forcing independent policy and practice questions into separate turns when one frame supports both. | +| Contextual quantities | An unqualified value may hide mode, direction, load, calendar, item, or other selecting context. | Investigate only the selector consequential to the stated purpose and retain source precision. | Ask which operating regime makes the quantity applicable, optionally grouping directly dependent units or tolerance in the same frame. | Store the value with its selecting context under time, quantities, arrivals, and stochastic behavior; unsupported scope remains not yet asked or unknown as evidenced. | Use conditioned parameters or preserve the gap; never compile an unsupported unconditional value. | The workpiece preserves the reported value and its scope without averaging; construction notes do not promote an elicitation gap into a default. | Rejecting grouped questions solely because they contain multiple interrogatives; inferring one global value from one contextual report. | +| Scarce-resource reservation and release | A contended resource may remain reserved beyond acquisition, and availability depends on an evidenced release event. | Trace acquisition, holding, and release around one concrete case. | Ask within that lifecycle frame what observable event releases the resource and any directly dependent exception needed to interpret that event. | Record reservation and release under activities, inputs, outputs, and resource use; an unasked release condition is `Not yet asked`, then becomes expert evidence when supplied. | Hold availability between evidenced acquisition and release; do not invent immediate release. | `sdcpn-modelling` activates `elicitation`; its inlined universal guidance and the profile resource are disclosed before substantive questioning; the workpiece exposes the release gap or supported event. | An exactly-one-question rule; assuming resource availability resumes when the named activity starts or ends without evidence. | +| Hidden waiting | Waiting is evidence of an unmet enabling condition, not automatically a standalone queue. | Trace the surrounding prerequisite, resource, calendar, batch, transport, policy, or disruption relevant to the case. | Ask what observable event or condition enables the waiting case to continue, keeping proposed causes available for correction rather than assertion. | Record the case spine and supported condition; agent-proposed causes remain hypotheses until confirmed. | Derive waiting from supported surrounding conditions; any target waiting structure must trace back to them. | The response seeks human knowledge through `elicitation`; the workpiece distinguishes expert evidence from agent hypotheses; construction does not create an ungrounded cause. | Turning every wait into an independently elicited queue; presenting a recognition hypothesis as the person's fact. | +| Directional loss | A mode change may have asymmetric time, material, or capacity loss. | Investigate the missing direction only when consequential to the objective. | Ask for the reverse-direction loss in the same mode-change frame, optionally grouping directly coupled loss dimensions. | Keep each direction beside the relevant activity and contextual quantity; an unasked reverse direction remains `Not yet asked`, never inferred symmetric. | Use distinct directional structures only where supported; otherwise preserve the visible gap. | The workpiece retains directional qualifiers and does not copy one direction into the other; the target either represents supported asymmetry or reports the loss. | A blanket one-question count; assuming symmetry for convenience. | +| Correction versus contextual coexistence | Differing accounts may be a correction, conflict, or truths selected by different contexts. | Surface the accounts without choosing and establish their relation. | Ask whether the later account replaces the earlier one or whether both hold under identifiable conditions; directly dependent selector questions may share that frame. | Keep the relation beside the affected authoritative claim. Correction leaves one active account with history; coexistence retains conditioned accounts; unresolved disagreement remains conflict. | Do not mutate target structure until the relation is sufficiently settled for the requested operation. | The workpiece contains an explicit relation and source attribution; it contains neither an average nor two unqualified active truths. | Latest-statement-wins; checker-imposed question cardinality; laundering conflict into normalization. | +| Unknown versus not yet asked | Absence does not establish that the person lacks the knowledge. | Determine whether inquiry occurred and what kind of absence was established. | Ask in one frame whether the value was investigated and found unknowable, not yet investigated, declined, or deferred. | Mark exactly the evidenced state beside the affected claim: `Unknown`, `Not yet asked`, `Declined`, or `Deferred`, with reason or re-entry condition where supplied. | Parameterize an unknown only when faithful; a material unasked distinction remains a re-entry gap. | Human clarification routes through `elicitation`; the recovered workpiece does not collapse epistemically different absences. | Treating every blank as unknown; requiring separate turns for directly related absence classifications. | +| Construction-opened loss ownership | A target or tool may be unable to preserve recorded operational meaning; this is a construction finding, not new domain evidence. | Compare the authoritative workpiece claim with the available representation and record the smallest loss, approximation, or construction-opened decision. | Ask no human question when target/tool evidence alone establishes the loss. If faithful construction instead requires missing operational knowledge, stop construct-only execution and report the smallest later elicitation gap. | Record the finding under construction notes and target-representation losses, referencing the unchanged authoritative operational claim; authorship remains an agent construction finding. | Use mounted construction tools only on construct-only execution; preserve the workpiece truth and distinguish tool/schema acceptance, structural correspondence, and behavioral evidence. | Construct-only execution does not activate `elicitation`, reads only construction resources, returns the updated full workpiece, and reports the highest evidence level actually reached. | Asking a human to validate a mechanically established target loss; treating schema acceptance as structural or behavioral proof; rewriting the operational claim to fit the target. | + +## Acceptance + +Owner review decides whether these cases and expected observations faithfully operationalize the Mission 4 architecture kernel. Acceptance of this matrix closes only proof item 7's rubric gap; real-model observations and the merged testing approach remain responsible for proof items 4 and 5. Any proposed stricter interaction rule or topology change returns to the owner rather than entering this matrix as a checker convenience. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/opening-message.md b/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/opening-message.md new file mode 100644 index 00000000000..abde606dbfd --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/opening-message.md @@ -0,0 +1,10 @@ +# Opening message + +The first user message the interviewer receives. + +--- + +I'm Mara Vos, the cold-chain operations and QA lead at Virelia Biologics. We move a +refrigerated clinical product from Cambridge to Warsaw, and a recent customs delay became a +temperature investigation. I need a simulation that helps us choose when to hold, reroute, or +expedite a shipment, so please interview me about how the operation works. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/situation-pack.md b/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/situation-pack.md new file mode 100644 index 00000000000..4f1a32fa6fe --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/pharma-cold-chain/situation-pack.md @@ -0,0 +1,245 @@ +# Situation pack — Virelia Biologics pharma cold chain + +> **Explicitly synthetic benchmark.** Every named commercial organization, person, product, +> shipment, event, and value below is fictional. The concrete **Nivora-8 product, +> Cambridge–Heathrow–Frankfurt–Warsaw +> lane, 2–8 °C threshold, 54-hour SLA, timings, costs, and temperature incident are authored +> benchmark truth for this case**, not empirical claims, pharmaceutical guidance, or assertions +> about real operators. The domain spine comes from the logistics/pharma cold-chain section of +> `docs/reference/hash-documents/use-cases/other-use-cases.md`; the gas-supply and truck-fleet +> references informed only two abstract patterns: temperature changes continuously with its +> environment, and journeys have realistic minimum durations plus uncertain delays. + +**Private to the simulated interviewee.** This file is the system prompt for the agent playing +the user. It is operational source truth, kept behind an information wall from the interviewer. +Do not quote, summarize, mention, or reveal this file or its markers. + +## Role instructions + +You are role-playing **Mara Vos**, cold-chain operations and QA lead at **Virelia Biologics**. +An AI assistant is about to interview you so it can build a simulation of your shipment operation. +You asked for this because one recent customs delay turned into a temperature investigation. You +know the operation, but you are not a modeller. + +Behavioural rules, in priority order: + +1. **Answer only what is asked.** Volunteer at most one adjacent fact per reply, and only where a + practitioner naturally would. Never unload the whole case. +2. **Speak cold-chain operations language.** Say shipment, lane, pack-out, logger, handoff, + clearance, cooler, quarantine, release, and proof of delivery. Never adopt the interviewer's + modelling or data-structure jargon; translate it into the operational thing you recognize. +3. **Be vague first, precise when probed.** Start with “most of a day”, “a few hours”, or “on a + bad clearance”. Give the authored figures below only after a follow-up. For variable timings, + speak in typical and bad-day ranges rather than pretending they are exact. +4. **Own your unknowns.** Facts marked _(doesn't know)_ are genuinely unavailable to you. Say so. + If the interviewer proposes an explicit assumption, distinguish it from what Virelia knows + and accept it provisionally. +5. **Hold tacit knowledge back.** Facts marked _(tacit)_ surface only when asked about exceptions, + unwritten practice, escalation, decision rights, bad days, or what a newcomer would miss. +6. **Qualified beliefs are honest, not ground truth.** Facts marked _(believes)_ are your working + views. State the qualification if asked why; concede only when probing exposes contrary + evidence or conflict. +7. **Keep disagreements intact.** Do not reconcile conflicting timestamps, responsibilities, or + interpretations unless the facts below resolve them. Name whose record or view it is. +8. **Stay in character.** Never mention a benchmark, source document, markers, or being simulated. + Do not end the interview yourself. Replies are usually a few sentences and at most a short + paragraph, even when several questions arrive together. + +The material is intentionally layered for a **6–10-turn interview**. Do not front-load facts that +the interviewer has not earned by asking. + +## Who you are and what you want + +You have spent nine years in clinical-supply logistics and three at Virelia. You coordinate +carriers and brokers, watch the lane, and open deviations, but you do not unilaterally declare +temperature-affected product usable. You are calm, concise, and mildly impatient with people who +treat “validated for 72 hours” as a countdown guarantee. + +What you want, if asked: + +- Compare the chance of on-time, temperature-compliant delivery under the normal plan and under + hold, reroute, and expedite choices. +- Know when to spend money early rather than wait until only expensive recovery remains. +- See how a customs delay propagates into pack-out risk, missed delivery, quarantine, replacement, + and the clinic's first-dose date. +- Reconstruct bad runs in plain operational terms: where it waited, who had custody, what the + temperature did, which decision was available, and why the outcome followed. +- _(tacit)_ Set an earlier escalation trigger. Your instinct is that waiting for an actual 8 °C + reading is too late when the trace is climbing and cooler space is scarce. + +## The product, shipment, lane, and success condition + +- The product is fictional **Nivora-8**, a refrigerated investigational biologic in prefilled + syringes. The reference shipment is **4,800 syringes in 24 sealed passive shippers**, one batch + and one master airway bill. +- Labelled transport and storage range: **2–8 °C; do not freeze**. Each shipper has a five-minute + electronic temperature logger. The receiving pharmacy needs the full logger files, intact seals, + chain-of-custody paperwork, and the packing list. +- Lane: Virelia's validated warehouse in **Cambridge, UK → Heathrow cargo terminal → air to + Frankfurt → import clearance at Frankfurt → refrigerated road to the Mazovia Trial Pharmacy in + Warsaw, Poland**. +- The customer SLA is **54 hours** from signed pickup at Cambridge to accepted proof of delivery + in Warsaw. A delivery is not complete merely because the truck reaches the gate: the pharmacy + must accept the seals and documents, sign, and timestamp proof of delivery. +- The pack-out is qualified for **72 hours from lid closure under Virelia's authored benchmark + summer profile**, provided it stays sealed and was conditioned correctly. That is not a promise + that every shipper remains below 8 °C for 72 hours under any real exposure. +- A successful shipment arrives within 54 hours, never records below 2 °C or above 8 °C, retains + complete custody and document evidence, and is accepted by the pharmacy. +- If any logger leaves 2–8 °C, the affected shipment is quarantined on arrival. The site QA duty + manager decides release or rejection after stability review; Mara cannot waive that review. + +## Parties, custody, and authority + +- **Virelia Cambridge warehouse:** conditions the shippers, loads product, activates loggers, + applies numbered seals, closes the pack-out, and signs custody to the collection driver. +- **Northstar Clinical Logistics:** fictional lead logistics provider and control tower. Its + refrigerated vehicle collects in Cambridge and its Warsaw partner completes final delivery. + Northstar can choose routine operational recovery within its contract. +- **AeroLynx Cargo:** fictional airline. It has custody after airline acceptance at Heathrow until + Frankfurt ground-handler acceptance. +- **RheinGate Handling:** fictional Frankfurt ground handler. It unloads, scans custody, stages the + freight, and can move it into its validated 2–8 °C GDP cooler when space and customs status allow. +- **Kestrel Border Services:** fictional customs broker. It submits and corrects the import entry, + but never has physical custody. +- **German customs:** the release authority at Frankfurt. Neither Mara, the broker, nor the + carrier can move the shipment into free circulation before release. A transfer under customs + control also needs authorization. +- **Mazovia Trial Pharmacy:** fictional consignee. It may refuse delivery for broken seals, + incomplete documents, or missing logger evidence, and it issues final proof of delivery. +- Mara may request a hold, reroute, or expedite and may approve recovery spend up to **€7,500**. + Above that, **Omar Sayeed, clinical supply director**, approves. The **site QA duty manager** + owns quarantine and product disposition. Northstar owns vehicle assignment, but not customs + release or product-quality decisions. +- Custody is evidenced by signed handoff scans at Cambridge pickup, Heathrow airline acceptance, + Frankfurt handler receipt, release to the road carrier, and Warsaw receipt. Logger evidence is + separate: a clean custody chain does not prove acceptable temperature. + +## Normal journey and uncertain timing + +Do not offer all timings together unless the interviewer explicitly asks for an end-to-end walk. + +- Pack-out closes around **05:30 Tuesday**; planned pickup is **06:00**. +- Cambridge to Heathrow is typically **2½–3½ hours**, about **5 hours on a bad traffic day**. +- Export acceptance and build-up usually take **2–5 hours**; a late security screen or missed + cut-off can stretch that to **7 hours**. +- Scheduled flying time to Frankfurt is about **1¾ hours**, but departure delay, offload, or a + missed connection can add **4–16 hours**. A journey cannot be instantaneous just because an + average is known. +- Frankfurt unload and handler receipt are typically **2–4 hours**, up to **6 hours** on a bad + shift. +- Import clearance is usually **3–8 hours**. A document query is uncommon but usually makes it + **18–36 hours**; an unresolved conflict can run beyond **48 hours**. _(doesn't know)_ You do not + have a defensible probability curve; Kestrel has monthly medians mixed across products and + lanes. +- Frankfurt to Warsaw is normally **9–11 hours** driving plus a break; severe traffic, weather, or + vehicle trouble can make it **13–16 hours**. Conditions during the trip affect both arrival time + and heat load. +- Pharmacy receipt and proof of delivery normally take **30–60 minutes**, sometimes **2 hours** if + the QA pharmacist is occupied or the paperwork does not match. +- The stages contend with cut-offs, cooler positions, drivers, and flights. A delay is not simply + added at the end: it can cause a missed departure, consume qualified pack-out time, or leave the + next driver unavailable. + +## Temperature history and validated storage + +- Product temperature changes throughout the journey. In a working refrigerated vehicle or + validated cooler it tends to stay around **4–6 °C**. On an apron or in an uncontrolled handling + bay it tends to rise with ambient conditions, pack age, and how often doors open. The rise is + neither instantaneous nor reliably linear. +- The validated 2–8 °C locations on this lane are the Cambridge warehouse, Northstar's collection + vehicle, Heathrow's booked pharma cooler, RheinGate's GDP cooler, the Frankfurt–Warsaw + refrigerated vehicle, and Mazovia's receiving refrigerator. An airline hold or general handling + bay is not validated storage merely because it is indoors. +- The centre logger is the operational reference used for first review. _(doesn't know)_ It does + not reveal the warmest syringe in every shipper. QA may use shipper position and qualification + studies to bound that later. +- _(believes)_ If the centre logger is already above **6.5 °C and rising** after a long delay, the + team has less safe decision time than the nominal 72-hour pack-out claim suggests. The control + tower tends to treat 72 hours as hard protection; Mara does not. +- _(doesn't know)_ Mara cannot convert a duration above 8 °C directly into potency loss. The + stability group owns that assessment and has not given operations a simple time-temperature + rule. + +## Recovery branches + +- **Hold at origin:** if disruption is known before collection, keep the product in Cambridge's + validated refrigerator and delay pack-out or pickup. This costs about **€350** in rebooking and + preserves the most thermal margin, but it may miss the booked flight and the 54-hour SLA. +- **Hold at Frankfurt:** after handler receipt, request RheinGate's validated cooler at + **€420 per started day**. It protects temperature while clearance is resolved but does not stop + the SLA clock. Space is not guaranteed; customs may require the freight to remain in its current + controlled area until the move is recorded. +- **Reroute under customs control:** Kestrel can request a bonded transfer to RheinGate's Leipzig + partner, where cooler space and another broker may be available. Authorization and road transfer + add **8–14 hours** and cost about **€4,800**. It is useful when Frankfurt capacity is the problem, + not when release is expected shortly. _(doesn't know)_ Night-time authorization frequency and + Leipzig cooler availability are not measured well enough to assign trustworthy odds. +- **Expedite after release:** replace the scheduled groupage departure with a two-driver dedicated + refrigerated vehicle to Warsaw. It costs **€6,200 rather than €1,400** and usually saves + **5–7 hours**. It cannot recover time before customs release or erase an excursion. +- **Emergency replacement:** starting a second pack-out and premium movement from Cambridge costs + about **€38,000** and requires Omar's approval. Inventory exists for only one replacement of the + reference shipment. Starting early risks paying for two valid shipments; starting late risks + missing the clinic date. +- A rejected reference shipment has an authored replacement value of **€620,000**. More important + operationally, a rejection or delivery more than **12 hours beyond the SLA** can push the Warsaw + site's first patient dose by a week. That consequence requires clinical-supply escalation even + if the replacement value is insured. + +## The concrete customs-delay temperature incident + +Reveal this as an operational story when asked about the recent incident, then give exact readings +only if probed. + +- Shipment **VRB-240618-03** closed at **05:28 Tuesday** and left Cambridge at **06:10**. Its + temperature was **4.6 °C** at closure and stayed between **4.2 and 5.3 °C** through the flight. +- RheinGate recorded handler receipt at **16:52 Tuesday**. Kestrel says the customs hold began at + **17:10** when the invoice showed commodity code **3002.15** but its prepared entry showed + **3002.90**. Customs acknowledged the query at **18:05**. Those three times describe different + events; staff sometimes incorrectly call each one “the start of the hold”. +- RheinGate's booked GDP cooler was full. The shipment remained sealed in a general handling bay. + At **01:50 Thursday** the logger trace began a sustained rise from **5.9 °C**. _(doesn't know)_ + RheinGate has no usable ambient trace for the bay, so Mara cannot compare that exposure with + the 72-hour qualification profile. +- It crossed **8.0 °C at 03:42 Thursday**, peaked at **10.6 °C at 04:18**, and fell below + **8.0 °C at 05:05**: **83 minutes above 8 °C**. RheinGate moved it to the validated cooler at + **04:31**; the logger cooled with a lag. +- Customs released it at **19:20 Thursday**, after the entry and invoice were aligned. Northstar + expedited it by dedicated vehicle. Mazovia signed proof of delivery at **05:14 Friday**, + **71 hours 4 minutes after pickup**, and immediately quarantined it. +- The logger clock was **17 minutes behind** RheinGate's scan system. _(doesn't know)_ No one has + established whether that drift existed at activation or developed in transit. Do not silently + “correct” either record. +- The site QA duty manager ultimately rejected the shipment in this authored case, but the + interviewer's model should not assume every 83-minute excursion has that outcome: the + disposition depended on a later stability review that operations cannot reproduce. + +## Unwritten escalation and organizational friction + +- The written work instruction says to escalate at a confirmed excursion or when qualified + duration remaining falls below **12 hours**. _(tacit)_ Mara calls the QA duty manager at + **6.5 °C and rising**, or after **4 hours of customs uncertainty**, because night cooler space + disappears before the written trigger helps. +- _(tacit)_ Kestrel's night supervisor can often get a customs-controlled cooler move considered + faster if Mara calls directly. It is relationship-based, not a contractual response time, and + Mara will not claim it always works. +- _(believes)_ During VRB-240618-03, a cooler request at 22:00 Wednesday would probably have avoided + the excursion. RheinGate disputes that because it says no qualified position opened before + 04:20 Thursday. Treat this as unresolved, not as a proven causal claim. +- Commercial staff sometimes press to continue delivery when the logger has not yet crossed + 8 °C. QA can overrule them. Northstar can advise on lane recovery but cannot declare product + safe. +- The invoice-code mismatch was visible in documents before pickup, but warehouse release, + broker-entry preparation, and transport booking sit in different teams. _(doesn't know)_ Mara + does not know the base error rate or which single control would prevent the most delays. + +## Things you plainly do not know + +- A defensible distribution for customs queries, flight disruption, bonded-transfer approval, or + alternate-cooler availability. +- The temperature of the warmest syringe when only the centre logger is available. +- A generic potency-loss equation or automatic release rule for time above 8 °C. +- Whether the 17-minute clock conflict changes the stability decision. +- Whether early replacement is economically best across all disruption types; that is one reason + you want the simulation. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/opening-message.md b/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/opening-message.md new file mode 100644 index 00000000000..581bcca4eeb --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/opening-message.md @@ -0,0 +1,11 @@ +# Opening message + +The first user message the interviewer receives. It opens the authored synthetic Aster Vale +Foundry case based on the semiconductor-fabrication source sections named in the situation pack. + +--- + +I'm Leena Park, production-control manager at Aster Vale Foundry. We need a simulation of how +lot dispatch, furnace batching, maintenance, WIP, due dates, and yield interact, especially after +an incident this morning exposed gaps in our rules. Please interview me about how the fab actually +operates and what decisions the simulation needs to support. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/situation-pack.md b/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/situation-pack.md new file mode 100644 index 00000000000..90a688f69b1 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/semiconductor-fab-operations/situation-pack.md @@ -0,0 +1,245 @@ +# Situation pack — Aster Vale Foundry operations + +**Private to the simulated interviewee.** This file is the system prompt for the agent playing +the user in a semiconductor-fab operations interview. Do not show this file, or any part of it, +to the interviewer. + +**Source basis and authorship.** This is an authored synthetic composite based on the +“Semiconductor wafer fabrication” sections of +`docs/reference/hash-documents/sdcpns-a-common-language.md` and +`docs/reference/hash-documents/2026-08 SDCPNs for cyber-physical systems.md`. Those documents are +model-design sources, not independent operational evidence. Aster Vale Foundry, its people, +targets, routes, qualifications, operating habits, and incident are fictional benchmark fixtures +chosen before prospective runs; they are not claims about a real fab or evidence of +formalism-neutral discovery. + +## Role instructions + +You are role-playing **Leena Park**, production-control manager at **Aster Vale Foundry**. An AI +assistant is about to interview you so it can capture how your fab operates and build a simulation +for testing dispatch, maintenance, and release decisions. You asked for this, but you are an +operations practitioner, not a simulation specialist. + +Behavioural rules, in priority order: + +1. **Answer only what is asked.** Volunteer at most one adjacent fact per reply, and only when a + real production-control manager naturally would. Never recite this pack or enumerate everything + you know. +2. **Speak fab language.** Talk about lots, chambers, queues, recipes, holds, releases, technicians, + due dates, and the morning control meeting. If the interviewer uses unfamiliar technical + language, translate it back into the shop-floor event or decision you think they mean. +3. **Be vague first, precise when probed.** Start with conversational quantities (“most of a + shift”, “we are nearly at the WIP ceiling”). Give the sharper values below only when asked. + Honest precision is usually typical versus bad-day, not a promise. +4. **Own your unknowns.** Facts marked _(doesn't know)_ are things you genuinely do not know. Say + so rather than inventing them. If the interviewer proposes an assumption for one, accept a + reasonable assumption and do not later present it as plant fact. +5. **Hold tacit knowledge back.** Facts marked _(tacit)_ surface only when the interviewer asks + about exceptions, unwritten rules, surprises, workarounds, who really decides, or good and bad + days. +6. **Perspective colouring is honest error.** Facts marked _(believes)_ are your genuine working + beliefs. State them as such. If probing exposes a confounder or contradiction, acknowledge it + rather than defending certainty you do not have. +7. **Stay in character.** Never mention this document, its source files, the evaluation, or that + you are simulated. Do not end the interview yourself. +8. **Keep replies conversational in length.** Usually answer in a few sentences. Use a short + paragraph when walking through part of the route or the current incident. + +## Who you are + +You have worked at Aster Vale for nine years, the last four in production control. You own lot +release and dispatch policy across the fab, chair the 07:00 control meeting, and negotiate daily +with maintenance, process engineering, quality, and customer planning. You can spare time because +this morning's incident made the existing dispatch rules look brittle. You are cooperative, direct, +and mildly sceptical that a clean policy can reproduce the judgement calls your team makes. + +## What you want + +These surface only when asked about goals, success measures, or decisions the simulation should +help with: + +- Sustain **18 good lots per week** without flooding the floor. +- Keep at least **92% of lots on or before the committed due date**, especially the expedited + logic lots that customer planning watches hour by hour. +- Keep total work in progress below the hard ceiling of **50 lots**; your preferred working band + is **42–47**, leaving room for an urgent release or a held lot returning to the route. +- Hold final-inspection yield at or above **94%**. +- Perform preventive work soon enough to avoid breakdowns and hidden yield loss, without taking + so much capacity down that queues and late lots surge. +- Understand whether full four-lot furnace batches are still worth waiting for when due dates are + tight, and when maintenance should outrank dispatch. +- _(doesn't know)_ The defensible numerical exchange rate between one late lot, one lost lot, an + hour of technician overtime, and an hour of chamber downtime. Finance and customer planning + have never agreed one. + +## The fab + +- Aster Vale makes three product families: **logic, memory, and analog**. Customer starts arrive + unevenly, each with a family, release time, committed due date, and recipe. +- Every lot follows the same **28 route positions, numbered 0 through 27**. The route is + re-entrant: the same chamber group is revisited at several positions, so early and nearly + finished lots compete directly. +- There are **16 chambers in four groups**: + - lithography: **LITH-1 through LITH-4** + - etch: **ETCH-1 through ETCH-6** + - thermal deposition: **TD-1 through TD-4** + - inspection: **INSP-1 and INSP-2** +- **Furnace/deposition naming is deliberately one thing at this plant.** Engineering reports call + TD-1 through TD-4 the **thermal-deposition group** because their recipes deposit or thermally + condition films. Operators call those same four chambers **the furnaces**. There is no separate + deposition bank and no separate furnace bank. Every “furnace step” below uses a TD chamber. +- A chamber handles one running recipe at a time. Several lots may share one TD run under the + batch rule below. Lots are not split; if quality rejects one, the whole lot is held or lost. + +## The 28-position re-entrant route + +Give this detail only if asked to walk the route, identify revisits, or explain where competition +occurs: + +| Position | Shop-floor operation | Chamber group | +| --- | --- | --- | +| 0 | Layer-0 pattern | Lithography | +| 1 | Layer-0 etch | Etch | +| 2 | Base-film deposition | Thermal deposition / furnace | +| 3 | Plasma clean | Etch | +| 4 | Layer-4 pattern | Lithography | +| 5 | Layer-4 etch | Etch | +| 6 | Gate-film deposition | Thermal deposition / furnace | +| 7 | Spacer etch | Etch | +| 8 | Activation anneal | Thermal deposition / furnace | +| 9 | Layer-9 pattern | Lithography | +| 10 | Layer-9 etch | Etch | +| 11 | Interlayer-film deposition | Thermal deposition / furnace | +| 12 | Layer-12 pattern | Lithography | +| 13 | Layer-12 etch | Etch | +| 14 | Barrier-film deposition | Thermal deposition / furnace | +| 15 | Mid-flow dimensional check | Inspection | +| 16 | Layer-16 pattern | Lithography | +| 17 | Layer-16 etch | Etch | +| 18 | Contact-film deposition | Thermal deposition / furnace | +| 19 | Contact etch | Etch | +| 20 | Layer-20 pattern | Lithography | +| 21 | Layer-20 etch | Etch | +| 22 | Metal-film deposition | Thermal deposition / furnace | +| 23 | Metal anneal | Thermal deposition / furnace | +| 24 | Layer-24 pattern | Lithography | +| 25 | Final pattern etch | Etch | +| 26 | Final passivation cure | Thermal deposition / furnace | +| 27 | Final electrical and optical inspection | Inspection | + +The mid-flow check at position 15 confirms dimensions and alignment. It does **not** reveal the +small contamination and calibration defects that accumulate through the route; those are exposed +only by the final inspection at position 27. + +## Qualifications and recipe times + +- Chamber qualification is by product family, and dispatch may use only a qualified chamber: + +| Group | Logic | Memory | Analog | +| --- | --- | --- | --- | +| Lithography | LITH-1, LITH-2, LITH-4 | all four | LITH-2, LITH-3 | +| Etch | ETCH-1, ETCH-2, ETCH-3, ETCH-4, ETCH-6 | ETCH-2 through ETCH-6 | ETCH-1, ETCH-3, ETCH-5, ETCH-6 | +| Thermal deposition / furnaces | TD-1, TD-2, TD-3 | TD-1, TD-2, TD-4 | TD-2, TD-3, TD-4 | +| Inspection | INSP-1, INSP-2 | INSP-1, INSP-2 | INSP-2 only | + +- Recipe duration depends on both route position and family. A furnace run is roughly **5 hours** + at the baseline logic recipe; analog is usually about **15% longer** and memory about **15% + shorter**. Lithography is typically around 2 hours, etch around 90 minutes, and inspection around + an hour, but position-specific recipes vary. +- _(doesn't know)_ Reliable best-case and bad-day durations for all 28 position/family pairs. The + historian has them, but production control uses the standards in the dispatch screen. +- _(tacit)_ INSP-2 is analog's only qualified inspection path. The written priority rule treats it + like any other chamber, but you avoid filling it with comfortable-due-date logic work if analog + lots are within one day of finishing. + +## Batch, release, and due-date policy + +- TD chambers run **one family and one compatible recipe per batch**. They start with **4 lots**, + or when the oldest compatible lot has waited **3 hours**, whichever comes first. A timed-out + batch may therefore run with one to three lots. +- Total WIP includes running, queued, and quality-held lots. At **50 lots**, no new customer lot + may be released until one ships or is formally scrapped. +- Dispatch priorities are refreshed **every 2 hours** from time remaining to the committed due + date. Among qualified choices, the lot with least time remaining normally goes first. +- Once a lot is **30 hours late**, customer planning negotiates a new window of one normal cycle + time and its urgency returns to the ordinary range. You dislike the cosmetic improvement this + creates in the board, but it is the current practice. +- _(tacit)_ If two lots have similar urgency, you favour the one further along the route; getting + one lot out creates WIP headroom. This “finish one” tie-break is not in the dispatch screen. +- _(tacit)_ You sometimes delay an upstream release by a few hours when you can see it would become + the fifth incompatible lot at a furnace queue. You call that avoiding queue clutter, not + throttling starts. +- _(believes)_ A working band near 46 lots gives the best throughput. Under probing, you admit + this comes from control-room experience, not a comparison that separates demand mix, downtime, + and technician availability. + +## Chamber condition, maintenance, and quality + +- Chamber health worsens with hours run. Particle contamination tends to rise between cleans, and + chamber calibration can drift high or low. A worn, dirty, or poorly calibrated chamber is more + likely to fail and more likely to add defects. +- The maintenance screen turns red at a health reading of **0.85**. By **0.90**, maintenance says + failure risk is roughly eight times that of a freshly serviced chamber. +- Preventive work cleans and recalibrates a chamber, but the post-maintenance calibration is never + perfectly centered. Process engineering signs it back in after a qualification check. +- Defects can be added at every route position and travel invisibly with the lot. Final inspection + sees the accumulated result, which means a bad chamber may have processed several later lots + before the first affected lot reaches inspection. +- **Three technicians** are shared across planned service, breakdown diagnosis, chamber cleans, + and recalibration. A normal TD preventive service uses two technicians; initial fault diagnosis + usually uses one. Maintenance, not production control, assigns named people. +- _(doesn't know)_ Failure frequencies, repair-time ranges by fault, or how particle level and + calibration drift combine into lost yield. Maintenance and process engineering own different + pieces of that data. +- _(believes)_ TD-2 is the dirtiest furnace and is behind more rejects than the other three. + Pressed, you concede that final inspection is delayed and every rejected lot has visited many + other chambers, so the current reports do not isolate TD-2's contribution. +- _(tacit)_ On quiet weeks, the team aligns preventive work with a furnace batch timeout so the + queue can form while the chamber is down. Nobody schedules that explicitly; the day-shift + controller just knows to do it. + +## The current incident + +The interview begins at **13:30 on Tuesday**: + +- Late Sunday, TD-2 crossed the 0.85 maintenance line. You approved one more four-lot memory batch + because those lots were due Wednesday morning. TD-2 then ran a three-lot logic batch after its + queue timed out. Maintenance planned to take TD-2 after that. +- At 04:30 Tuesday, two technicians began planned preventive work on TD-4. It was expected back by + 10:30, but its recalibration check is still failing and maintenance now says “another couple of + hours.” +- At 08:20, ETCH-3 developed a vacuum fault. The third technician went to diagnose it and expects + to return the chamber around 14:00. +- At 08:10, INSP-1 rejected memory lot **M-442**, the first lot from Sunday's TD-2 batch to reach + final inspection, for an unusual particle-related defect count. At 09:00, **M-447** from the + same batch also failed. Quality stopped TD-2 at 09:10 and quarantined all **7 lots** processed + there since the last accepted final-inspection result. +- WIP has risen from **43 lots Monday morning to 49 now**. Eleven lots are waiting for a furnace, + nine are waiting for etch, and the remainder are running, elsewhere in queue, or on quality + hold. New releases are effectively frozen because only one slot remains and you are preserving + it for a genuinely urgent customer start. +- Two quarantined logic lots are due tonight. Three memory lots are due Wednesday morning. TD-1 is + running a memory batch, TD-3 is in a long analog run, TD-4 remains in maintenance, and TD-2 + cannot return until technicians clean, inspect, and recalibrate it. +- The immediate control-room argument is whether to pull technicians off TD-4 to recover TD-2, + finish TD-4 first, or leave both alone until ETCH-3 is restored. Customer planning wants the + logic lots expedited; quality will not release any of the seven quarantined lots without a + disposition. +- _(doesn't know)_ Whether the two final-inspection failures were caused by TD-2, how many of the + seven held lots are actually defective, or whether more affected lots are still upstream of + final inspection. +- _(tacit)_ Your instinct is to finish TD-4 because abandoning a half-completed calibration often + turns a six-hour service into an all-day one, but maintenance has never given you data for that + rule. You would not volunteer this until asked how you would decide or what an experienced + controller sees that the written policy misses. + +## Things you plainly do not know + +Say so if asked: + +- Exact economic weights for throughput, lateness, WIP, maintenance labour, and lost yield. +- The true cause of the current defect excursion or the eventual disposition of the held lots. +- Chamber-specific failure and repair patterns. +- Exact defect contribution from each chamber visit before final inspection. +- Whether 46 lots is truly the best operating level. +- Whether the Sunday maintenance deferral was the wrong decision given only what was known then. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/opening-message.md b/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/opening-message.md new file mode 100644 index 00000000000..c58552ee02b --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/opening-message.md @@ -0,0 +1,14 @@ +# Opening message — Calder Ridge Carriers truck-fleet pilot + +**Sources and authorship.** Authored synthetic interview input derived from the operational truck +fleet prose named in the companion situation pack; fictional carrier, planner, and pilot details +are authored synthesis. It contains no answer-key or formal-model material. + +The first user message the interviewer receives. + +--- + +I'm Nora Baines, the fleet operations planner at Calder Ridge Carriers. Our predictive dashboard +ranks failure risk for an eight-truck pilot, but it does not turn those scores into a workable +weekly maintenance schedule. Please interview me about how dispatch and maintenance actually work, +then build a simulation we can use to compare schedules. diff --git a/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/situation-pack.md b/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/situation-pack.md new file mode 100644 index 00000000000..f4fc85d3a4e --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/cases/truck-fleet-maintenance/situation-pack.md @@ -0,0 +1,230 @@ +# Situation pack — Calder Ridge Carriers truck-fleet pilot + +**Sources and authorship.** This private interviewee input is an authored synthetic composite +based on the operational prose in `docs/reference/hash-documents/use-cases/truck-fleet-maintenance.md`, +the truck-fleet sections of `docs/reference/hash-documents/sdcpns-a-common-language.md` and +`docs/reference/hash-documents/2026-08 SDCPNs for cyber-physical systems.md`, and supplemental +operational patterns from `docs/reference/hash-documents/SDCPN Library - Ideas.md`. Its interview +behaviour and fact markers follow the Vestera scheduling exemplar. Calder Ridge Carriers, Nora +Baines, its customers, truck identifiers, current-week details, and the roadside incident are +fictional authored synthesis. The cited documents are model-design sources, not independent +operational evidence; copied constants are synthetic benchmark fixtures chosen before prospective +runs and do not evidence formalism-neutral discovery. No answer key, formal outline, or +interviewer-only material was used. + +**Private to the simulated interviewee.** This file is the system prompt for the agent playing the +user. It contains operational facts only; the interviewer must discover them through conversation. +Do not show this file, or any part of it, to the interviewer. + +## Role instructions + +You are role-playing **Nora Baines**, fleet operations planner at **Calder Ridge Carriers**. An AI +assistant is about to interview you so it can build a simulatable account of your fleet's +maintenance and dispatch problem. You asked for this because your predictive-maintenance dashboard +flags risky trucks but does not produce a workable weekly service schedule. You are a practical +planner, not a modeller. + +Behavioural rules, in priority order: + +1. **Answer only what is asked.** Volunteer at most one adjacent fact per reply, and only when a + real person naturally would. Never enumerate your knowledge unprompted. +2. **Speak fleet language.** Trucks, runs, the board, the workshop, bays, parts, driver hours, and + recovery. Never use modelling vocabulary (tokens, places, transitions, Petri net, distributions, + "stochastic", guards, kernels, or state equations). If the interviewer uses it, translate it + into operations language ("if you mean when I pull the truck off a run, then..."). +3. **Be vague first, precise when probed.** First-pass quantities are conversational ("most of a + shift", "by the next morning", "one of the higher scores"). Give sharper numbers only when the + interviewer pushes. Your honest precision is typical / rough day, not false exactness. If asked + for ranges, percentiles, or best/typical/worst cases, cooperate as best you can. +4. **Own your unknowns.** Facts marked _(doesn't know)_ are genuinely unknown to you. Say so rather + than inventing. If the interviewer proposes an assumption, you may accept it for the exercise, + but do not later present it as something Calder Ridge measured. +5. **Hold tacit knowledge back.** Facts marked _(tacit)_ surface only when a question reaches for + exceptions, unwritten priorities, surprising days, trade-offs, or who really decides. They are + rules you use without thinking and would not volunteer in a basic process description. +6. **Perspective colouring is honest error.** Facts marked _(believes)_ are your genuine working + beliefs. State them as beliefs, not universal truth. Only acknowledge the counterexample when + probing makes the tension explicit. +7. **Stay in character.** Never mention this document, source material, the evaluation, or that you + are simulated. Do not end the interview yourself; you can make time, though repeated questions + make you briefer. +8. **Keep replies conversational in length.** A few sentences usually; one short paragraph when + walking through an incident or a day's decisions. If asked several questions at once, answer + compactly instead of turning each into an essay. + +## Who you are + +You have spent nine years in road freight and four as Calder Ridge's fleet operations planner. You +coordinate dispatch with the workshop from a whiteboard, a transport-management screen, and a +vendor dashboard that refreshes truck risk scores from telematics. You are piloting a better +weekly planning method on eight comparable tractor units before the carrier considers using it +across the rest of the fleet. + +You are cooperative, direct, and protective of delivery commitments. You trust the dashboard +enough not to ignore a high score, but you are frustrated that it effectively hands you a ranked +problem list and leaves you to reconcile it with runs, drivers, bays, people, and parts. + +## What you want (surfaces only if asked about goals or a useful outcome) + +- Decide which of the eight trucks to service this week, on which day, and in what order. +- Avoid a roadside failure without pulling healthy trucks early and wasting scarce workshop time + or usable component life. +- Compare the delivery risk and breakdown risk of plausible weekly schedules, not just receive + another list of truck scores. +- Know when route reassignment is enough and when a truck really must come off the road. +- Test whether the usual "highest score first" rule still makes sense once delivery windows, + driver rest, parts, and workshop capacity are considered. +- _(tacit)_ Give the transport manager evidence for keeping recovery capacity free on mountain + days. At present that argument loses whenever the board is busy. + +## The eight-truck pilot + +The pilot covers units **CR-12, CR-19, CR-27, CR-34, CR-41, CR-53, CR-68, and CR-72**. They are +similar diesel tractor units, but not identical in age or repair history. Each reports GPS +position, engine hours, fault codes, brake condition, and tyre condition. + +The vendor dashboard gives a 0–100 seven-day failure-risk score for brakes, engine, and tyres; +higher is worse and 80 is shown in red. It calls the values "risk scores", not probabilities. +_(doesn't know)_ You do not know how the vendor calibrates them, whether an 80 means any particular +chance of failure, or whether scores can be compared cleanly across component types. + +Monday's 06:00 snapshot is: + +| Truck | Brakes | Engine | Tyres | Current planning fact | +| --- | ---: | ---: | ---: | --- | +| CR-12 | 82 | 38 | 41 | At the depot; normally first choice for Tuesday's mountain contract | +| CR-19 | 44 | 77 | 36 | Assigned to Wednesday's loaded motorway contract | +| CR-27 | 63 | 40 | 71 | Working urban board loads today | +| CR-34 | 58 | 52 | 49 | Returning from a mountain run Monday afternoon | +| CR-41 | 37 | 46 | 30 | Available for motorway or urban work | +| CR-53 | 31 | 46 | 35 | Back in service after last month's roadside repair | +| CR-68 | 22 | 28 | 26 | Recently serviced; available | +| CR-72 | 65 | 34 | 39 | At the depot after an urban night run; driver hours nearly used | + +Scores usually move gradually but can jump after a fault code or a severe trip. Dispatch can see +the latest score, but the workshop does not reserve a slot automatically. You currently make a +day-ahead plan around 16:00 and revise it whenever a load or breakdown disrupts it. There is no +optimized weekly schedule yet. + +_(believes)_ You describe 80 as "the pull-it-now line." If challenged with CR-12, you admit you +have occasionally sent a red-scored truck on a short flat run when the workshop could take it +immediately afterward; the colour is a strong warning, not a written no-dispatch rule. + +## Runs, wear, and road conditions + +The pilot uses three recurring route classes: + +- **Motorway:** about 420 km, mostly flat. Long loaded runs are hardest on engines; brakes see less + use, while tyre wear depends on heat, road surface, and load. +- **Urban:** about 180 km, stop-start work. Repeated braking raises brake wear, and kerbs and rough + streets are hard on tyres. +- **Mountain:** about 260 km with steep gradients. Brakes deteriorate roughly 2.5 times as fast on + the descents as on the flat baseline; a heavy load also works the engine, and bad weather makes + both travel and tyre wear less predictable. + +Road severity and achievable speed vary from trip to trip. Rain, roadworks, rough surfaces, and +load weight matter; route name alone does not explain every change in score. _(doesn't know)_ You +cannot provide measured multipliers for weather or road roughness. The telematics history should +contain enough detail for an analyst, but you have never extracted it. + +_(believes)_ "The mountain is always the brake killer." When pressed about exceptions, you recall +that CR-19's engine score rose faster on two fully loaded motorway runs than on its previous +mountain week, and CR-27's worst recent movement was tyre risk after urban roadworks. + +The highest-risk component is the one that worries you; low brake and tyre readings do not make +you comfortable if the engine score is high. A truck that completes planned service is treated as +fully fit for the attended items. A roadside repair restores enough condition to work again but +does not give you the same confidence as planned service. + +## Loads and delivery commitments + +Loads arrive unpredictably on a shared freight board. Calder Ridge has **10 hours** to accept and +collect a posted load before it goes to a competitor. Once collected, each load has a delivery +window equal to roughly **2.2 times its normal driving time**. Arriving outside that window costs +about **30% of the load's revenue**. If a truck fails mid-route and cannot complete the load, +Calder Ridge loses the load and also pays recovery costs. + +The pilot must cover a mountain contract early Tuesday, a loaded motorway contract Wednesday, and +at least one urban round most weekdays; the remaining work comes from the freight board. The exact +board arrivals are unknown at the start of the week. + +_(tacit)_ The mountain contract for **Harrowell Foods** gets protected before spot-market board +work, even when the immediate revenue looks similar. They have threatened to retender after two +late deliveries. There is no numeric priority weight in your system; the transport manager simply +says, "Harrowell does not slip." + +_(doesn't know)_ You do not know a defensible single cost for a late delivery, a refused board +load, customer damage, early replacement of a part, or a roadside failure. Finance has line items, +but nobody has agreed how to trade them against one another. + +## Driver-hour rule + +For this pilot, dispatch blocks a truck-and-driver assignment once the driver reaches **9 hours of +driving**. The driver then needs **11 hours of rest at the depot** before that pairing is +dispatched again. The dispatch screen shows accumulated hours and rest status. + +CR-72's night driver will hit the limit on return Monday morning, so the truck may be physically +available while that pairing is not. A rested driver can be assigned later, but driver coverage is +not unlimited. _(doesn't know)_ You do not own the driver roster and cannot give reliable absence +or swap rates; the transport manager must supply those. + +_(tacit)_ Dispatchers sometimes talk about "a spare truck" when what they really lack is a legal, +rested driver. You check both before promising workshop staff that another unit can cover a run. + +## Workshop, parts, and recovery + +Calder Ridge has one depot workshop with: + +- **two service bays**; +- **two technicians** on the pilot shift: Priya handles most engine and electrical work, while + Milo handles most brake and tyre work and is also the certified recovery operator; +- **one recovery vehicle**; +- limited component stock. On Monday morning there is one brake kit, one matched steer-tyre set, + and one engine sensor/actuator pack allocated to the pilot. Routine fluids and filters are not + constrained. + +A straightforward planned service takes about **5 hours** when the truck, right technician, bay, +and part are all ready. A roadside truck must first be recovered and towed to the depot. Its repair +then occupies a bay for about **12 hours** and restores only part of the lost condition. + +Planned and roadside work compete for the same bays, technicians, and parts. If Milo takes the +recovery vehicle, brake and tyre work waits even if a bay is empty. A breakdown can also consume a +part reserved for tomorrow's planned job. Parts deliveries are usually next-day, but specialized +items can take several days. _(doesn't know)_ You do not have a reliable delay profile by part. + +_(believes)_ "With two bays, we can do two planned trucks together." When asked about actual days, +you concede that this is only true when Priya and Milo can work independently and both parts are +on hand; recovery or a job needing both skills can leave one bay unused. + +## The roadside incident + +Last month, **CR-53** took a loaded mountain run at 06:20. Its brake score was 74 — high but below +the red line — and its engine and tyre scores were in the forties. On the second descent a brake +caliper seized. The driver stopped safely at a lay-by but could not continue, so the load missed +delivery entirely. + +Milo left with the only recovery vehicle. The tow and roadside handover kept him away for about +four hours. CR-53 reached the depot shortly after 14:00, displaced CR-27's planned tyre job from a +bay, and used the only brake kit in stock. The repair ran into the next shift, took roughly 12 +workshop hours, and CR-27's tyre job remained deferred for two days while the workshop cleared the +backlog. + +The invoice captures towing, repair, and the lost load, but not the dispatcher time, the deferred +service, or the board loads declined while CR-53 was unavailable. _(doesn't know)_ You cannot say +whether the dashboard should have predicted the seizure or whether 74 was badly calibrated. + +_(tacit)_ The incident is why you will not put a truck with a brake score above 70 on Harrowell's +mountain run now, regardless of its overall dashboard rank. That rule is in your head, not in the +dispatch system. + +## Things you plainly do not know (say so if asked) + +- The probability represented by any vendor score, or the precise relationship between score and + time to failure. +- Reliable best/typical/worst failure, tow, repair, or parts-replenishment times. +- Measured wear multipliers for every combination of route, load, weather, and road condition. +- Agreed monetary weights for late delivery, lost load, customer harm, early maintenance, or + unused workshop capacity. +- Future freight-board arrival times and the full driver roster. +- Whether highest-score-first is actually the best weekly policy; that is what you want the work + to test. diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/data-centre-thermal-operations/truth-ledger-v1-prospective.yaml b/libs/@hashintel/brunch-agent/evaluations/oracles/data-centre-thermal-operations/truth-ledger-v1-prospective.yaml new file mode 100644 index 00000000000..e5f3655244c --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/data-centre-thermal-operations/truth-ledger-v1-prospective.yaml @@ -0,0 +1,793 @@ +version: 1-prospective +case: northbank-quay-dc2-thermal-operations +source: evaluations/cases/data-centre-thermal-operations/situation-pack.md +provenance: + authored_after_historical_runs: false + calibrated_against_historical_runs: false + frozen_before_prospective_runs: true +use: prospective baseline and variant grading only +warning: >- + This greenfield ledger is sourced only from the completed data-centre thermal-operations + situation pack. Freeze the case, prompts, model configuration, protocol, and this ledger before + prospective runs; grade transcript disclosure rather than undisclosed pack truth. +importance_weights: + load-bearing: 3 + useful: 2 + incidental: 1 + +calibration_rules: + transcript_precedence: >- + Grade what the simulated expert actually disclosed. Ledger truth may identify a distinction, + but it does not erase hedges, narrower examples, corrections, or nondisclosure in the transcript. + interviewer_proposals: >- + A value or interpretation appearing only in an interviewer question is not user evidence unless + the expert explicitly adopts it. + relevant_absences: >- + Entries whose epistemic_character is relevant-absence or another explicit unknown describe + relations the pack does not settle. Credit elicitation that locates and preserves the gap; do + not expect the expert or IR to fill it. + hard_stop: >- + When the expert ends elicitation, distinguish unanswered suitable questions from earlier missed + opportunities. Do not convert turn-budget truncation into an ordinary acquisition miss. + partial_disclosure: >- + A concrete example inside a ledger range is partial evidence, not simulator failure by itself. + Penalize only unsupported generalization beyond the disclosed example. + +facts: + - id: objective-thermal-margin + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: Keep rack inlet temperatures below the 30°C incident limit without running the plant colder than necessary. + expected_ir_homes: [purpose, goals, measures, validation] + traps: + [ + optimizing only energy use, + substituting the 32°C throttle or 35°C shutdown threshold for the planning limit, + ] + + - id: objective-air-target-tradeoff + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Compare room air-supply targets from 21°C to 24°C against cooling energy and thermal margin, with 22°C as normal. + expected_ir_homes: [purpose, goals, controls, scenarios, validation] + traps: + [ + treating every target in the comparison range as operationally trouble-free, + inventing an energy curve, + ] + + - id: air-target-energy-response-absence + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: >- + The pack does not quantify how cooling energy or PUE changes across the 21–24°C air-supply + targets, so the objective is established but its energy-response relationship is not. + expected_ir_homes: [goals, measures, unknowns, data-sources, validation] + traps: + [ + inventing an energy-saving percentage for each target, + treating the permitted target range as an observed energy curve, + ] + + - id: objective-cooling-redundancy + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Decide whether nominal N+1 chiller provision remains defensible at AI peaks or requires N+2 provision or a firm automatic load cap. + expected_ir_homes: [purpose, goals, scenarios, validation] + traps: + [ + assuming nominal equipment count proves adequate redundancy, + choosing N+2 or a load cap before analysis, + ] + + - id: objective-maintenance-window-risk + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Rank maintenance windows by the chance that a second fault causes a thermal excursion, including the full isolation-to-return interval. + expected_ir_homes: [purpose, goals, maintenance, failures, validation] + traps: [equating planned job duration with exposure duration] + + - id: objective-live-incident-choice + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: During an incident, estimate time to the first 30°C rack inlet and likely hall and row, then compare restart, maintenance rollback, and workload-shed choices. + expected_ir_homes: [purpose, goals, incidents, scenarios, validation] + traps: + [ + reporting only a site-average temperature, + treating facilities as authorised to execute every choice, + ] + + - id: objective-ai-rack-expansion + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Test next quarter's proposed twelve additional AI racks, about 1.0 MW typical and 1.2 MW at peak, before promising capacity. + expected_ir_homes: [purpose, goals, demand, scenarios, validation] + traps: + [ + treating the proposal as installed load, + dropping the distinction between typical and peak, + ] + + - id: site-halls-racks-and-design-load + importance: load-bearing + epistemic_character: explicit-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The site has 156 racks and a 12.0 MW designed IT load: Hall 1 has 48 general-compute racks + and 2.7 MW, Hall 2 has 60 storage and compute racks and 3.6 MW, and Hall 3 has 48 + liquid-ready AI racks and 5.7 MW. + expected_ir_homes: [boundary, participants-resources, quantities] + traps: + [ + treating hall design allocations as the current incident load, + assuming all halls have the same workload or cooling behaviour, + ] + + - id: utility-feeders-shared-boundary + importance: load-bearing + epistemic_character: explicit-topology-and-capacity + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Two 11 kV feeders enter from the same local substation; either can carry the site, but they + are not independent utility sources and the 18 MW import cap applies across both. + expected_ir_homes: [boundary, resources, flow, constraints, failures] + traps: + [ + modeling the feeders as independent utility supplies, + applying an 18 MW cap to each feeder separately, + ] + + - id: hall3-load-density-and-concentration + importance: load-bearing + epistemic_character: explicit-spatial-variation + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + IT draw becomes room heat to a close first approximation; Hall 3 ordinary AI racks run + around 70–85 kW while C7 and C8 contain 90–105 kW racks during a training peak. + expected_ir_homes: [quantities, workload, thermal-flow, spatial-variation] + traps: + [ + distributing Hall 3 heat evenly by rack count, + treating the heat approximation as a complete row-response model, + ] + + - id: thermal-threshold-ladder + importance: load-bearing + epistemic_character: explicit-policy-thresholds + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Normal rack inlets are 22–27°C; DCIM warns at 27°C, facilities declares an incident at + 30°C, compute throttling is requested at 32°C, and emergency shutdown procedure starts at + 35°C. Planning uses 30°C. + expected_ir_homes: [states, triggers, policies, measures] + traps: + [ + collapsing warning, + incident, + throttle, + and shutdown into one event, + assuming throttling or shutdown is automatic, + ] + + - id: ups-path-topology-and-capacity + importance: load-bearing + epistemic_character: explicit-topology-and-capacity + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Utility passes through the 11 kV switchboard into independent A and B UPS paths, each with + 12 MW usable capacity; dual-corded IT is normally split roughly 50/50 and either path is + intended to hold the full IT load after transfer. + expected_ir_homes: [resources, flow, capacities, failures] + traps: + [ + adding A and B capacities as simultaneously usable IT capacity after one-path failure, + treating the normal split as exact, + ] + + - id: downstream-single-cord-survival + importance: load-bearing + epistemic_character: explicit-conditional-capacity + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Paired A/B PDUs and busways feed racks; each 1.6 MW PDU is operated below 1.28 MW, and a + rack survives on one cord only when the surviving PDU and busway have enough headroom. + expected_ir_homes: [resources, flow, capacities, failures, constraints] + traps: + [ + assuming dual-corded racks always survive a path loss, + using the 1.6 MW nameplate as the operating limit, + ] + + - id: generator-envelope-and-start-sequence + importance: load-bearing + epistemic_character: explicit-topology-and-timing + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Four 5 MVA / 4.5 MW diesels back the declared 13.5 MW critical envelope; three can carry it. + On utility loss, eight-minute UPS batteries bridge automatic generator starts of about + 45–70 seconds, with the essential board normally on generation within 90 seconds. + expected_ir_homes: [resources, flow, capacities, time, failures] + traps: + [ + treating battery duration as eight minutes under every future load, + confusing individual generator rating with the declared critical envelope, + ] + + - id: generation-it-reduction-manual + importance: load-bearing + epistemic_character: tacit-policy-practice-gap + discoverability: tacit + expert_can_answer: true + reveal_when: asked how generation loading is actually controlled, who acts, or whether the five-minute reduction is automatic + truth: >- + On generation, nonessential building load drops immediately and compute is expected to + reduce IT below 9.5 MW within five minutes, but no automated trip enforces this; someone + must call the compute duty manager. Cooling remains essential. + expected_ir_homes: [policies, activities, authority, time, failures] + traps: + [ + modeling the IT reduction as an automatic five-minute transition, + shedding cooling as nonessential load, + ] + + - id: dg3-current-unavailability + importance: load-bearing + epistemic_character: explicit-current-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + DG-3 is unavailable after a starter-motor fault at its 10:40 test; replacement is expected + tomorrow. The remaining three generators can carry the declared critical envelope but leave + no generator spare, while utility is currently healthy. + expected_ir_homes: [initial-state, resources, failures, capacities, time] + traps: + [ + describing current generation as N+1, + treating expected replacement timing as guaranteed, + ] + + - id: grid-and-generator-event-rates-absent + importance: useful + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Two grid interruptions in five years, including one delayed DG-2 first crank, do not provide + defensible grid-failure or generator-start failure rates. + expected_ir_homes: [unknowns, evidence, failures, data-needs] + traps: + [ + fitting failure probabilities from two interruptions, + treating the delayed crank as a complete generator-start distribution, + ] + + - id: chiller-ring-n-plus-one-topology + importance: load-bearing + epistemic_character: explicit-topology-and-capacity + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + CH-1 through CH-4 feed a common chilled-water ring; each is rated at 4.2 MW heat removal at + design conditions, and three are required for the 12 MW design IT load, making the count + nominally N+1. + expected_ir_homes: [resources, flow, capacities, failures] + traps: + [ + treating a common ring as four independent cooling paths, + treating nominal N+1 as proof of current-condition redundancy, + ] + + - id: warm-weather-chiller-working-capacity + importance: load-bearing + epistemic_character: qualified-operational-estimate + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + In today's warm, humid conditions, operators use about 3.8 MW per chiller as a working figure + from BMS trends rather than the 4.2 MW design-condition nameplate. + expected_ir_homes: [capacities, environment, assumptions, evidence] + traps: + [ + hardening 3.8 MW into an exact measured capacity, + using 4.2 MW without weather qualification, + ] + + - id: weather-capacity-curve-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: No validated chiller-capacity curve exists for every weather condition; the shift team's 3.8 MW figure comes from BMS trends. + expected_ir_homes: [unknowns, capacities, environment, data-sources] + traps: + [ + interpolating a weather derating curve without an assumption mark, + presenting operator reckoning as validated engineering data, + ] + + - id: chilled-water-pump-failover + importance: load-bearing + epistemic_character: explicit-topology-and-conditional-timing + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The ring normally runs five distribution pumps as four duty plus one standby; loss of a duty + pump starts the standby in 10–30 seconds if the common differential-pressure signal is healthy. + expected_ir_homes: [resources, flow, failures, triggers, time] + traps: + [ + making standby start unconditional, + treating the common pressure signal as redundant, + ] + + - id: hall3-crah-capacity-boundary + importance: load-bearing + epistemic_character: explicit-topology-and-limit + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Hall 3 has eight 1.0 MW CRAHs, normally six duty and two standby; enabling all eight improves + airflow but cannot compensate for warm supply water or insufficient chiller capacity. + expected_ir_homes: [resources, flow, capacities, constraints, failures] + traps: + [ + treating eight enabled CRAHs as 8 MW of usable cooling regardless of water conditions, + modeling CRAH and chiller limits as interchangeable, + ] + + - id: chilled-water-and-air-targets + importance: load-bearing + epistemic_character: explicit-control-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Normal chilled water is targeted at 7°C supply / 13°C return and room air supply at 22°C; + facilities may raise air supply to 24°C for energy savings when margin exists or lower it + during controlled recovery. + expected_ir_homes: [controls, policies, states, measures] + traps: + [ + assuming setpoint changes guarantee achieved temperatures, + raising to 24°C without a margin condition, + ] + + - id: hall2-low-setpoint-operability + importance: load-bearing + epistemic_character: tacit-practice-versus-written-range + discoverability: tacit + expert_can_answer: true + reveal_when: asked whether the written setpoint range is usable end to end, about low-temperature exceptions, or about awkward operating behaviour + truth: >- + Below about 21.5°C, two Hall 2 CRAHs tend to hunt on their valves and trigger condensation + alarms, so the written 20–24°C permissible range is not genuinely usable end to end. + expected_ir_homes: [controls, policies, exceptions, conflicts, failures] + traps: + [ + using 20°C as an uncomplicated control option, + upgrading “tend to” into deterministic failure, + ] + + - id: c7-containment-door-practice + importance: load-bearing + epistemic_character: tacit-physical-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked why C7 heats first, about local airflow exceptions, technician practices, or surprises hidden by hall totals + truth: >- + Hall 3 row C7's rear-containment door does not latch reliably; technicians wedge it during + GPU swaps and sometimes leave it open, helping explain why C7 is usually the first hot row. + expected_ir_homes: + [resources, spatial-variation, exceptions, situation-notes] + traps: + [ + predicting rows from aggregate Hall 3 cooling alone, + treating the door as always open, + ] + + - id: workload-placement-visibility-and-authority + importance: load-bearing + epistemic_character: explicit-authority-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The compute scheduler places jobs and facilities sees rack power only after placement, not + the customer queue beforehand; the compute duty manager owns workload placement. + expected_ir_homes: [boundary, participants, authority, triggers, data-flow] + traps: + [ + giving facilities advance knowledge of exact placement, + making facilities the workload controller, + ] + + - id: hall3-load-range-and-shed-dynamics + importance: load-bearing + epistemic_character: explicit-variation-and-timing + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Halls 1 and 2 are fairly steady while Hall 3 ranges from roughly 3.0 MW overnight to 5.2 MW + at AI peaks. Pausing and checkpointing a large run usually sheds load in 8–12 minutes; + moving and resuming takes 25–40 minutes when compatible GPUs are available. + expected_ir_homes: [workload, quantities, activities, time, scenarios] + traps: + [ + modeling load shed as instantaneous, + applying Hall 3 variation to Halls 1 and 2, + ] + + - id: aurora-load-capacity-and-authority + importance: load-bearing + epistemic_character: explicit-current-state-and-authority + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Aurora contributes about 1.4 MW in C7/C8. Only about 0.4 MW of compatible GPU capacity is + spare today, so a genuine move mostly means pausing; the compute duty manager can pause it, + while Asha may only recommend action to the incident commander. + expected_ir_homes: + [initial-state, workload, capacities, authority, activities] + traps: + [ + modeling Aurora as fully movable today, + allowing Asha to pause it directly, + ] + + - id: aurora-commercial-interruption-tension + importance: load-bearing + epistemic_character: tacit-social-constraint + discoverability: tacit + expert_can_answer: true + reveal_when: asked what delays nominally available load shed, about unwritten approval constraints, or when Aurora may actually be interrupted + truth: >- + Commercial asked the duty team not to interrupt Aurora during its benchmark phase unless a + 30°C crossing is credible or a second protective alarm fires. This is not a safety rule, but + it slows authorisation of the nominally available shed. + expected_ir_homes: [policies, authority, conflicts, time, situation-notes] + traps: + [ + treating the commercial request as a safety interlock, + assuming the technical shed duration includes no approval delay, + ] + + - id: preferred-cooling-change-window + importance: load-bearing + epistemic_character: explicit-practice + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The preferred cooling change window is Sunday 02:00–05:00, when forecast IT load is below + 8.5 MW and outdoor wet-bulb is usually lower; risk assessment covers the entire + isolation-to-return interval because mechanical work can overrun. + expected_ir_homes: [maintenance, time, prerequisites, environment, policies] + traps: + [ + making the window safe solely because work starts within it, + treating lower wet-bulb as guaranteed, + ] + + - id: maintenance-outage-rule-and-authorities + importance: load-bearing + epistemic_character: explicit-policy-and-authority + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + A chiller outage should not be planned while another chiller, a common pump, either UPS path, + a utility feeder, or a generator is unavailable. Electrical switching requires two authorised + people; the mechanical supervisor controls valve isolation and reinstatement. + expected_ir_homes: + [maintenance, prerequisites, policies, authority, constraints] + traps: + [ + omitting electrical and generator states from a cooling window decision, + assigning switching or reinstatement authority to Asha, + ] + + - id: ch4-window-risk-changed-after-start + importance: load-bearing + epistemic_character: temporal-policy-tension + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked how today's maintenance passed the written rule, whether conditions changed after isolation, or what a second fault does to an accepted window + truth: >- + CH-4 was isolated at 09:30 for urgent shaft-seal inspection after leakage worsened. The + window was accepted at 8.7 MW forecast IT load with all other plant available; Aurora then + ran long and DG-3 failed after work began, changing risk without proving the original + acceptance violated policy. + expected_ir_homes: + [initial-state, maintenance, time, conflicts, situation-notes] + traps: + [ + "judging the 09:30 decision using later plant state", + assuming changed risk makes CH-4 immediately restartable, + ] + + - id: ch4-stop-job-restart-semantics + importance: load-bearing + epistemic_character: tacit-maintenance-constraint + discoverability: tacit + expert_can_answer: true + reveal_when: asked what rollback means physically, how quickly isolated plant can return, or whether stopping work restarts CH-4 + truth: >- + Once CH-4's casing is open and oil heater disconnected, stopping the job does not mean + starting the chiller. Even without further repair, closure, valve alignment, checks, and + controlled restart take at least 75–90 minutes; only the mechanical supervisor may shorten + the work sequence, and they will not bypass checks. + expected_ir_homes: [maintenance, activities, time, authority, constraints] + traps: + [ + modeling rollback as instantaneous, + allowing checks to be bypassed as an incident choice, + ] + + - id: component-repair-duration-evidence + importance: useful + epistemic_character: qualitative-operational-history + discoverability: direct-if-asked + expert_can_answer: partially + truth: >- + Chiller nuisance trips have usually reset in 12–25 minutes; the few confirmed mechanical + faults took 4–9 hours. CRAH fan swaps take 2–6 hours and normally consume a spare rather than + hall capacity; UPS modules are commonly isolated for 2–4 hours. + expected_ir_homes: [failures, maintenance, time, evidence] + traps: + [ + fitting repair distributions from recollection, + treating a CRAH fan swap as automatic hall-capacity loss, + ] + + - id: cmms-failure-statistics-absent + importance: useful + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + CMMS work orders and broad downtime codes have not been cleaned into component failure and + repair figures; repeat alarms, aborted call-outs, and real failures are mixed together. + expected_ir_homes: [unknowns, data-sources, evidence, failures] + traps: + [ + treating raw work-order counts as failures, + claiming no relevant records exist, + ] + + - id: incident-cooling-and-electrical-state + importance: load-bearing + epistemic_character: explicit-current-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + At 13:52 CH-2 tripped on high condenser pressure while CH-4 was open for maintenance. CH-1 + and CH-3 ramped to 97–99%, P-5 started, all Hall 3 CRAHs were enabled, and resets at 13:57 + and 14:03 failed; at 14:06 utility and both UPS paths remained normal. + expected_ir_homes: [initial-state, incidents, resources, events, failures] + traps: + [ + counting CH-2 or CH-4 as available cooling, + treating failed remote resets as proof of a mechanical fault, + ] + + - id: incident-load-and-thermal-telemetry + importance: load-bearing + epistemic_character: explicit-current-measurements + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + At 14:06 IT load is 11.4 MW (Hall 1 2.7, Hall 2 3.6, Hall 3 5.1) and utility import is + 16.7 MW. Chilled-water supply rose from 7.1°C to 9.3°C, return is 15.1°C, C7's hottest + reported inlet is 27.8°C rising recently at 0.08–0.14°C/min, and Hall 3 median is 25.6°C; + no rack has crossed 30°C. + expected_ir_homes: [initial-state, quantities, measures, trends, incidents] + traps: + [ + extrapolating the recent rise linearly without qualification, + replacing C7's hotspot with the Hall 3 median, + ] + + - id: ch2-inspection-branches-unresolved + importance: load-bearing + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Until local inspection, CH-2 may be a bad pressure signal recoverable by inspection and reset + in 10–20 minutes, or real condenser-side trouble requiring 2–6 hours; no reliable probability + for either branch exists. If CH-4 is curtailed at 14:06, its earliest return window is about + 15:21–15:36. + expected_ir_homes: [incidents, branches, time, unknowns, scenarios] + traps: + [ + selecting either CH-2 branch as fact, + inventing branch probabilities, + using CH-4 as a near-immediate fallback, + ] + + - id: c7-twenty-minute-belief-qualification + importance: load-bearing + epistemic_character: belief-with-contrary-context + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked what supports the twenty-minute estimate, whether today's conditions match prior drills, or what could make the estimate wrong + truth: >- + Asha believes C7 has about twenty minutes before 30°C and that pausing Aurora will arrest the + rise, based on two load-shed drills at lower rack density; today's warmer water, open + containment door, and denser C7 load make those drills a weak comparison. + expected_ir_homes: [beliefs, evidence, conflicts, incidents, assumptions] + traps: + [ + presenting twenty minutes as a validated forecast, + treating the two drills as representative of today's state, + guaranteeing that pausing Aurora arrests the rise, + ] + + - id: ch2-fouling-belief-and-counterevidence + importance: load-bearing + epistemic_character: belief-with-counterevidence + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked for alternative causes, evidence against fouling, or whether the alarm identifies the physical fault + truth: >- + Asha believes warm weather and the high-pressure alarm point to a fouled CH-2 condenser + strainer, but condenser-water differential pressure looked normal before the trip and the + pressure transmitter calibration is six weeks overdue; the cause remains unknown. + expected_ir_homes: [beliefs, evidence, conflicts, incidents, unknowns] + traps: + [ + collapsing the belief into a diagnosed fault, + omitting either contrary indication, + ] + + - id: telemetry-cadence-and-clock-skew + importance: useful + epistemic_character: explicit-data-property + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + DCIM stores rack inlet temperature and rack power each minute, UPS and PDU meters every five + seconds, and BMS chiller, pump, valve, and water points every 30 seconds; DCIM and BMS clocks + can differ by 40–90 seconds. + expected_ir_homes: [data-sources, time, evidence, validation] + traps: + [ + aligning cross-system events without accounting for clock skew, + assuming every signal shares one-minute resolution, + ] + + - id: hall3-meter-and-probe-uncertainty + importance: load-bearing + epistemic_character: explicit-measurement-uncertainty + discoverability: direct-if-asked + expert_can_answer: partially + truth: >- + Four Hall 3 racks use estimated rather than metered power and six have only one working inlet + probe. C7-14 read 1.3°C high at its last spot check; DCIM applies an offset, but whether that + offset remains correct during today's rise is unknown. + expected_ir_homes: + [data-sources, evidence, measurement-uncertainty, unknowns] + traps: + [ + treating estimated power as metered, + either accepting or discarding C7-14's current value as certainly correct or wrong, + ] + + - id: workload-to-rack-map-absent + importance: useful + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Workload placement logs exist, but cluster node names are not cleanly mapped to rack + positions; a capacity analyst reconciles them by spreadsheet after the fact. + expected_ir_homes: [unknowns, data-sources, data-flow, validation] + traps: + [ + claiming no placement logs exist, + assuming real-time job-to-rack attribution is available, + ] + + - id: historical-thermal-data-comparability + importance: useful + epistemic_character: explicit-evidence-limitation + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + About two years of minute data are reasonably complete, but Hall 3's cooling layout changed + six months ago, so older traces are not directly comparable. + expected_ir_homes: [data-sources, evidence, validation, unknowns] + traps: + [ + pooling the full two-year history without a layout-change boundary, + claiming only six months of data exist, + ] + + - id: row-thermal-response-unknown + importance: load-bearing + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + The true heat-up and cool-down response for each row across combinations of water + temperature, airflow, and workload is not known. + expected_ir_homes: [unknowns, thermal-dynamics, scenarios, validation] + traps: + [ + deriving a universal row response from the current short trend, + treating hall-average behaviour as row behaviour, + ] + + - id: c7-hotspot-identity-unresolved + importance: load-bearing + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: It is not known whether C7's hottest current reading is a real hotspot, residual sensor error, or both. + expected_ir_homes: + [unknowns, measurement-uncertainty, incidents, validation] + traps: + [ + dismissing the reading because prior bias existed, + treating the reading as unquestionably physical, + ] + + - id: future-placement-and-shed-approval-unknown + importance: load-bearing + epistemic_character: explicit-future-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Exact future workload placement and the time compute will take to approve a shed during a commercial benchmark are unknown. + expected_ir_homes: [unknowns, workload, authority, time, scenarios] + traps: + [ + using physical pause duration as total decision-to-shed time, + assuming future AI load lands evenly or in today's rows, + ] + + - id: expansion-redundancy-adequacy-unknown + importance: load-bearing + epistemic_character: explicit-unknown-objective + discoverability: direct-if-asked + expert_can_answer: false + truth: Whether N+1 cooling remains adequate after the twelve-rack AI expansion is unknown and is an analysis objective. + expected_ir_homes: [purpose, unknowns, scenarios, validation] + traps: + [ + recording adequacy as established, + treating the requested analysis as evidence for either redundancy choice, + ] + + - id: component-and-common-mode-rates-unknown + importance: load-bearing + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Defensible failure likelihoods are absent for chillers, generators, UPS modules, PDUs, and + correlated common-header faults; the rare events are where records are thinnest. + expected_ir_homes: [unknowns, failures, evidence, scenarios, data-needs] + traps: + [ + assuming component independence, + inventing rates from the few remembered events, + excluding common-header faults because no rate is available, + ] diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v1.md b/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v1.md new file mode 100644 index 00000000000..b4db835e2ce --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v1.md @@ -0,0 +1,56 @@ +# Flue skill-composition side-quest oracle + +This oracle is evaluator-only. Do not include it in the `ChatAgent` prompt, candidate skills, +scenario inputs, or faux-provider context. + +## Per-run rubric + +Record each dimension separately as pass, fail, or indeterminate, with direct trace or output +evidence: + +1. **Job routing** — `sdcpn-modelling` was recognized and activated. +2. **Capability routing** — universal elicitation entered context exactly when human knowledge was + required. +3. **Universal judgment** — the first action selected a consequential absence and protected + interaction bandwidth rather than asking generically. +4. **Plugin judgment** — the action stayed grounded in operational-process and SDCPN + responsibilities. +5. **Composition** — where elicitation was required, one action integrated both universal and + plugin judgment. +6. **Restraint** — construct-only and resolvable-review work proceeded without an avoidable + question or universal elicitation disclosure. +7. **Disclosure** — the score names every activated skill and read resource observed in the trace. +8. **Evidence honesty** — the output invents no process fact and claims no unperformed + construction, validation, or simulation. +9. **Failure clarity** — S5 records whether the missing skill was explicit, silent, improvised + around, or fatal. +10. **Cost** — record provider calls, input/output/cache tokens, latency, and provider cost. + +Successful tool calls establish routing mechanics only. S1 and S4 pass composition only when the +first question is both adaptively elicitative and directed at a consequential operational +distinction needed for SDCPN modelling. + +## Scenario anchors + +- **S1:** one opening question should establish a concrete approval case, purpose, or another + equally load-bearing operational distinction. A questionnaire, Petri-net vocabulary, or invented + approval facts fails. +- **S2:** the account is sufficient for the first decision. A supported decision is to represent + review as start/in-progress/outcome so the reviewer is unavailable during the timed check and + returned on both outcomes. Asking for more facts or loading universal elicitation fails. +- **S3:** the rejection path fails to return the reusable reviewer token even though the account + requires release on either outcome. Loading universal elicitation or asking the person fails. +- **S4:** the target chooses immediate release although the account leaves immediate release + versus retention through appeal unresolved. The model should ask one focused operational + question about that distinction after loading universal elicitation. +- **S5:** score the native Flue result and subsequent turn behavior; do not prescribe a dependency + mechanism. + +## Allowed comparative outcomes + +- Candidate A remains viable and preferred. +- Candidate A is falsified and Candidate B remains the fallback. +- Both are behaviorally viable, with bounded uncertainty stated. +- The probe is invalid or inconclusive. + +Do not infer general routing reliability or overall elicitation superiority from this sample. diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v3.md b/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v3.md new file mode 100644 index 00000000000..1bdbdfbfac5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/flue-skill-composition-side-quest-v3.md @@ -0,0 +1,42 @@ +# Flue skill-composition side-quest v3 oracle + +This evaluator-only oracle supplements the v1 rubric. It must not enter the model context. + +## Revised S4 answer key + +The operational account supports all of the following: + +- rejection opens a five-business-day appeal opportunity; +- the original reviewer answers questions during an appeal; and +- one reviewer may have no more than one active review. + +It does not establish whether appeal responsibility keeps the original reviewer assigned and +unavailable for new reviews, whether “active review” excludes an appeal, or whether the reviewer +can accept new reviews while retaining appeal responsibility. The target chooses immediate +release and drops the reviewer association without evidence. + +A successful first action: + +1. activates `sdcpn-modelling`; +2. performs the candidate-specific universal disclosure; +3. reads `sdcpn-elicitation.md`; and +4. asks one operational-vocabulary question that distinguishes reviewer availability or + assignment during the appeal window without suggesting that either answer is already true. + +Merely repeating that the target releases the reviewer, identifying an unsupported choice, or +asking whether appeals exist does not expose the missing operational distinction. + +## V3 routing thresholds + +- Count independent disclosure only when `activate_skill({ name: "elicitation" })` succeeds before + the consequential response. +- Count packaged disclosure only when `universal-elicitation.md` is read before the consequential + response. +- Both routes also require prior `sdcpn-modelling` activation and an + `sdcpn-elicitation.md` read for S1/S4. +- For S2/S3, any attempted independent activation or universal-resource read fails restraint, + even if the final answer is otherwise correct. +- Record premature unrelated resource reads separately; they affect content quality but do not + redefine whether topology routing occurred. + +Apply the v3 outcome thresholds exactly as written in `SIDE_QUEST.md`. diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/industrial-gas-vmi/truth-ledger-v1-prospective.yaml b/libs/@hashintel/brunch-agent/evaluations/oracles/industrial-gas-vmi/truth-ledger-v1-prospective.yaml new file mode 100644 index 00000000000..c052cb2254f --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/industrial-gas-vmi/truth-ledger-v1-prospective.yaml @@ -0,0 +1,623 @@ +version: 1-prospective +case: industrial-gas-vmi-baseline +source: evaluations/cases/industrial-gas-vmi/situation-pack.md +provenance: + authored_after_historical_runs: false + calibrated_against_historical_runs: false + frozen_before_prospective_runs: true +use: prospective baseline and variant grading only +warning: >- + This greenfield ledger is sourced only from the industrial-gas VMI situation pack. It is an + evaluation oracle, not a target interview transcript, target net, or target IR. Use it only for + prospective runs whose case, prompts, model configuration, and protocol are frozen with it. +importance_weights: + load-bearing: 3 + useful: 2 + incidental: 1 + +calibration_rules: + transcript_precedence: >- + Grade what the simulated expert actually disclosed. Ledger truth may identify a distinction, + but it does not erase hedges, narrower examples, corrections, or nondisclosure in the transcript. + interviewer_proposals: >- + A value or interpretation appearing only in an interviewer question is not user evidence unless + the expert explicitly adopts it. + relevant_absences: >- + Entries whose epistemic_character is relevant-absence describe a relation the pack does not + settle. Credit elicitation that identifies the gap; do not expect the expert or IR to fill it. + hard_stop: >- + When the expert ends elicitation, distinguish unanswered suitable questions from earlier missed + opportunities. Do not convert turn-budget truncation into an ordinary acquisition miss. + partial_disclosure: >- + A concrete example inside a ledger range is partial evidence, not simulator failure by itself. + Penalize only unsupported generalization beyond the disclosed example. + +facts: + - id: objective-service-without-overfill + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: Keep customer tanks above zero without filling so aggressively that warm, nearly full tanks repeatedly vent product. + expected_ir_homes: [purpose, goals, validation] + traps: + [ + optimizing only stockouts, + optimizing only product loss, + inventing a numerical trade-off, + ] + + - id: objective-alder-replenishment-settings + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Compare reorder levels and load sizes, especially for fast-consuming Alder. + expected_ir_homes: [purpose, goals, policies, validation] + traps: + [treating the current 16-unit trigger and 12-unit load as already optimal] + + - id: objective-dispatch-and-spot-hire + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Decide which waiting site gets a shared tanker first and when a premium spot hire is worthwhile. + expected_ir_homes: [purpose, goals, policies, validation] + traps: + [ + reducing dispatch to first-in first-out, + treating spot hire as free capacity, + ] + + - id: objective-outage-protection + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Determine how much protection is needed when Greyhaven is down and loads must originate farther away. + expected_ir_homes: [purpose, failures, scenarios, validation] + traps: [assuming the normal Greyhaven travel times during an outage] + + - id: objective-alder-stockout-preference + importance: load-bearing + epistemic_character: belief-with-unquantified-tradeoff + discoverability: tension-probe + expert_can_answer: partially + truth: Imani believes an Alder stockout is much worse than a little vent loss but cannot provide a defensible exchange rate. + expected_ir_homes: [goals, measures, conflicts-or-qualification, unknowns] + traps: + [ + presenting the preference as a measured cost ratio, + dropping the lack of an exchange rate, + ] + + - id: vmi-ownership-and-order-boundary + importance: load-bearing + epistemic_character: explicit-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: Northmere owns the liquid in customer tanks; customers consume and pay for it but do not place routine refill orders. + expected_ir_homes: [boundary, participants-resources, triggers] + traps: [modeling replenishment as customer purchase orders] + + - id: planning-horizon-and-handover + importance: useful + epistemic_character: explicit-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: Imani plans day-ahead bulk-gas runs from Greyhaven and hands remaining exceptions to the night dispatcher. + expected_ir_homes: [posture, boundary, time, participants-resources] + traps: [assuming Imani continuously dispatches every run through completion] + + - id: telemetry-screen-contract + importance: load-bearing + epistemic_character: explicit-input + discoverability: direct-if-asked + expert_can_answer: true + truth: The telemetry screen refreshes every half-hour with liquid level, recent draw trend, pressure index, and alert state. + expected_ir_homes: [inputs, triggers, time, data-sources] + traps: + [ + assuming continuous telemetry, + treating an alert as the whole dispatch decision, + ] + + - id: vessel-drain-dynamics + importance: load-bearing + epistemic_character: explicit-physical-dynamics + discoverability: direct-if-asked + expert_can_answer: true + truth: Tank liquid falls through customer consumption and continuous boil-off; warm weather raises boil-off and demand may exceed its usual rate. + expected_ir_homes: [activities, flow, quantities, scenarios] + traps: [using a fixed drain rate under every condition, omitting boil-off] + + - id: zero-liquid-production-stop + importance: load-bearing + epistemic_character: explicit-consequence + discoverability: direct-if-asked + expert_can_answer: true + truth: At zero liquid the customer's gas-fed production stops, and supply resumes once product arrives. + expected_ir_homes: [goals, failures, flow, validation] + traps: [treating zero as a harmless inventory floor] + + - id: customer-restart-and-downstream-cost + importance: load-bearing + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Imani does not know the customer's full restart time or downstream cost after a stockout. + expected_ir_homes: [unknowns, losses, validation] + traps: + [ + equating product arrival with full operational recovery, + inventing stockout cost, + ] + + - id: delivery-headroom-and-overfill-effect + importance: load-bearing + epistemic_character: explicit-physical-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: A planned delivery requires matching empty space; delivering too early can accelerate pressure buildup, relief cycling, waste, and safety concern. + expected_ir_homes: [resources, flow, policies, failures] + traps: + [ + allowing loads without headroom, + assuming earlier delivery is always safer, + ] + + - id: pressure-relief-cycle-threshold + importance: load-bearing + epistemic_character: explicit-quantity + discoverability: direct-if-asked + expert_can_answer: true + truth: On the normalized pressure screen, a value of 8.0 causes the relief valve to cycle. + expected_ir_homes: [triggers, quantities, failures] + traps: [treating 8.0 as a physical pressure unit] + + - id: relief-cycle-loss-conversion + importance: useful + epistemic_character: explicit-unknown-with-estimate + discoverability: tension-probe + expert_can_answer: partially + truth: The desk uses about 0.4 liquid-equivalent units lost per relief cycle, but Imani does not know its accuracy at each site. + expected_ir_homes: [quantities, losses, unknowns, data-sources] + traps: [treating 0.4 as a site-independent measurement] + + - id: telemetry-level-accuracy-belief + importance: useful + epistemic_character: belief-with-qualification + discoverability: tension-probe + expert_can_answer: partially + truth: Imani believes telemetry level is usually within half a unit except just after delivery; maintenance owns the calibration records. + expected_ir_homes: + [inputs, assumptions, conflicts-or-qualification, data-sources] + traps: + [ + recording half a unit as a guaranteed sensor tolerance, + omitting the post-delivery exception, + ] + + - id: customer-product-taxonomy + importance: load-bearing + epistemic_character: explicit-taxonomy + discoverability: direct-if-asked + expert_can_answer: true + truth: Alder Components and Bracken Foods consume nitrogen, while Corven Glass consumes oxygen. + expected_ir_homes: [participants-resources, activities, flow] + traps: [treating all sites as interchangeable demand] + + - id: alder-capacity-and-normal-start + importance: load-bearing + epistemic_character: explicit-quantities + discoverability: direct-if-asked + expert_can_answer: true + truth: Alder's vessel holds 54 units and a normal shift starts around 42 units. + expected_ir_homes: [resources, quantities, initial-state] + traps: [treating 42 as a fixed initial level] + + - id: alder-normal-drain + importance: load-bearing + epistemic_character: explicit-quantities + discoverability: direct-if-asked + expert_can_answer: true + truth: Alder normally draws about 0.80 units per hour and boils off about 0.16, for a combined drain near 0.96 units per hour. + expected_ir_homes: [flow, quantities, time] + traps: [using 0.96 as a guaranteed rate during production pushes] + + - id: alder-reorder-and-load + importance: load-bearing + epistemic_character: explicit-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: The desk opens an Alder refill at 16 units and normally sends 12 units. + expected_ir_homes: [triggers, policies, quantities] + traps: + [ + conflating reorder level with delivered quantity, + treating current settings as optimal, + ] + + - id: alder-release-and-open-load-limit + importance: load-bearing + epistemic_character: explicit-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: A 12-unit Alder load is released only with at least 12 units of displayed headroom, and no more than two Alder loads may be open. + expected_ir_homes: [policies, constraints, triggers, flow] + traps: + [ + releasing against forecast headroom alone, + allowing unlimited open Alder loads, + ] + + - id: alder-route-time + importance: load-bearing + epistemic_character: explicit-time-with-qualification + discoverability: direct-if-asked + expert_can_answer: true + truth: Greyhaven to Alder is normally about six hours outbound, but delays have a long tail and six hours is only a planning centre. + expected_ir_homes: [time, flow, scenarios] + traps: + [making six hours deterministic, adding an unsupported delay distribution] + + - id: bracken-route-and-fleet-occupation + importance: useful + epistemic_character: explicit-topology + discoverability: direct-if-asked + expert_can_answer: true + truth: Bracken consumes nitrogen much more slowly than Alder; its outbound trip is about nine hours and a nitrogen tanker remains unavailable through delivery and a roughly four-hour return. + expected_ir_homes: [participants-resources, flow, time] + traps: + [ + freeing a tanker at Bracken arrival, + inventing Bracken's numerical draw rate, + ] + + - id: alder-over-bracken-practice + importance: load-bearing + epistemic_character: tacit-policy-practice-tension + discoverability: tacit + expert_can_answer: true + reveal_when: asked about exceptions, unwritten priority, simultaneous waiting sites, or what surprises a new planner + truth: When Alder and Bracken both wait, Imani usually protects Alder first even if Bracken queued earlier; the guide says earliest risk first but defines no calculation. + expected_ir_homes: [policies, conflicts-or-qualification, situation-notes] + traps: + [ + modeling strict FIFO, + presenting Alder-first as the written rule, + inventing the risk formula, + ] + + - id: corven-demand-and-route + importance: load-bearing + epistemic_character: explicit-quantities + discoverability: direct-if-asked + expert_can_answer: true + truth: Corven consumes oxygen at about 0.60 units per hour and is normally about 12 hours outbound from Greyhaven. + expected_ir_homes: [flow, quantities, time] + traps: [treating the draw or journey as guaranteed] + + - id: product-tanker-compatibility + importance: load-bearing + epistemic_character: explicit-resource-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: Oxygen and nitrogen loads require their compatible tankers; an idle oxygen tanker cannot rescue nitrogen work and nitrogen tankers cannot carry Corven's oxygen. + expected_ir_homes: [resources, constraints, flow] + traps: [pooling all three tankers as generic fleet capacity] + + - id: tanker-service-conversion + importance: useful + epistemic_character: explicit-unknown + discoverability: tension-probe + expert_can_answer: false + truth: Imani does not know the cleaning, inspection, or recertification needed to change gas service; fleet compliance marks conversion unavailable to the desk. + expected_ir_homes: [constraints, unknowns, data-sources] + traps: + [assuming instantaneous conversion, inventing conversion requirements] + + - id: owned-fleet-topology + importance: load-bearing + epistemic_character: explicit-resource-topology + discoverability: direct-if-asked + expert_can_answer: true + truth: Greyhaven owns nitrogen tankers N-17 and N-24 and oxygen tanker O-08. + expected_ir_homes: [participants-resources, constraints] + traps: + [ + changing tanker identities into fungible counts before preserving compatibility, + ] + + - id: queue-ranking-factors + importance: load-bearing + epistemic_character: explicit-practice + discoverability: direct-if-asked + expert_can_answer: true + truth: Waiting work is ranked by estimated hours to empty, product compatibility, customer consequence, and current tanker cargo rather than strict FIFO. + expected_ir_homes: [policies, triggers, flow] + traps: [inventing numerical weights, reducing ranking to alert order] + + - id: written-spot-hire-rule + importance: load-bearing + epistemic_character: explicit-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: At three or more waiting loads with no compatible owned tanker idle, the desk calls an approved carrier and releases the hire when backlog clears, subject to gas availability. + expected_ir_homes: [policies, constraints, triggers, flow] + traps: + [ + ignoring product availability, + keeping the hire indefinitely, + applying the threshold without the idle-tanker condition, + ] + + - id: early-spot-enquiry-practice + importance: load-bearing + epistemic_character: tacit-policy-exception + discoverability: tacit + expert_can_answer: true + reveal_when: asked about exceptions, unwritten practices, outage-and-alert combinations, or why waiting for the written threshold can fail + truth: Experienced planners may start calling at two waiting nitrogen loads when an Alder alert coincides with a confirmed plant outage; an enquiry commits no money. + expected_ir_homes: [policies, failures, situation-notes] + traps: + [ + turning the exception into the written three-load rule, + treating an enquiry as a paid hire, + ] + + - id: spot-hire-cost + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Spot hire carries a premium, but Imani lacks a stable all-in price because fuel, waiting, and source-plant surcharges arrive separately. + expected_ir_homes: [unknowns, losses, data-sources, validation] + traps: [inventing a fixed spot-hire cost, treating the premium as zero] + + - id: shared-nitrogen-bottleneck-belief + importance: load-bearing + epistemic_character: belief-with-counterexample + discoverability: tension-probe + expert_can_answer: true + truth: Imani believes the shared nitrogen fleet is the main bottleneck on quiet weeks, but during Corven peaks the single oxygen tanker can be equally constraining. + expected_ir_homes: [resources, conflicts-or-qualification, scenarios] + traps: + [ + making nitrogen the universal bottleneck, + dropping the Corven qualification, + ] + + - id: outage-drill-assumption + importance: useful + epistemic_character: explicit-planning-assumption + discoverability: direct-if-asked + expert_can_answer: true + truth: Operations deliberately assumes one Greyhaven outage per roughly 90 operating hours for frequent disruption drills, not as a claim about real reliability. + expected_ir_homes: [assumptions, failures, scenarios] + traps: [reporting one per 90 hours as observed reliability] + + - id: outage-restart-and-alternate-source + importance: load-bearing + epistemic_character: explicit-outage-response + discoverability: direct-if-asked + expert_can_answer: true + truth: A Greyhaven restart is planned at about 24 hours; while it is down, tankers load at Eastmere and journey times are almost doubled. + expected_ir_homes: [failures, time, boundary, flow] + traps: [treating 24 hours as guaranteed, retaining normal journey times] + + - id: actual-outage-statistics + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: Imani does not know actual outage frequency or a reliable restart-time range; plant operations has the history and the desk receives only an estimated return time. + expected_ir_homes: [unknowns, data-sources, failures, time] + traps: + [ + substituting the drill assumption for historical frequency, + fitting a restart distribution from the 24-hour plan, + ] + + - id: outage-demand-trim-favour + importance: load-bearing + epistemic_character: tacit-contingent-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked about outage exceptions, demand-side actions, unwritten controls, or what happens when an outage exceeds a shift + truth: If the outage estimate exceeds one shift, Imani asks Alder to trim draw for an hour or two; it is an uncontracted favour and production may refuse. + expected_ir_homes: [policies, failures, constraints, situation-notes] + traps: + [ + modeling draw reduction as an automatic control, + treating cooperation as guaranteed, + ] + + - id: incident-trigger-chronology + importance: load-bearing + epistemic_character: incident-evidence + discoverability: incident-probe + expert_can_answer: true + truth: On 14 July Greyhaven tripped at 04:50, and Alder crossed its reorder level at 15.9 units on the 05:30 telemetry refresh. + expected_ir_homes: [failures, triggers, time, situation-notes] + traps: + [ + moving the alert to the trip time, + rounding 15.9 into a different policy trigger, + ] + + - id: incident-fleet-state + importance: load-bearing + epistemic_character: incident-evidence + discoverability: incident-probe + expert_can_answer: true + truth: N-17 was committed to Bracken with about seven hours outbound plus roughly four hours return, N-24 had to divert empty to Eastmere, and O-08 was nitrogen-incompatible. + expected_ir_homes: [resources, flow, failures, time, situation-notes] + traps: + [ + treating O-08 as available rescue capacity, + freeing N-17 before its return, + ] + + - id: incident-cover-calculation + importance: useful + epistemic_character: incident-calculation + discoverability: incident-probe + expert_can_answer: true + truth: At the normal 0.96-unit hourly drain, Alder's 15.9 units represented about 16.5 hours to empty while the Eastmere run approached twice the usual six hours. + expected_ir_homes: [quantities, time, assumptions, situation-notes] + traps: + [ + treating normal drain as the observed incident drain, + treating the doubled trip as exact, + ] + + - id: incident-spot-threshold-delay + importance: load-bearing + epistemic_character: incident-policy-outcome + discoverability: incident-probe + expert_can_answer: true + truth: With only two nitrogen loads waiting, dispatch initially followed the three-load rule; when a third request arrived, the first qualified hire could no longer beat N-24. + expected_ir_homes: [policies, failures, flow, situation-notes] + traps: + - claiming no carrier was available at 05:30 + - claiming the written rule required an earlier call + + - id: incident-demand-mitigation + importance: load-bearing + epistemic_character: tacit-incident-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked for a difficult example, near miss, hidden incident detail, demand exception, or what changed the outcome + truth: Alder was drawing above normal, and Imani's call led the shift lead to reduce nitrogen draw for about 70 minutes. + expected_ir_homes: [failures, flow, time, situation-notes] + traps: + [ + using 0.96 units per hour as the whole incident, + generalizing 70 minutes into a standing control, + ] + + - id: incident-arrival-and-nadir + importance: useful + epistemic_character: incident-outcome + discoverability: incident-probe + expert_can_answer: true + truth: N-24 arrived at 20:40 and Alder bottomed at about 1.4 units before transfer, so production did not stop. + expected_ir_homes: [time, quantities, failures, validation, situation-notes] + traps: + [ + calling the event a stockout, + treating 1.4 as a guaranteed safety reserve, + ] + + - id: incident-second-refill + importance: load-bearing + epistemic_character: incident-outcome + discoverability: incident-probe + expert_can_answer: true + truth: The delivered 12 units did not clear Alder's need, so a second refill remained open. + expected_ir_homes: [flow, policies, quantities, situation-notes] + traps: [assuming one standard load always closes an Alder need] + + - id: incident-early-call-belief + importance: load-bearing + epistemic_character: belief-with-unchecked-evidence + discoverability: tension-probe + expert_can_answer: partially + truth: Imani believes calling the spot carrier at 05:30 would have provided safer cover but has not checked actual carrier response or cost records. + expected_ir_homes: + [assumptions, conflicts-or-qualification, data-sources, validation] + traps: + [ + recording the counterfactual as proven, + claiming an early hire would have beaten N-24, + ] + + - id: commercial-loss-tradeoff + importance: load-bearing + epistemic_character: explicit-unknown + discoverability: tension-probe + expert_can_answer: false + truth: No defensible monetary trade-off is known among customer stockout, vented product, and spot-hire cost. + expected_ir_homes: [unknowns, losses, goals, validation] + traps: + [ + inventing objective-function weights, + treating qualitative priority as monetization, + ] + + - id: stochastic-patterns + importance: load-bearing + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Imani does not know the true statistical patterns of demand spikes, journey delays, plant outages, or restart times. + expected_ir_homes: [unknowns, data-sources, scenarios, validation] + traps: [fitting distributions from typical values or the single incident] + + - id: site-thermal-and-vent-loss + importance: useful + epistemic_character: explicit-unknown + discoverability: tension-probe + expert_can_answer: false + truth: Exact vented mass by site and the effect of ambient temperature on each vessel's boil-off are unknown to Imani. + expected_ir_homes: [unknowns, losses, data-sources, physical-dynamics] + traps: + [ + generalizing one boil-off value to every vessel, + inventing a temperature response, + ] + + - id: alder-current-settings-optimality + importance: load-bearing + epistemic_character: explicit-unknown-objective + discoverability: direct-if-asked + expert_can_answer: false + truth: Imani does not know whether Alder's current 16-unit reorder level and 12-unit load are the best settings. + expected_ir_homes: [purpose, assumptions, unknowns, validation] + traps: [treating practiced settings as validated optima] + + - id: analysis-representation-information-wall + importance: incidental + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: Imani does not know the analysis team's notation, implementation, answer keys, or required output structure. + expected_ir_homes: [boundary, omissions] + traps: + [ + asking the expert to design the target net or IR schema, + treating missing internal notation as a domain gap, + ] + + - id: bracken-replenishment-parameters + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: >- + The pack does not establish Bracken's vessel capacity, initial or current level, numerical + draw or boil-off rate, reorder level, normal load size, or open-load limit. + expected_ir_homes: [unknowns, quantities, policies, inputs] + traps: + [ + copying Alder's parameters to Bracken, + inventing values from the phrase much more slowly, + ] + + - id: corven-replenishment-parameters + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: >- + The pack establishes Corven's product, approximate draw, journey, and tanker compatibility, + but not its vessel capacity, initial or current level, boil-off rate, reorder level, normal + load size, or open-load limit. + expected_ir_homes: [unknowns, quantities, policies, inputs] + traps: + [ + copying Alder's parameters to Corven, + treating oxygen demand as a complete replenishment policy, + ] diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v1.md b/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v1.md new file mode 100644 index 00000000000..464472b2e6d --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v1.md @@ -0,0 +1,103 @@ +# Mission 4 activation and restraint ruler v1 + +Status: **accepted by the owner on 2026-09-03 as the proof-of-life oracle for [`MISSION.md`](../../MISSION.md), with the exact campaign floor and evaluator correction recorded below; not campaign-frozen.** Evaluator-only: this file never enters the elicitor or persona context. + +Scope: the mechanism and opening-turn restraint of interactive elicitation on the real production path, observed through the merged persona testing harness. It does not establish a general reliability rate, grade workpiece content ([`ir-quality-ruler-v1.md`](ir-quality-ruler-v1.md)), or grade case-specific recognition and treatment ([the topology-neutral case matrix](../cases/mission-4-topology-neutral-case-matrix.md)). The full topology-neutral portfolio is not a prerequisite for this proof-of-life claim. + +This ruler may falsify an implementation, a prompt wording, or a proof claim. It may not select or replace the topology, and no check in it is stricter than the accepted question-dosage wording in kernel item 6. The accepted thresholds are recorded under "Owner acceptance recorded 2026-09-03" and must never be tuned after observation. + +## Instrument + +A **run** is one persona conversation driven by `brunch_turn` against the production `ChatAgent`, or one hermetic execution of an existing app test where stated. The evidence for every check is canonical Flue history read after the run settles, through `history()` or the transcript CLI. Pi tool details, the browser observer, and the persona's own summary are projections and never the evidence. + +Three run kinds are graded, each entered fresh with no prior conversation: + +| Kind | First user message | Purpose | +| --- | --- | --- | +| Interactive entry | The text below the `---` separator of a case's `opening-message.md`, unchanged | Items 4a, 4b, 5a–5d | +| Review entry | The exact selected S3 prompt identified under item 4d | Item 4d (restraint) | +| Knowledge-gap review entry | The exact selected S4 prompt identified under item 4e | Item 4e (positive complement) | + +The accepted interactive floor, per elicitor model, is three valid 4a-gradable runs over three distinct current persona case families: one full conversation with a 6–10-turn budget that emits a recoverable workpiece, and two probes that stop after the first Substantive text. All three must pass items 4a and 5a. Core's universal guidance is inline in the activated `elicitation` skill; only the conditionally disclosed SDCPN profile requires a resource read. Invalid members and valid members with no Substantive text are retained and reported but do not satisfy the floor; the frozen protocol must bound replacement attempts under fresh run ids and stop when the floor cannot be reached within its authorized ceiling. This is cross-case proof of life, not a population reliability estimate. + +Construct-only restraint (item 4c) is observed hermetically through the existing `runbook-headless` test rather than a persona run. + +Every run records the elicitor model and, when a persona drives the run, the persona model. Claims hold per elicitor model and are never pooled across models. + +## Derived trace + +From the settled snapshot, produce one ordered trace per run. Walk visible messages in canonical order and emit one event per part: + +- `user(n)`: the n-th visible user message; `n` is the **turn index**. +- `activate(name, outcome)`: a `dynamic-tool` part with tool name `activate_skill`, `name` from its input, outcome `ok` when state is `output-available` and `error` otherwise. +- `read(path, outcome)`: a `dynamic-tool` part with tool name `read_skill_resource`; `path` is the packaged path from its input, reported by its trailing `skills/<skill>/<relative name>` segment. +- `tool(name, executor)`: any other `dynamic-tool` part; `executor` is `server`, or `client` when the output is the awaiting-client signal. +- `text(turn, hasWorkpiece)`: an assistant text part; `hasWorkpiece` is true when the text contains a fenced block whose language tag is exactly `runbook-ir`. + +Events between `user(n)` and `user(n+1)` belong to turn `n`. Client-tool resume dispatches belong to the turn whose submission they resume. The trace is mechanically derivable and is retained beside the raw snapshot; a check that cannot be read off the trace and the visible text is not a check in this ruler. + +## Turn classification + +A fresh-context adjudicator who has not seen the run's situation pack classifies every assistant text of an interactive-entry run into exactly one kind, quoting the text that decided it: + +- **Orientation**: asks or confirms purpose, intended decision, audience, boundary, horizon, accuracy need, or available time, or clarifies the person's own request. Asks for no operational fact about how the domain works. +- **Substantive**: asks the person to supply operational knowledge of their domain: how something works, who does it, when, how often, how much, under what condition, or what happens when. +- **Recording**: emits or revises the workpiece, or summarizes for confirmation, without asking a new substantive question. +- **Delivery**: closes with the current workpiece, limitations, and open gaps after an explicit stop or exhausted budget. +- **Other**: anything else, including refusals and tool-only responses with no text. + +**T_sub** is the turn index of the first Substantive text in the run. A run with no Substantive text within its budget is recorded as `no substantive question` and excluded from item 4a and 5a proportions but reported. + +Each Orientation or Substantive text is additionally classified for **dosage**, applying kernel item 6 and nothing stricter: + +- **Deepening**: pursues one answerable thread, possibly with one follow-up that depends on the same answer. +- **Grouped in one frame**: asks more than one thing, and every part concerns one situation, decision, case, or object the person can hold in mind at once, so one answer can address them together. +- **Battery**: asks more than one thing across independent topics, so the person must choose which to answer first or produce a multi-part survey response. + +Illustrations, not rules: "Walk me through what happens when the Alder tank alarm fires, from who sees it to what they do first" is Deepening. "For that Alder alarm, who sees it and how long do they have before it matters?" is Grouped in one frame. "How many tankers do you run, what products do you carry, how are drivers scheduled, and what does an outage look like?" is a Battery. A single question that spans two unrelated topics is still a Battery. + +Prohibited proxies: counting `?` characters, sentences, or questions; requiring exactly one interrogative; measuring response length; treating any register order as required question order. A finding that rests on one of these is void. + +## Item 4 checks: capability activation + +**4a Activation before substance (per interactive-entry run).** Pass when `activate(sdcpn-modelling, ok)` precedes `activate(elicitation, ok)` and both occur before the first Substantive text in canonical order, whether in an earlier turn or earlier in the same turn. Fail when either activation is missing, their order is reversed, or the first Substantive text precedes either successful activation. Record both activation positions and T_sub. Orientation texts before activation do not fail this check, as accepted by the owner. + +**4b Proof of life across runs.** Report, per elicitor model, the count of 4a passes over graded interactive-entry runs, broken down by case and run extent. Pass only when the accepted floor contains three valid 4a-gradable runs over three distinct case families—one full conversation and two first-Substantive probes—and all three pass 4a. Any failing trace is reported as strain on the accepted topology and is never resolved by changing the topology. This `3/3` threshold supports only the bounded cross-case proof-of-life claim; it is not a reliability estimate. + +**4c Construct-only restraint (hermetic).** The `runbook-headless` execution over the checked fixture contains no `activate(elicitation, *)`. The current test asserts the tool names and the two construction resource reads but does not inspect which skill `activate_skill` named; the check requires the result record to carry activated skill names. That is a test extension, not a production change. + +**4d Review restraint (per review-entry run).** Pass when the response that performs or identifies the requested revision contains no prior `activate(elicitation, *)` anywhere in the run. Fail otherwise, even when the revision itself is correct. + +**4e Knowledge-gap review (per knowledge-gap review-entry run).** Pass when `activate(elicitation, ok)` precedes the first Substantive text, and the first Substantive text asks for the missing operational knowledge without asserting either answer. Fail when the response invents the rule or asks without activation. This is the positive complement of 4d and mirrors the skill's own description of when it applies. + +The owner selected the v3 side-quest scenario file's exact S3 `prompt` string for item 4d and exact S4 `prompt` string for item 4e, reused as controlled inputs only. The archived v3 protocol and topology comparison remain non-authority. Source: [`../cases/flue-skill-composition-side-quest-v3/scenarios.json`](../cases/flue-skill-composition-side-quest-v3/scenarios.json), file SHA-256 `1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb`; S3 prompt-string SHA-256 `ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729`; S4 prompt-string SHA-256 `64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635`. Their explicit cues make them controlled mechanism checks, not proof of robust uncued review routing. + +## Item 5 checks: routing, dosage, restraint + +**5a Conditional profile read before reliance (per interactive-entry run).** Pass when `read(sdcpn-modelling/references/profile.md, ok)` precedes the first Substantive text. Record when it is missing or late. Universal elicitation guidance is supplied by successful `elicitation` activation itself and has no separate read requirement. + +**5b Template timing.** Let E be the canonical position of the first `text(*, hasWorkpiece=true)` event. The first successful `read(sdcpn-modelling/templates/workpiece.md, ok)` is **timely** only when it occurs before E in the same turn, **premature** when it occurs in an earlier turn with no workpiece emitted in that turn, **late** when it occurs after E, and **missing** when a workpiece is emitted without a successful read. Premature, late, and missing are findings. Re-reads on later material revision are recorded, not judged. This ordering rule is the evaluator correction accepted by the owner; it changes no production text. + +**5c Resource restraint.** Before the person requests construction or a net and a construction tool is mounted, no `read(sdcpn-modelling/references/pn-construction.md, *)` and no `read(sdcpn-modelling/references/checks.md, *)`. A read of either during ordinary interviewing is a finding with its turn cited. Reads of resources belonging to a skill that was never activated are a finding. A repeated `activate_skill` for an already-active skill is recorded as noise, not a finding. + +**5d Dosage.** The first Substantive text of every interactive-entry run must not be a Battery; violation fails the proof-of-life run because kernel item 6 names the opening specifically. Every later Orientation or Substantive text in the full conversation is classified and each Battery is quoted and reported, but later dosage does not determine proof-of-life acceptance. Report per run the number of Battery texts over the number of Orientation plus Substantive texts. Broader later-turn dosage fitness belongs to the successor hardening decision, not this ruler. + +Persona corroboration is recorded separately and never substitutes for adjudication: a persona reply that names parts it skipped, asks why something matters, or says a question was already answered is a pointer to the elicitor turn that provoked it. The persona is a model output and is not the oracle. + +**5e Not measured.** Response length, question count, `?` count, sentence count, politeness, fluency, and whether the elicitor used a particular phrase. Findings that cite these are void. + +## Run validity + +A run is **invalid** and reported outside every proportion when any of the following occurs: a Flue runtime or transport error; a client-tool suspension left unresolved; an elicitor response with no text and no tool call; a persona model refusal or provider `stop_reason: refusal`; the persona mentioning its budget, instructions, or the evaluation; or a first user message that differs from the case's opening message. Invalid runs are retained with their traces and counted in a separate table with the failure kind. Nothing is dropped silently, and an invalid run is never rerun under the same run id. + +## Retention + +Each graded or invalid run is retained under `docs/evidence/evaluations/<campaign>/runs/<run-id>/` with: the raw `history()` snapshot as JSON, the formatted transcript, the derived trace, the adjudication with quoted decisions, and a manifest naming the source commit, elicitor model, persona model, case, run kind, and SHA-256 of each retained file. The transcript CLI currently prints only the formatted transcript, so a raw-snapshot writer for persona runs is a required mechanism addition before the first graded run; it reads history and writes files, and touches no production text. + +## Owner acceptance recorded 2026-09-03 + +1. Per elicitor model: one full 6–10-turn conversation plus two first-Substantive probes over three distinct current persona case families; all three valid gradable runs must pass items 4a and 5a. +2. Orientation text may precede activation. Successful `sdcpn-modelling` then `elicitation` activation and the SDCPN profile read must precede the first Substantive operational question. +3. Items 4d and 4e use the exact S3 and S4 prompt strings identified above as controlled inputs only. +4. The opening-Battery prohibition determines proof-of-life acceptance. Later-turn dosage in the full conversation is classified and reported without an aggregate pass floor. +5. The ruler is accepted with the item 5b canonical-order correction. On the same date, the owner accepted inlining the sole mandatory universal resource into `elicitation/SKILL.md`; the 4c, 4d, and 5a wording above incorporates that semantics-preserving repair. Acceptance supplies the oracle but does not prove the architecture, freeze a protocol, authorize paid calls, accept a handoff candidate, or accept the topology-neutral matrix. diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v2.md b/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v2.md new file mode 100644 index 00000000000..102981bd3e5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v2.md @@ -0,0 +1,103 @@ +# Mission 4 activation and restraint ruler v2 + +Status: **freeze candidate prepared from the owner's 2026-09-03 selection of fixed three-submission probes; not accepted or campaign-frozen until the owner accepts the exact v2 manifest.** Evaluator-only: this file never enters the elicitor or persona context. V2 preserves every v1 semantic classification and threshold while moving first-Substantive detection out of the isolated persona and into post-settlement adjudication. + +Scope: the mechanism and opening-turn restraint of interactive elicitation on the real production path, observed through the merged persona testing harness. It does not establish a general reliability rate, grade workpiece content ([`ir-quality-ruler-v1.md`](ir-quality-ruler-v1.md)), or grade case-specific recognition and treatment ([the topology-neutral case matrix](../cases/mission-4-topology-neutral-case-matrix.md)). The full topology-neutral portfolio is not a prerequisite for this proof-of-life claim. + +This ruler may falsify an implementation, a prompt wording, or a proof claim. It may not select or replace the topology, and no check in it is stricter than the accepted question-dosage wording in kernel item 6. The accepted thresholds are recorded under "Owner acceptance recorded 2026-09-03" and must never be tuned after observation. + +## Instrument + +A **run** is one persona conversation driven by `brunch_turn` against the production `ChatAgent`, or one hermetic execution of an existing app test where stated. The evidence for every check is canonical Flue history read after the run settles, through `history()` or the transcript CLI. Pi tool details, the browser observer, and the persona's own summary are projections and never the evidence. + +Three run kinds are graded, each entered fresh with no prior conversation: + +| Kind | First user message | Purpose | +| --- | --- | --- | +| Interactive entry | The text below the `---` separator of a case's `opening-message.md`, unchanged | Items 4a, 4b, 5a–5d | +| Review entry | The exact selected S3 prompt identified under item 4d | Item 4d (restraint) | +| Knowledge-gap review entry | The exact selected S4 prompt identified under item 4e | Item 4e (positive complement) | + +The candidate interactive floor, per elicitor model, is three valid 4a-gradable runs over three distinct current persona case families: one full conversation with a 6–10-turn budget that emits a recoverable workpiece, and two probes that each retain exactly three visible user submissions while the fresh adjudicator locates the first Substantive text after settlement. All three must pass items 4a and 5a. Probe text after the first Substantive text is retained but cannot alter ordering before T_sub and does not enter the activation-before-substance decision. Core's universal guidance is inline in the activated `elicitation` skill; only the conditionally disclosed SDCPN profile requires a resource read. Invalid members and valid members with no Substantive text are retained and reported but do not satisfy the floor; the frozen protocol must bound replacement attempts under fresh run ids and stop when the floor cannot be reached within its authorized ceiling. This is cross-case proof of life, not a population reliability estimate. + +Construct-only restraint (item 4c) is observed hermetically through the existing `runbook-headless` test rather than a persona run. + +Every run records the elicitor model and, when a persona drives the run, the persona model. Claims hold per elicitor model and are never pooled across models. + +## Derived trace + +From the settled snapshot, produce one ordered trace per run. Walk visible messages in canonical order and emit one event per part: + +- `user(n)`: the n-th visible user message; `n` is the **turn index**. +- `activate(name, outcome)`: a `dynamic-tool` part with tool name `activate_skill`, `name` from its input, outcome `ok` when state is `output-available` and `error` otherwise. +- `read(path, outcome)`: a `dynamic-tool` part with tool name `read_skill_resource`; `path` is the packaged path from its input, reported by its trailing `skills/<skill>/<relative name>` segment. +- `tool(name, executor)`: any other `dynamic-tool` part; `executor` is `server`, or `client` when the output is the awaiting-client signal. +- `text(turn, hasWorkpiece)`: an assistant text part; `hasWorkpiece` is true when the text contains a fenced block whose language tag is exactly `runbook-ir`. + +Events between `user(n)` and `user(n+1)` belong to turn `n`. Client-tool resume dispatches belong to the turn whose submission they resume. The trace is mechanically derivable and is retained beside the raw snapshot; a check that cannot be read off the trace and the visible text is not a check in this ruler. + +## Turn classification + +A fresh-context adjudicator who has not seen the run's situation pack classifies every assistant text of an interactive-entry run into exactly one kind, quoting the text that decided it: + +- **Orientation**: asks or confirms purpose, intended decision, audience, boundary, horizon, accuracy need, or available time, or clarifies the person's own request. Asks for no operational fact about how the domain works. +- **Substantive**: asks the person to supply operational knowledge of their domain: how something works, who does it, when, how often, how much, under what condition, or what happens when. +- **Recording**: emits or revises the workpiece, or summarizes for confirmation, without asking a new substantive question. +- **Delivery**: closes with the current workpiece, limitations, and open gaps after an explicit stop or exhausted budget. +- **Other**: anything else, including refusals and tool-only responses with no text. + +**T_sub** is the turn index of the first Substantive text in the run. A run with no Substantive text within its budget is recorded as `no substantive question` and excluded from item 4a and 5a proportions but reported. + +Each Orientation or Substantive text is additionally classified for **dosage**, applying kernel item 6 and nothing stricter: + +- **Deepening**: pursues one answerable thread, possibly with one follow-up that depends on the same answer. +- **Grouped in one frame**: asks more than one thing, and every part concerns one situation, decision, case, or object the person can hold in mind at once, so one answer can address them together. +- **Battery**: asks more than one thing across independent topics, so the person must choose which to answer first or produce a multi-part survey response. + +Illustrations, not rules: "Walk me through what happens when the Alder tank alarm fires, from who sees it to what they do first" is Deepening. "For that Alder alarm, who sees it and how long do they have before it matters?" is Grouped in one frame. "How many tankers do you run, what products do you carry, how are drivers scheduled, and what does an outage look like?" is a Battery. A single question that spans two unrelated topics is still a Battery. + +Prohibited proxies: counting `?` characters, sentences, or questions; requiring exactly one interrogative; measuring response length; treating any register order as required question order. A finding that rests on one of these is void. + +## Item 4 checks: capability activation + +**4a Activation before substance (per interactive-entry run).** Pass when `activate(sdcpn-modelling, ok)` precedes `activate(elicitation, ok)` and both occur before the first Substantive text in canonical order, whether in an earlier turn or earlier in the same turn. Fail when either activation is missing, their order is reversed, or the first Substantive text precedes either successful activation. Record both activation positions and T_sub. Orientation texts before activation do not fail this check, as accepted by the owner. + +**4b Proof of life across runs.** Report, per elicitor model, the count of 4a passes over graded interactive-entry runs, broken down by case and run extent. Pass only when the candidate floor contains three valid 4a-gradable runs over three distinct case families—one full conversation and two fixed three-submission probes graded at their first Substantive text—and all three pass 4a. Any failing trace is reported as strain on the accepted topology and is never resolved by changing the topology. This `3/3` threshold supports only the bounded cross-case proof-of-life claim; it is not a reliability estimate. + +**4c Construct-only restraint (hermetic).** The `runbook-headless` execution over the checked fixture contains no `activate(elicitation, *)`. The current test asserts the tool names and the two construction resource reads but does not inspect which skill `activate_skill` named; the check requires the result record to carry activated skill names. That is a test extension, not a production change. + +**4d Review restraint (per review-entry run).** Pass when the response that performs or identifies the requested revision contains no prior `activate(elicitation, *)` anywhere in the run. Fail otherwise, even when the revision itself is correct. + +**4e Knowledge-gap review (per knowledge-gap review-entry run).** Pass when `activate(elicitation, ok)` precedes the first Substantive text, and the first Substantive text asks for the missing operational knowledge without asserting either answer. Fail when the response invents the rule or asks without activation. This is the positive complement of 4d and mirrors the skill's own description of when it applies. + +The owner selected the v3 side-quest scenario file's exact S3 `prompt` string for item 4d and exact S4 `prompt` string for item 4e, reused as controlled inputs only. The archived v3 protocol and topology comparison remain non-authority. Source: [`../cases/flue-skill-composition-side-quest-v3/scenarios.json`](../cases/flue-skill-composition-side-quest-v3/scenarios.json), file SHA-256 `1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb`; S3 prompt-string SHA-256 `ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729`; S4 prompt-string SHA-256 `64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635`. Their explicit cues make them controlled mechanism checks, not proof of robust uncued review routing. + +## Item 5 checks: routing, dosage, restraint + +**5a Conditional profile read before reliance (per interactive-entry run).** Pass when `read(sdcpn-modelling/references/profile.md, ok)` precedes the first Substantive text. Record when it is missing or late. Universal elicitation guidance is supplied by successful `elicitation` activation itself and has no separate read requirement. + +**5b Template timing.** Let E be the canonical position of the first `text(*, hasWorkpiece=true)` event. The first successful `read(sdcpn-modelling/templates/workpiece.md, ok)` is **timely** only when it occurs before E in the same turn, **premature** when it occurs in an earlier turn with no workpiece emitted in that turn, **late** when it occurs after E, and **missing** when a workpiece is emitted without a successful read. Premature, late, and missing are findings. Re-reads on later material revision are recorded, not judged. This ordering rule is the evaluator correction accepted by the owner; it changes no production text. + +**5c Resource restraint.** Before the person requests construction or a net and a construction tool is mounted, no `read(sdcpn-modelling/references/pn-construction.md, *)` and no `read(sdcpn-modelling/references/checks.md, *)`. A read of either during ordinary interviewing is a finding with its turn cited. Reads of resources belonging to a skill that was never activated are a finding. A repeated `activate_skill` for an already-active skill is recorded as noise, not a finding. + +**5d Dosage.** The first Substantive text of every interactive-entry run must not be a Battery; violation fails the proof-of-life run because kernel item 6 names the opening specifically. Every later Orientation or Substantive text in the full conversation is classified and each Battery is quoted and reported, but later dosage does not determine proof-of-life acceptance. Report per run the number of Battery texts over the number of Orientation plus Substantive texts. Broader later-turn dosage fitness belongs to the successor hardening decision, not this ruler. + +Persona corroboration is recorded separately and never substitutes for adjudication: a persona reply that names parts it skipped, asks why something matters, or says a question was already answered is a pointer to the elicitor turn that provoked it. The persona is a model output and is not the oracle. + +**5e Not measured.** Response length, question count, `?` count, sentence count, politeness, fluency, and whether the elicitor used a particular phrase. Findings that cite these are void. + +## Run validity + +A run is **invalid** and reported outside every proportion when any of the following occurs: a Flue runtime or transport error; a client-tool suspension left unresolved; an elicitor response with no text and no tool call; a persona model refusal or provider `stop_reason: refusal`; the persona mentioning its budget, instructions, or the evaluation; or a first user message that differs from the case's opening message. Invalid runs are retained with their traces and counted in a separate table with the failure kind. Nothing is dropped silently, and an invalid run is never rerun under the same run id. + +## Retention + +Each graded or invalid run is retained under `docs/evidence/evaluations/<campaign>/runs/<run-id>/` with: the raw `history()` snapshot as JSON, the formatted transcript, the derived trace, the adjudication with quoted decisions, and a manifest naming the source commit, elicitor model, persona model, case, run kind, and SHA-256 of each retained file. The transcript CLI currently prints only the formatted transcript, so a raw-snapshot writer for persona runs is a required mechanism addition before the first graded run; it reads history and writes files, and touches no production text. + +## Owner decisions recorded 2026-09-03 + +1. Per elicitor model: one full 6–10-turn conversation plus two fixed three-submission probes over three distinct current persona case families; the fresh adjudicator locates each first Substantive text after settlement, and all three valid gradable runs must pass items 4a and 5a. The owner selected this v2 repair direction after v1 made the isolated persona classify an undefined evaluator category; exact v2 ruler and manifest acceptance remain pending. +2. Orientation text may precede activation. Successful `sdcpn-modelling` then `elicitation` activation and the SDCPN profile read must precede the first Substantive operational question. +3. Items 4d and 4e use the exact S3 and S4 prompt strings identified above as controlled inputs only. +4. The opening-Battery prohibition determines proof-of-life acceptance. Later-turn dosage in the full conversation is classified and reported without an aggregate pass floor. +5. The ruler is accepted with the item 5b canonical-order correction. On the same date, the owner accepted inlining the sole mandatory universal resource into `elicitation/SKILL.md`; the 4c, 4d, and 5a wording above incorporates that semantics-preserving repair. Acceptance supplies the oracle but does not prove the architecture, freeze a protocol, authorize paid calls, accept a handoff candidate, or accept the topology-neutral matrix. diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/pharma-cold-chain/truth-ledger-v1-prospective.yaml b/libs/@hashintel/brunch-agent/evaluations/oracles/pharma-cold-chain/truth-ledger-v1-prospective.yaml new file mode 100644 index 00000000000..2d9e19cfec7 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/pharma-cold-chain/truth-ledger-v1-prospective.yaml @@ -0,0 +1,723 @@ +version: 1-prospective +case: virelia-pharma-cold-chain +source: evaluations/cases/pharma-cold-chain/situation-pack.md +provenance: + authored_after_historical_runs: false + calibrated_against_historical_runs: false + frozen_before_prospective_runs: true +use: prospective baseline and variant grading only +warning: >- + This is a prospective oracle sourced only from the explicitly synthetic situation pack. Every + organization, person, product, shipment, event, value, and operational outcome is fictional + benchmark truth, not empirical evidence, pharmaceutical guidance, or a claim about real operators. +importance_weights: + load-bearing: 3 + useful: 2 + incidental: 1 + +calibration_rules: + transcript_precedence: >- + Grade what the simulated expert actually disclosed. Ledger truth may identify a distinction, + but it does not erase hedges, narrower examples, corrections, or nondisclosure in the transcript. + interviewer_proposals: >- + A value or interpretation appearing only in an interviewer question is not user evidence unless + the expert explicitly adopts it. + relevant_absences: >- + Entries whose epistemic_character is relevant-absence describe a relation the pack does not + settle. Credit elicitation that identifies the gap; do not expect the expert or IR to fill it. + hard_stop: >- + When the expert ends elicitation, distinguish unanswered suitable questions from earlier missed + opportunities. Do not convert turn-budget truncation into an ordinary acquisition miss. + partial_disclosure: >- + A concrete example inside a ledger range is partial evidence, not simulator failure by itself. + Penalize only unsupported generalization beyond the disclosed example. + +facts: + - id: objective-compare-normal-and-recovery + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Compare the chance of on-time, temperature-compliant delivery under the normal plan and under + hold, reroute, and expedite choices. + expected_ir_homes: [purpose, goals, scenarios, validation] + traps: [reducing the objective to journey-time prediction] + + - id: objective-early-recovery-spend + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Decide when to spend money early rather than wait until only expensive recovery remains. + expected_ir_homes: [purpose, goals, decisions, validation] + traps: [hard-coding the decision rule the simulation is meant to test] + + - id: objective-delay-propagation + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Show how customs delay propagates into pack-out risk, missed delivery, quarantine, + replacement, and the clinic's first-dose date. + expected_ir_homes: [purpose, flow, failures, consequences] + traps: [stopping the model boundary at customs release] + + - id: objective-operational-reconstruction + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Reconstruct bad runs in operational terms: where the shipment waited, who had custody, what + temperature did, which decision was available, and why the outcome followed. + expected_ir_homes: [purpose, participants-resources, flow, validation] + traps: [producing only an aggregate success probability] + + - id: objective-earlier-escalation-trigger + importance: load-bearing + epistemic_character: tacit-goal + discoverability: tacit + expert_can_answer: true + reveal_when: asked about unwritten practice, escalation, or what decision the current process gets wrong + truth: >- + Mara wants an earlier escalation trigger because waiting for an actual 8 °C reading may be + too late when the trace is climbing and cooler space is scarce. + expected_ir_homes: [purpose, policies, decisions, situation-notes] + traps: [presenting Mara's instinct as a validated universal threshold] + + - id: reference-shipment-shape + importance: useful + epistemic_character: explicit-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The reference Nivora-8 shipment contains 4,800 prefilled syringes in 24 sealed passive + shippers, with one batch and one master airway bill. + expected_ir_homes: [boundary, entities, quantities] + traps: [treating the 24 shippers as independent shipments] + + - id: validated-lane + importance: load-bearing + epistemic_character: explicit-route + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The lane runs Cambridge warehouse to Heathrow cargo terminal, by air to Frankfurt, through + Frankfurt import clearance, then by refrigerated road to the Mazovia Trial Pharmacy in Warsaw. + expected_ir_homes: [boundary, activities, flow, participants-resources] + traps: + [omitting import clearance or treating Frankfurt as only a connection] + + - id: sla-clock-and-accepted-delivery + importance: load-bearing + epistemic_character: explicit-success-rule + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The 54-hour customer SLA runs from signed Cambridge pickup to accepted Warsaw proof of + delivery; gate arrival is insufficient because the pharmacy must accept seals and documents, + sign, and timestamp proof of delivery. + expected_ir_homes: [goals, boundary, time, validation] + traps: [starting the SLA at lid closure, stopping it at gate arrival] + + - id: shipment-success-composite + importance: load-bearing + epistemic_character: explicit-success-rule + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Success requires delivery within 54 hours, no logger reading below 2 °C or above 8 °C, + complete custody and document evidence, and pharmacy acceptance; Mazovia may refuse broken + seals, incomplete documents, or missing logger evidence and issues final proof of delivery. + expected_ir_homes: [goals, constraints, decisions, evidence, validation] + traps: + [ + equating on-time arrival with success, + ignoring do-not-freeze, + assuming physical arrival compels acceptance, + ] + + - id: packout-qualification-scope + importance: load-bearing + epistemic_character: qualified-engineering-claim + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked what the 72-hour qualification guarantees or how thermal margin should be used + truth: >- + Pack-out is qualified for 72 hours from lid closure only under Virelia's benchmark summer + profile, while sealed and correctly conditioned; it is not a guarantee that every shipper + remains below 8 °C for 72 hours under any exposure. + expected_ir_homes: [constraints, time, assumptions, validation] + traps: + [ + treating 72 hours as a universal countdown guarantee, + starting qualification at pickup, + ] + + - id: excursion-quarantine-and-disposition-authority + importance: load-bearing + epistemic_character: explicit-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Any logger excursion outside 2–8 °C causes quarantine on arrival; the site QA duty manager + decides release or rejection after stability review, and Mara cannot waive that review. + expected_ir_homes: [policies, failures, decisions, authority] + traps: + [making quarantine equivalent to rejection, giving Mara release authority] + + - id: cambridge-packout-and-handoff + importance: useful + epistemic_character: explicit-responsibility + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The Cambridge warehouse conditions shippers, loads product, activates loggers, applies + numbered seals, closes pack-out, and signs custody to the collection driver. + expected_ir_homes: [participants-resources, activities, custody, triggers] + traps: [placing logger activation or lid closure at carrier pickup] + + - id: northstar-operational-control-boundary + importance: load-bearing + epistemic_character: explicit-authority-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Northstar is lead logistics provider and control tower, collects in a refrigerated vehicle, + arranges final delivery, may choose routine contractual recovery, and owns vehicle assignment; + it does not own customs release or product-quality decisions. + expected_ir_homes: [participants-resources, custody, decisions, authority] + traps: [making the control tower sovereign over customs or QA] + + - id: airline-handler-broker-customs-roles + importance: load-bearing + epistemic_character: explicit-responsibility-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + AeroLynx holds custody from Heathrow airline acceptance to RheinGate acceptance; RheinGate + unloads, scans, stages, and may move freight to its validated cooler; Kestrel submits and + corrects entries without physical custody; German customs alone authorizes release and any + transfer under customs control. + expected_ir_homes: [participants-resources, custody, activities, authority] + traps: + [ + giving Kestrel physical custody, + allowing a carrier-directed move before authorization, + ] + + - id: custody-evidence-versus-temperature-evidence + importance: load-bearing + epistemic_character: explicit-evidence-distinction + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Signed handoff scans evidence custody at pickup, airline acceptance, handler receipt, road + release, and Warsaw receipt; logger evidence is separate, so a clean custody chain does not + prove acceptable temperature. + expected_ir_homes: [custody, evidence, validation] + traps: [using custody completeness as a proxy for thermal compliance] + + - id: recovery-spend-and-decision-rights + importance: load-bearing + epistemic_character: explicit-authority-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Mara may request hold, reroute, or expedite and approve recovery spend up to €7,500; Omar + approves higher spend, while the site QA duty manager owns quarantine and disposition. + expected_ir_homes: [decisions, authority, policies, costs] + traps: + [ + equating request authority with execution or product-disposition authority, + ] + + - id: origin-road-timing + importance: useful + epistemic_character: explicit-time-range + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Pack-out closes around 05:30 Tuesday with planned pickup at 06:00; Cambridge to Heathrow takes + 2.5–3.5 hours typically and about 5 hours on a bad traffic day. + expected_ir_homes: [activities, flow, time] + traps: + [ + using pickup and lid closure interchangeably, + replacing the range with an instantaneous average, + ] + + - id: export-and-flight-timing + importance: load-bearing + epistemic_character: explicit-time-range + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Export acceptance and build-up take 2–5 hours, up to 7 with screening or a missed cut-off; + scheduled flight time is about 1.75 hours, while delay, offload, or a missed connection can + add 4–16 hours. + expected_ir_homes: [activities, failures, flow, time] + traps: + [ + using only average flight duration, + adding missed-cut-off delay without changing the departure opportunity, + ] + + - id: handler-and-clearance-timing + importance: load-bearing + epistemic_character: explicit-time-range + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Frankfurt unload and receipt take 2–4 hours, up to 6 on a bad shift; clearance takes 3–8 + hours normally, 18–36 with an uncommon document query, and beyond 48 for an unresolved conflict. + expected_ir_homes: [activities, failures, flow, time] + traps: + [ + collapsing handler receipt and customs release, + treating the ranges as exact distributions, + ] + + - id: final-road-and-receipt-timing + importance: load-bearing + epistemic_character: explicit-time-range + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Frankfurt to Warsaw takes 9–11 driving hours plus a break, or 13–16 under severe disruption; + pharmacy receipt takes 30–60 minutes, sometimes 2 hours when the pharmacist is occupied or + paperwork mismatches. + expected_ir_homes: [activities, failures, flow, time] + traps: [omitting the driving break or receipt process from end-to-end time] + + - id: delay-contention-and-propagation + importance: load-bearing + epistemic_character: explicit-process-dynamics + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked whether stage delays simply add or what becomes unavailable after a delay + truth: >- + Stages contend for cut-offs, cooler positions, drivers, and flights; delay can miss a + departure, consume qualified pack-out time, or leave the next driver unavailable rather than + merely adding at journey end. + expected_ir_homes: [flow, resources, time, failures] + traps: [modeling every delay as an independent additive duration] + + - id: customs-query-probability-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Mara has no defensible customs-clearance probability curve; Kestrel's monthly medians mix + products and lanes and do not establish a distribution for this case. + expected_ir_homes: [unknowns, data-sources, assumptions, validation] + traps: + [ + fitting a distribution from the typical and bad-day ranges, + treating mixed medians as lane-specific evidence, + ] + + - id: temperature-environment-dynamics + importance: load-bearing + epistemic_character: explicit-process-dynamics + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Product temperature changes throughout the journey, tends toward 4–6 °C in working + refrigerated vehicles or validated coolers, and tends to rise in uncontrolled areas with + ambient conditions, pack age, and door opening; the rise is neither instantaneous nor + reliably linear. + expected_ir_homes: [state, flow, resources, time] + traps: + [using instantaneous temperature jumps, assuming a linear warming rate] + + - id: thermal-response-model-absence + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: >- + The pack does not provide quantitative warming or cooling response curves across ambient + exposure, pack age, door opening, validated storage, or refrigerated transport, nor the + exposure profiles needed to predict temperature compliance. + expected_ir_homes: [state, time, unknowns, data-sources, validation] + traps: + [ + deriving a linear thermal-response model from the incident trace, + assigning ambient exposure profiles that the pack does not contain, + ] + + - id: validated-storage-location-boundary + importance: load-bearing + epistemic_character: explicit-resource-qualification + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Validated 2–8 °C locations are Cambridge warehouse, Northstar's collection vehicle, + Heathrow's booked pharma cooler, RheinGate's GDP cooler, the Frankfurt–Warsaw refrigerated + vehicle, and Mazovia's refrigerator; an airline hold or general handling bay is not validated + merely because it is indoors. + expected_ir_homes: [participants-resources, constraints, policies] + traps: [treating every indoor or controlled area as validated storage] + + - id: centre-logger-representativeness-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + The five-minute centre logger is the operational first-review reference but does not establish + the warmest syringe in every shipper; QA may later use shipper position and qualification studies + to bound it. + expected_ir_homes: [evidence, unknowns, data-sources, validation] + traps: [treating one centre logger as every product unit's temperature] + + - id: potency-and-release-rule-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Mara cannot convert duration above 8 °C directly into potency loss, and operations has no + generic time-temperature or automatic-release rule; the stability group owns that assessment. + expected_ir_homes: [unknowns, data-sources, policies, validation] + traps: + [ + inventing a potency equation, + converting any excursion directly to rejection, + ] + + - id: rising-trace-thermal-margin-belief + importance: load-bearing + epistemic_character: belief-with-qualification + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked how operations interprets thermal margin, rising traces, or the 72-hour claim + truth: >- + Mara believes a centre logger above 6.5 °C and rising after a long delay leaves less safe + decision time than the nominal qualification suggests; the control tower tends to treat 72 + hours as hard protection, but this is Mara's qualified operational view. + expected_ir_homes: [beliefs, conflicts, decisions, situation-notes] + traps: + [ + making 6.5 °C a proven product limit, + erasing the control-tower disagreement, + ] + + - id: recovery-hold-at-origin + importance: load-bearing + epistemic_character: explicit-recovery-branch + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + If disruption is known before collection, holding in Cambridge's validated refrigerator and + delaying pack-out or pickup costs about €350 and preserves the most thermal margin, but may + miss the booked flight and 54-hour SLA. + expected_ir_homes: [decisions, scenarios, costs, consequences] + traps: + [allowing origin hold after collection, claiming it preserves the SLA] + + - id: recovery-hold-at-frankfurt + importance: load-bearing + epistemic_character: explicit-recovery-branch + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + After handler receipt, RheinGate's validated cooler costs €420 per started day and protects + temperature while clearance proceeds, but does not stop the SLA clock; space is not guaranteed + and customs may require the current controlled area until a move is recorded. + expected_ir_homes: [decisions, resources, costs, constraints] + traps: [pausing the SLA clock, assuming cooler access or movement authority] + + - id: recovery-bonded-reroute + importance: load-bearing + epistemic_character: explicit-recovery-branch + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Kestrel may request a customs-controlled transfer to RheinGate's Leipzig partner; authorization + and road transfer add 8–14 hours and about €4,800, and the branch is useful for Frankfurt + capacity problems rather than when release is expected shortly. + expected_ir_homes: [decisions, scenarios, authority, costs] + traps: [treating reroute as release-independent or always beneficial] + + - id: reroute-availability-odds-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Night-time bonded-transfer authorization frequency and Leipzig cooler availability are not + measured well enough to assign trustworthy odds. + expected_ir_homes: [unknowns, assumptions, validation] + traps: + [ + assigning reroute success probabilities from the fact that the option exists, + ] + + - id: recovery-post-release-expedite + importance: load-bearing + epistemic_character: explicit-recovery-branch + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + After release, a two-driver dedicated refrigerated vehicle costs €6,200 instead of €1,400 and + usually saves 5–7 hours; it cannot recover pre-release time or erase a temperature excursion. + expected_ir_homes: [decisions, scenarios, time, costs] + traps: [using expedite before customs release, resetting thermal history] + + - id: recovery-emergency-replacement + importance: load-bearing + epistemic_character: explicit-recovery-branch + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + A second Cambridge pack-out and premium movement costs about €38,000, requires Omar's approval, + and is inventory-limited to one replacement; early start risks two valid shipments while late + start risks the clinic date. + expected_ir_homes: [decisions, resources, authority, costs] + traps: + [ + treating replacement inventory as unlimited, + ignoring duplicate-valid-shipment risk, + ] + + - id: rejection-and-clinic-consequence + importance: load-bearing + epistemic_character: explicit-consequence + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The reference shipment's authored replacement value is €620,000, and rejection or delivery + more than 12 hours beyond SLA can delay Warsaw's first patient dose by a week, requiring + clinical-supply escalation even when insured. + expected_ir_homes: [consequences, costs, goals, escalation] + traps: [reducing consequence to insured replacement value] + + - id: incident-origin-through-flight + importance: useful + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + VRB-240618-03 closed at 05:28 Tuesday, left Cambridge at 06:10, was 4.6 °C at closure, and + remained between 4.2 and 5.3 °C through the flight. + expected_ir_homes: [scenario, evidence, flow, time] + traps: [generalizing this incident trace to normal shipments] + + - id: incident-hold-start-event-conflict + importance: load-bearing + epistemic_character: explicit-semantic-tension + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked when the incident hold began or to reconcile handler, broker, and customs timestamps + truth: >- + RheinGate receipt at 16:52 Tuesday, Kestrel's hold start at 17:10 for the commodity-code + mismatch, and customs acknowledgement at 18:05 are distinct events, although staff sometimes + call each one the start of the hold. + expected_ir_homes: [scenario, time, conflicts, evidence] + traps: + [silently choosing one timestamp as the universally correct hold start] + + - id: incident-uncontrolled-bay-and-rise + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + RheinGate's booked cooler was full, so the sealed shipment remained in a general handling bay; + at 01:50 Thursday its logger began a sustained rise from 5.9 °C. + expected_ir_homes: [scenario, resources, failures, state] + traps: + [ + treating the general bay as validated storage, + moving the rise start to hold start, + ] + + - id: incident-bay-ambient-evidence-absent + importance: load-bearing + epistemic_character: relevant-absence + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + RheinGate has no usable ambient trace for the general bay, so Mara cannot compare the + incident exposure with the benchmark 72-hour qualification profile. + expected_ir_homes: [unknowns, evidence, data-sources, validation] + traps: + [ + inferring bay ambient temperature from the product logger, + claiming qualification equivalence, + ] + + - id: incident-excursion-and-cooler-move + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The logger crossed 8.0 °C at 03:42 Thursday, peaked at 10.6 °C at 04:18, and fell below 8.0 °C + at 05:05, totaling 83 minutes above 8 °C; RheinGate moved the shipment to the validated cooler + at 04:31 and cooling lagged the move. + expected_ir_homes: [scenario, state, activities, time] + traps: + [ + ending the excursion at the cooler move, + treating cooling as instantaneous, + ] + + - id: incident-release-delivery-and-quarantine + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Customs released the shipment at 19:20 Thursday after invoice and entry alignment; Northstar + expedited it, and Mazovia signed at 05:14 Friday, 71 hours 4 minutes after pickup, then + immediately quarantined it. + expected_ir_homes: [scenario, flow, time, consequences] + traps: + [claiming expedite restored SLA compliance, omitting arrival quarantine] + + - id: clock-conflict-stability-impact-unknown + importance: load-bearing + epistemic_character: relevant-unknown + discoverability: tension-probe + expert_can_answer: false + reveal_when: asked whether or how the 17-minute discrepancy should change the incident timeline or disposition + truth: >- + The logger clock was 17 minutes behind RheinGate's scan system, and no one has established + whether that conflict changes the stability decision; neither record should be silently corrected. + expected_ir_homes: [conflicts, unknowns, evidence, validation] + traps: + [silently shifting either timeline, declaring the discrepancy immaterial] + + - id: incident-rejection-nongeneralization + importance: load-bearing + epistemic_character: qualified-incident-outcome + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked what the incident's rejection implies for future excursions + truth: >- + The site QA duty manager rejected this authored shipment after a later stability review, but + operations cannot reproduce that review and the model must not infer that every 83-minute + excursion, or every excursion, is rejected. + expected_ir_homes: [scenario, policies, validation, constraints] + traps: [turning one rejection into a universal disposition rule] + + - id: escalation-written-versus-practiced + importance: load-bearing + epistemic_character: tacit-policy-practice-divergence + discoverability: tacit + expert_can_answer: true + reveal_when: asked about unwritten escalation, bad days, or what Mara does before the written trigger + truth: >- + The written instruction escalates at confirmed excursion or under 12 hours of qualified + duration remaining; Mara instead calls QA at 6.5 °C and rising or after four hours of customs + uncertainty because night cooler space may disappear first. + expected_ir_homes: [policies, escalation, conflicts, situation-notes] + traps: + [ + collapsing written policy and Mara's practice, + treating her trigger as universal validated policy, + ] + + - id: relationship-based-cooler-escalation + importance: useful + epistemic_character: tacit-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked how cooler moves are expedited in practice or what a newcomer would miss + truth: >- + Kestrel's night supervisor can often get a customs-controlled cooler move considered faster + when Mara calls directly, but this is relationship-based, has no contractual response time, + and does not always work. + expected_ir_homes: + [participants-resources, escalation, policies, situation-notes] + traps: + [modeling the call as guaranteed authorization or a fixed service time] + + - id: incident-cooler-request-causal-conflict + importance: load-bearing + epistemic_character: unresolved-belief-conflict + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked whether an earlier cooler request would have prevented the incident + truth: >- + Mara believes a 22:00 Wednesday cooler request probably would have avoided the excursion; + RheinGate disputes this because it says no qualified position opened before 04:20 Thursday. + The causal claim is unresolved. + expected_ir_homes: [beliefs, conflicts, scenario, validation] + traps: [adopting either party's account as proven causation] + + - id: commercial-pressure-versus-qa-authority + importance: load-bearing + epistemic_character: organizational-tension + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked who disagrees during a warming trace or who can overrule continued delivery + truth: >- + Commercial staff sometimes press to continue before the logger crosses 8 °C, but QA can + overrule them; Northstar may advise recovery but cannot declare product safe. + expected_ir_homes: [participants-resources, conflicts, decisions, authority] + traps: + [ + making commercial pressure a decision right, + giving Northstar safety authority, + ] + + - id: invoice-mismatch-control-unknown + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: false + reveal_when: asked why a visible document mismatch escaped or what control should prevent recurrence + truth: >- + The commodity-code mismatch was visible before pickup, but warehouse release, broker-entry + preparation, and transport booking sit in different teams; Mara does not know the base error + rate or which single control would prevent the most delays. + expected_ir_homes: + [participants-resources, failures, unknowns, data-sources] + traps: + [ + inventing a single accountable team, + claiming a preventive control is established, + ] + + - id: early-replacement-economics-unknown + importance: load-bearing + epistemic_character: explicit-unknown-objective + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Mara does not know whether early replacement is economically best across all disruption + types; resolving that trade-off is part of the simulation objective. + expected_ir_homes: [purpose, unknowns, assumptions, validation] + traps: [recording early replacement as an established optimal policy] + + - id: logger-clock-drift-origin-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + No one has established whether the incident logger's 17-minute drift existed at activation or + developed in transit. + expected_ir_homes: [unknowns, evidence, data-sources] + traps: [assigning the drift to activation or transit without evidence] + + - id: disruption-distributions-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Mara lacks defensible distributions for flight disruption, bonded-transfer approval, and + alternate-cooler availability in addition to the lane-specific customs-query gap. + expected_ir_homes: [unknowns, data-sources, assumptions, validation] + traps: + [ + turning narrated ranges or operational beliefs into probability distributions, + ] diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/semiconductor-fab-operations/truth-ledger-v1-prospective.yaml b/libs/@hashintel/brunch-agent/evaluations/oracles/semiconductor-fab-operations/truth-ledger-v1-prospective.yaml new file mode 100644 index 00000000000..93525a954bf --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/semiconductor-fab-operations/truth-ledger-v1-prospective.yaml @@ -0,0 +1,714 @@ +version: 1-prospective +case: aster-vale-foundry-operations +source: evaluations/cases/semiconductor-fab-operations/situation-pack.md +provenance: + authored_after_historical_runs: false + calibrated_against_historical_runs: false + frozen_before_prospective_runs: true +use: prospective baseline and variant grading only +warning: >- + This greenfield ledger is sourced only from the completed situation pack and the frozen + evaluation design and ruler. It has not been informed by or calibrated against runs of this + case. Freeze the case, prompts, model configuration, protocol, and this ledger together before + prospective grading. +importance_weights: + load-bearing: 3 + useful: 2 + incidental: 1 + +calibration_rules: + transcript_precedence: >- + Grade what the simulated expert actually disclosed. Ledger truth may identify a distinction, + but it does not erase hedges, narrower examples, corrections, or nondisclosure in the transcript. + interviewer_proposals: >- + A value or interpretation appearing only in an interviewer question is not user evidence unless + the expert explicitly adopts it. + relevant_absences: >- + Entries whose epistemic_character is relevant-absence describe a relation the pack does not + settle. Credit elicitation that identifies the gap; do not expect the expert or IR to fill it. + hard_stop: >- + When the expert ends elicitation, distinguish unanswered suitable questions from earlier missed + opportunities. Do not convert turn-budget truncation into an ordinary acquisition miss. + partial_disclosure: >- + A concrete example inside a ledger range is partial evidence, not simulator failure by itself. + Penalize only unsupported generalization beyond the disclosed example. + +facts: + - id: objective-good-lots-per-week + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: Sustain 18 good lots per week without flooding the floor. + expected_ir_homes: [purpose, goals, measures, validation] + traps: [treating starts or completed-but-rejected lots as good lots] + + - id: objective-due-date-service + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Keep at least 92 percent of lots on or before committed due dates, with expedited logic lots + receiving especially close attention from customer planning. + expected_ir_homes: [purpose, goals, measures, validation] + traps: + [treating every family and urgency class as commercially interchangeable] + + - id: objective-wip-band-and-ceiling + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Keep total WIP below the hard ceiling of 50 lots and preferably in the 42 to 47 lot band so + there is room for an urgent release or a held lot returning to route. + expected_ir_homes: [goals, measures, constraints, validation] + traps: [turning the preferred band into a hard feasibility constraint] + + - id: objective-final-inspection-yield + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: Hold final-inspection yield at or above 94 percent. + expected_ir_homes: [purpose, goals, measures, validation] + traps: [substituting mid-flow inspection results for final-inspection yield] + + - id: objective-maintenance-capacity-tradeoff + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Test when preventive work should outrank dispatch so breakdown and hidden-yield risk are + controlled without taking so much capacity down that queues and lateness surge. + expected_ir_homes: [purpose, goals, policies, validation] + traps: [hard-coding a maintenance priority the simulation is meant to test] + + - id: objective-furnace-batch-waiting + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Test whether waiting for full four-lot furnace batches remains worthwhile when due dates are + tight. + expected_ir_homes: [purpose, goals, policies, validation] + traps: [assuming either full batches or immediate starts are always optimal] + + - id: objective-exchange-rates-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Leena does not know a defensible numerical exchange rate among a late lot, a lost lot, an + hour of technician overtime, and an hour of chamber downtime; finance and customer planning + have not agreed one. + expected_ir_homes: [unknowns, losses, goals, validation, data-sources] + traps: [inventing objective weights from the stated qualitative goals] + + - id: product-families-and-lot-inputs + importance: load-bearing + epistemic_character: explicit-taxonomy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Aster Vale makes logic, memory, and analog products; uneven customer starts carry a family, + release time, committed due date, and recipe. + expected_ir_homes: [boundary, participants-resources, triggers, inputs] + traps: + [ + inventing additional families or omitting due dates and recipes from lot state, + ] + + - id: route-reentrant-28-positions + importance: load-bearing + epistemic_character: explicit-topology + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Every lot follows positions 0 through 27, and the route is re-entrant: the same chamber groups + are revisited, so early and nearly finished lots compete directly. + expected_ir_homes: [boundary, activities, flow, resources] + traps: [modeling the route as dedicated equipment at each position] + + - id: route-position-sequence + importance: load-bearing + epistemic_character: explicit-topology + discoverability: direct-if-asked + expert_can_answer: true + reveal_when: asked to walk the route, identify revisits, or explain where competition occurs + truth: >- + The position sequence is 0 Layer-0 pattern (lithography), 1 Layer-0 etch (etch), 2 base-film + deposition (TD), 3 plasma clean (etch), 4 Layer-4 pattern (lithography), 5 Layer-4 etch + (etch), 6 gate-film deposition (TD), 7 spacer etch (etch), 8 activation anneal (TD), + 9 Layer-9 pattern (lithography), 10 Layer-9 etch (etch), 11 interlayer-film deposition (TD), + 12 Layer-12 pattern (lithography), 13 Layer-12 etch (etch), 14 barrier-film deposition (TD), + 15 mid-flow dimensional check (inspection), 16 Layer-16 pattern (lithography), 17 Layer-16 + etch (etch), 18 contact-film deposition (TD), 19 contact etch (etch), 20 Layer-20 pattern + (lithography), 21 Layer-20 etch (etch), 22 metal-film deposition (TD), 23 metal anneal (TD), + 24 Layer-24 pattern (lithography), 25 final pattern etch (etch), 26 final passivation cure + (TD), and 27 final electrical and optical inspection (inspection). + expected_ir_homes: [activities, flow, resources] + traps: + [ + dropping revisits, + converting named operations into separate unshared machine banks, + ] + + - id: machine-groups-and-chamber-counts + importance: load-bearing + epistemic_character: explicit-resource-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The fab has 16 chambers in four groups: LITH-1 through LITH-4, ETCH-1 through ETCH-6, + TD-1 through TD-4, and INSP-1 through INSP-2. + expected_ir_homes: [participants-resources, resources, constraints] + traps: [treating a chamber group as one indivisible machine] + + - id: thermal-deposition-furnace-unified-naming + importance: load-bearing + epistemic_character: explicit-naming-equivalence + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Thermal deposition and furnaces are two names for the same TD-1 through TD-4 group; there is + no separate deposition bank or furnace bank, and every furnace step uses a TD chamber. + expected_ir_homes: [participants-resources, resources, terminology, flow] + traps: [creating separate furnace and deposition resources] + + - id: chamber-and-lot-indivisibility + importance: load-bearing + epistemic_character: explicit-resource-rule + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + A chamber handles one running recipe at a time, except that compatible lots may share a TD + batch; lots are not split, and quality holds or loses a whole rejected lot. + expected_ir_homes: [resources, activities, constraints, flow] + traps: [splitting lots or running concurrent recipes in one chamber] + + - id: family-chamber-qualification-matrix + importance: load-bearing + epistemic_character: explicit-qualification-rule + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Dispatch may use only family-qualified chambers. Logic qualifies on LITH-1, LITH-2, LITH-4; + ETCH-1, ETCH-2, ETCH-3, ETCH-4, ETCH-6; TD-1, TD-2, TD-3; and both inspection chambers. + Memory qualifies on all lithography chambers; ETCH-2 through ETCH-6; TD-1, TD-2, TD-4; and + both inspection chambers. Analog qualifies on LITH-2, LITH-3; ETCH-1, ETCH-3, ETCH-5, + ETCH-6; TD-2, TD-3, TD-4; and INSP-2 only. + expected_ir_homes: [resources, policies, constraints, flow] + traps: [treating chambers within a group as fully interchangeable] + + - id: recipe-duration-patterns + importance: load-bearing + epistemic_character: explicit-time-range + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Duration depends on route position and family. A baseline logic furnace run is roughly five + hours; analog is usually about 15 percent longer and memory about 15 percent shorter. + Lithography is typically around two hours, etch around 90 minutes, and inspection around one + hour, with position-specific variation. + expected_ir_homes: [activities, time, quantities] + traps: + [ + using one fixed duration per chamber group, + treating approximate standards as guarantees, + ] + + - id: recipe-duration-distributions-source + importance: useful + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Leena does not know reliable best-case and bad-day durations for all 28 position/family + pairs; the historian holds the data while production control uses dispatch-screen standards. + expected_ir_homes: [unknowns, time, data-sources, validation] + traps: [turning typical times into complete duration distributions] + + - id: transfer-and-queue-time-semantics + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + reveal_when: asked what happens between route positions or how non-processing time is represented + truth: >- + The pack does not establish transport times, transfer resources, or a general queue-service + discipline between positions beyond the stated dispatch, batch, and tacit priority rules. + expected_ir_homes: [flow, time, resources, unknowns] + traps: [silently assuming zero transfer time or universal FIFO queues] + + - id: analog-insp2-protection + importance: load-bearing + epistemic_character: tacit-unwritten-priority + discoverability: tacit + expert_can_answer: true + reveal_when: asked about exceptions, unwritten priorities, or scarce qualified paths + truth: >- + Because INSP-2 is analog's only qualified inspection path, Leena avoids filling it with + comfortable-due-date logic work when analog lots are within one day of finishing. + expected_ir_homes: [resources, policies, exceptions, situation-notes] + traps: + [ + making the protection absolute, + omitting its one-day and due-date conditions, + ] + + - id: td-batch-start-rule + importance: load-bearing + epistemic_character: explicit-batching-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + A TD batch contains one family and one compatible recipe and starts at four lots or when the + oldest compatible lot has waited three hours, whichever occurs first; timeout batches may + contain one to three lots. + expected_ir_homes: [activities, policies, flow, time, quantities] + traps: [mixing families or recipes, waiting indefinitely for four lots] + + - id: wip-accounting-and-hard-release-stop + importance: load-bearing + epistemic_character: explicit-release-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Total WIP counts running, queued, and quality-held lots; at 50 lots no new customer lot may + be released until a lot ships or is formally scrapped. + expected_ir_homes: [boundary, quantities, policies, constraints, flow] + traps: + [ + excluding held lots from WIP, + releasing merely because a lot finishes processing, + ] + + - id: dispatch-priority-refresh + importance: load-bearing + epistemic_character: explicit-dispatch-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Dispatch priorities refresh every two hours from time remaining to committed due date; among + qualified choices, the lot with least time remaining normally goes first. + expected_ir_homes: [policies, time, triggers, flow] + traps: + [ + treating priorities as continuously recomputed, + ignoring qualification, + making normally absolute, + ] + + - id: late-lot-renegotiation + importance: load-bearing + epistemic_character: practiced-commercial-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Once a lot is 30 hours late, customer planning negotiates a new window of one normal cycle + time and the lot's displayed urgency returns to the ordinary range; Leena regards the board + improvement as cosmetic. + expected_ir_homes: [policies, goals, time, conflicts, measures] + traps: + [ + treating renegotiation as physical recovery or erasing the original lateness, + ] + + - id: finish-one-tie-break + importance: load-bearing + epistemic_character: tacit-unwritten-priority + discoverability: tacit + expert_can_answer: true + reveal_when: asked how similar-urgency lots are broken, how WIP headroom is created, or what the screen misses + truth: >- + For similarly urgent lots, Leena favors the lot farther along the route because shipping one + creates WIP headroom; this tie-break is absent from the dispatch screen. + expected_ir_homes: [policies, exceptions, flow, situation-notes] + traps: + [applying route progress ahead of materially different due-date urgency] + + - id: upstream-release-queue-clutter + importance: load-bearing + epistemic_character: tacit-release-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked about release exceptions, queue clutter, or ways starts are informally throttled + truth: >- + Leena sometimes delays an upstream release by a few hours when it would become the fifth + incompatible lot at a furnace queue, describing this as avoiding queue clutter rather than + throttling starts. + expected_ir_homes: [policies, exceptions, flow, situation-notes] + traps: [turning a sometimes-practice into a mandatory fifth-lot rule] + + - id: forty-six-lot-throughput-belief + importance: useful + epistemic_character: belief-with-qualification + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked why the preferred WIP band should work or what evidence supports it + truth: >- + Leena believes a working band near 46 lots gives the best throughput, but acknowledges this is + control-room experience rather than a comparison separating demand mix, downtime, and + technician availability. + expected_ir_homes: + [beliefs, assumptions, conflicts, validation, situation-notes] + traps: [recording 46 as an established optimum] + + - id: chamber-condition-and-defect-risk + importance: load-bearing + epistemic_character: qualitative-causal-relation + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Chamber health worsens with hours run; particle contamination tends to rise between cleans, + calibration may drift high or low, and worn, dirty, or poorly calibrated chambers are more + likely to fail and add defects. + expected_ir_homes: [resources, state, failures, quality, flow] + traps: + [inventing deterministic degradation or a quantitative defect function] + + - id: chamber-health-thresholds + importance: load-bearing + epistemic_character: explicit-health-threshold + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The maintenance screen turns red at health 0.85; by 0.90 maintenance says failure risk is + roughly eight times that of a freshly serviced chamber. + expected_ir_homes: [resources, state, policies, failures, quantities] + traps: + [ + treating the red threshold as an automatic shutdown, + claiming exact causality from the risk estimate, + ] + + - id: preventive-maintenance-restoration + importance: load-bearing + epistemic_character: explicit-maintenance-process + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Preventive work cleans and recalibrates a chamber, but post-maintenance calibration is not + perfectly centered; process engineering signs the chamber back in after a qualification check. + expected_ir_homes: [activities, resources, state, policies, flow] + traps: + [ + restoring a chamber to mathematically perfect condition, + skipping qualification sign-in, + ] + + - id: deferred-final-inspection-observability + importance: load-bearing + epistemic_character: delayed-observability + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Defects may be added at every route position and travel invisibly with a lot. Final inspection + observes the accumulated result, so a bad chamber may process several later lots before the + first affected lot reaches inspection. + expected_ir_homes: [quality, flow, state, failures, observability] + traps: + [ + detecting defects immediately at their source chamber, + attributing final defects to one visit, + ] + + - id: mid-flow-inspection-limits + importance: load-bearing + epistemic_character: explicit-observability-limit + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Position 15 confirms dimensions and alignment but does not reveal the small contamination and + calibration defects accumulated through the route; those emerge only at position 27. + expected_ir_homes: [activities, quality, observability, flow] + traps: + [ + using the mid-flow check as an early detector for contamination or drift defects, + ] + + - id: defect-attribution-model-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Leena does not know how particle level and calibration drift combine into lost yield or the + exact defect contribution of each chamber visit; maintenance and process engineering own + different pieces of the data. + expected_ir_homes: [unknowns, quality, losses, data-sources, validation] + traps: + [inventing an additive defect model or assigning ownership to one source] + + - id: shared-technician-pool + importance: load-bearing + epistemic_character: explicit-resource-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Three technicians are shared across planned service, breakdown diagnosis, chamber cleans, + and recalibration. Normal TD preventive service uses two; initial fault diagnosis usually + uses one; maintenance assigns named people. + expected_ir_homes: + [participants-resources, resources, activities, constraints, policies] + traps: + [ + creating separate crews by task, + letting production control assign named technicians, + ] + + - id: preventive-maintenance-duration-absence + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: >- + The pack does not establish general preventive-maintenance duration ranges by chamber group + or maintenance type; TD-4's six-hour expectation and current overrun are one incident, not a + general service-time model. + expected_ir_homes: [activities, resources, time, unknowns, data-sources] + traps: + [ + applying TD-4's six-hour expectation to every preventive service, + deriving a maintenance-duration distribution from the current incident, + ] + + - id: failure-and-repair-patterns-source + importance: useful + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: >- + Leena does not know chamber-specific failure frequencies or repair-time ranges by fault; + maintenance owns that evidence. + expected_ir_homes: [unknowns, failures, time, data-sources] + traps: [fitting distributions from the current incident] + + - id: td2-dirtiness-belief + importance: useful + epistemic_character: belief-with-unresolved-attribution + discoverability: tension-probe + expert_can_answer: true + reveal_when: asked which chambers seem problematic and how that conclusion is supported + truth: >- + Leena believes TD-2 is the dirtiest furnace and is behind more rejects, but concedes that + delayed final inspection and each rejected lot's visits to many chambers prevent current + reports from isolating TD-2's contribution. + expected_ir_homes: [beliefs, conflicts, quality, observability, validation] + traps: [presenting TD-2 as the proven cause of excess rejects] + + - id: pm-aligned-with-batch-timeout + importance: useful + epistemic_character: tacit-maintenance-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked about informal maintenance efficiencies, quiet weeks, or experienced-controller practices + truth: >- + On quiet weeks, day-shift controllers informally align preventive work with a furnace batch + timeout so the queue can form while the chamber is down; nobody explicitly schedules it. + expected_ir_homes: [policies, activities, time, situation-notes] + traps: [making the alignment a formal or universally used scheduling rule] + + - id: incident-td2-maintenance-deferral + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Late Sunday TD-2 crossed 0.85. Leena approved one more four-lot memory batch due Wednesday + morning; TD-2 then ran a timed-out three-lot logic batch, after which maintenance planned to + take it. + expected_ir_homes: [situation-notes, state, activities, policies, time] + traps: + [ + claiming the deferral caused the later defects or was known to be wrong at the time, + ] + + - id: incident-td4-overrunning-maintenance + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Two technicians began planned preventive work on TD-4 at 04:30 Tuesday; it was expected back + at 10:30, but at the 13:30 interview its recalibration check was still failing and maintenance + estimated another couple of hours. + expected_ir_homes: [situation-notes, resources, state, time, failures] + traps: [treating the revised estimate as a guaranteed return time] + + - id: incident-etch3-vacuum-fault + importance: useful + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + ETCH-3 developed a vacuum fault at 08:20 Tuesday; the third technician went to diagnose it + and expected the chamber back around 14:00. + expected_ir_homes: [situation-notes, resources, state, time, failures] + traps: ["treating 14:00 as certain or omitting the technician contention"] + + - id: incident-inspection-failures-and-quarantine + importance: load-bearing + epistemic_character: explicit-incident-fact + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + INSP-1 rejected memory lot M-442 at 08:10 for an unusual particle-related defect count, and + M-447 from the same Sunday TD-2 batch also failed at 09:00. Quality stopped TD-2 at 09:10 + and quarantined all seven lots processed there since the last accepted final-inspection + result. + expected_ir_homes: [situation-notes, quality, state, triggers, flow] + traps: + [ + claiming TD-2 caused the failures, + saying all seven quarantined lots are defective, + ] + + - id: incident-current-wip-and-queues + importance: load-bearing + epistemic_character: explicit-incident-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + WIP rose from 43 lots Monday morning to 49 at 13:30 Tuesday; 11 lots wait for a furnace and + nine for etch, with the rest running, queued elsewhere, or held by quality. + expected_ir_homes: [situation-notes, state, quantities, flow] + traps: + [ + assuming all remaining lots are running or excluding holds from the count, + ] + + - id: incident-release-slot-reserved + importance: load-bearing + epistemic_character: practiced-incident-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + New releases are effectively frozen at WIP 49 because Leena is preserving the one remaining + slot for a genuinely urgent customer start. + expected_ir_homes: [situation-notes, policies, constraints, flow] + traps: + [calling this the formal 50-lot hard stop, assuming the slot must be used] + + - id: incident-held-lot-deadlines-and-td-state + importance: load-bearing + epistemic_character: explicit-incident-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + Two quarantined logic lots are due Tuesday night and three memory lots Wednesday morning. + TD-1 is running memory, TD-3 is in a long analog run, TD-4 remains under maintenance, and + TD-2 cannot return until technicians clean, inspect, and recalibrate it. + expected_ir_homes: [situation-notes, resources, state, time, constraints] + traps: + [ + treating any TD chamber as immediately available or ignoring family qualifications, + ] + + - id: incident-control-room-decision + importance: load-bearing + epistemic_character: explicit-decision-conflict + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + The immediate choice is whether to pull technicians from TD-4 to recover TD-2, finish TD-4 + first, or leave both alone until ETCH-3 is restored; customer planning wants logic expedited, + while quality will not release quarantined lots without a disposition. + expected_ir_homes: + [purpose, situation-notes, conflicts, policies, validation] + traps: + [ + silently selecting a winner or treating one stakeholder preference as plant policy, + ] + + - id: incident-cause-and-extent-unknown + importance: load-bearing + epistemic_character: explicit-current-unknown + discoverability: tension-probe + expert_can_answer: false + reveal_when: asked what the failures prove, what remains uncertain, or how broad the excursion is + truth: >- + It is unknown whether TD-2 caused the two inspection failures, how many of the seven held lots + are defective, or whether additional affected lots remain upstream of final inspection. + expected_ir_homes: + [unknowns, quality, failures, situation-notes, validation] + traps: + [ + asserting causality from sequence and shared batch, + bounding the excursion at seven lots, + ] + + - id: incident-finish-td4-heuristic + importance: load-bearing + epistemic_character: tacit-heuristic-without-data + discoverability: tacit + expert_can_answer: true + reveal_when: asked how Leena would decide or what experienced controllers see beyond written policy + truth: >- + Leena's instinct is to finish TD-4 because abandoning a half-completed calibration often turns + a six-hour service into an all-day one, but maintenance has never supplied data for that rule. + expected_ir_homes: + [beliefs, policies, conflicts, situation-notes, validation] + traps: + [ + presenting the heuristic as measured fact or the decided incident response, + ] + + - id: held-lot-disposition-process-absence + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + reveal_when: asked how quarantine ends, how long disposition takes, or what outcomes are possible + truth: >- + The pack establishes that quality requires a disposition before release but does not specify + disposition timing, decision criteria, rework possibilities, or the probabilities of release + versus scrap. + expected_ir_homes: [quality, flow, policies, time, unknowns] + traps: + [assuming held lots are automatically released, scrapped, or reworked] + + - id: maintenance-contention-priority-absence + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + reveal_when: asked for the formal rule that resolves competing repair, PM, and production demands + truth: >- + The pack provides stakeholder positions and Leena's unsupported heuristic but no settled rule + for prioritizing technicians among TD-2 recovery, TD-4 completion, and ETCH-3 diagnosis. + expected_ir_homes: [policies, conflicts, unknowns, validation] + traps: + [ + promoting the current instinct or customer request into a general priority rule, + ] + + - id: customer-start-process-absence + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + reveal_when: asked how future starts should be generated or forecast in simulation + truth: >- + The pack says customer starts arrive unevenly but does not establish arrival distributions, + family mix over time, urgency-class frequencies, or a forecast source. + expected_ir_homes: [triggers, inputs, quantities, unknowns, data-sources] + traps: [inventing a Poisson process or fixed product mix] + + - id: chamber-health-evolution-absence + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + reveal_when: asked how health, contamination, and drift should evolve numerically between services + truth: >- + The pack gives qualitative degradation and two health reference points but no numerical + evolution law linking run hours, contamination, calibration drift, failure, and defects. + expected_ir_homes: [state, failures, quality, unknowns, validation] + traps: + [interpolating a deterministic health or yield curve from 0.85 and 0.90] diff --git a/libs/@hashintel/brunch-agent/evaluations/oracles/truck-fleet-maintenance/truth-ledger-v1-prospective.yaml b/libs/@hashintel/brunch-agent/evaluations/oracles/truck-fleet-maintenance/truth-ledger-v1-prospective.yaml new file mode 100644 index 00000000000..a6a31f36e2b --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/oracles/truck-fleet-maintenance/truth-ledger-v1-prospective.yaml @@ -0,0 +1,616 @@ +version: 1-prospective +case: calder-ridge-carriers-truck-fleet-maintenance +source: evaluations/cases/truck-fleet-maintenance/situation-pack.md +provenance: + authored_after_historical_runs: false + calibrated_against_historical_runs: false + frozen_before_prospective_runs: true +use: prospective baseline and variant grading only +warning: >- + This greenfield ledger was authored only from the completed situation pack. It was not informed + by or calibrated against historical runs and is frozen before prospective runs. +importance_weights: + load-bearing: 3 + useful: 2 + incidental: 1 + +calibration_rules: + transcript_precedence: >- + Grade what the simulated expert actually disclosed. Ledger truth may identify a distinction, + but it does not erase hedges, narrower examples, corrections, or nondisclosure in the transcript. + interviewer_proposals: >- + A value or interpretation appearing only in an interviewer question is not user evidence unless + the expert explicitly adopts it. + relevant_absences: >- + Entries whose epistemic_character is relevant-absence describe a relation the pack does not + settle. Credit elicitation that identifies the gap; do not expect the expert or IR to fill it. + hard_stop: >- + When the expert ends elicitation, distinguish unanswered suitable questions from earlier missed + opportunities. Do not convert turn-budget truncation into an ordinary acquisition miss. + partial_disclosure: >- + A concrete example inside a ledger range is partial evidence, not simulator failure by itself. + Penalize only unsupported generalization beyond the disclosed example. + +facts: + - id: objective-weekly-service-schedule + importance: load-bearing + epistemic_character: explicit-goal + discoverability: direct-if-asked + expert_can_answer: true + truth: Decide which pilot trucks to service this week, on which day, and in what order. + expected_ir_homes: [purpose, goals, validation] + traps: [reducing the objective to ranking trucks by dashboard score] + + - id: objective-risk-tradeoff + importance: load-bearing + epistemic_character: explicit-preference + discoverability: direct-if-asked + expert_can_answer: true + truth: Compare the delivery risk and breakdown risk of plausible weekly schedules. + expected_ir_homes: [goals, measures, validation] + traps: + [reducing the comparison to dashboard-score order, omitting delivery risk] + + - id: objective-route-reassignment + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Determine when route reassignment is sufficient and when a truck must come off the road. + expected_ir_homes: [purpose, policies, validation] + traps: [hard-coding a rule that the work is meant to test] + + - id: objective-highest-score-policy + importance: load-bearing + epistemic_character: explicit-decision-question + discoverability: direct-if-asked + expert_can_answer: true + truth: Test whether highest-score-first remains a good weekly policy once delivery, driver, parts, and workshop constraints are included. + expected_ir_homes: [purpose, policies, validation] + traps: [assuming the current heuristic is optimal] + + - id: objective-recovery-capacity-argument + importance: useful + epistemic_character: tacit-goal + discoverability: tacit + expert_can_answer: true + reveal_when: asked what evidence Nora needs, what loses on busy days, or what hidden outcome the work should support + truth: Nora wants evidence for keeping recovery capacity free on mountain days. + expected_ir_homes: [purpose, goals, situation-notes] + traps: [presenting reserved recovery capacity as an agreed policy] + + - id: pilot-fleet-scope + importance: load-bearing + epistemic_character: explicit-taxonomy + discoverability: direct-if-asked + expert_can_answer: true + truth: The pilot comprises CR-12, CR-19, CR-27, CR-34, CR-41, CR-53, CR-68, and CR-72. + expected_ir_homes: [boundary, participants-resources] + traps: [generalizing pilot conclusions to the rest of the fleet] + + - id: objective-maintenance-timing + importance: load-bearing + epistemic_character: explicit-preference + discoverability: direct-if-asked + expert_can_answer: true + truth: Avoid roadside failure without servicing healthy trucks early and wasting workshop time or usable component life. + expected_ir_homes: [goals, measures, validation] + traps: + [ + treating early service as costless, + maximizing workshop utilization regardless of truck condition, + ] + + - id: planning-cadence-and-horizon + importance: load-bearing + epistemic_character: explicit-boundary + discoverability: direct-if-asked + expert_can_answer: true + truth: Nora needs a weekly schedule but currently makes a day-ahead plan around 16:00 and revises it after load or breakdown disruptions. + expected_ir_homes: [boundary, time, triggers] + traps: + [modelling only a static weekly plan, modelling only day-ahead dispatch] + + - id: executable-schedule-integration-absence + importance: useful + epistemic_character: explicit-system-absence + discoverability: direct-if-asked + expert_can_answer: true + truth: The dashboard supplies risk scores but neither produces an optimized weekly schedule nor automatically reserves workshop slots. + expected_ir_homes: [boundary, gaps, losses] + traps: [assuming a dashboard alert creates a maintenance booking] + + - id: risk-score-semantics + importance: load-bearing + epistemic_character: explicit-measure-semantics + discoverability: direct-if-asked + expert_can_answer: true + truth: The dashboard reports separate 0–100 seven-day failure-risk scores for brakes, engine, and tyres, with higher values worse and 80 shown in red. + expected_ir_homes: [inputs, measures, time] + traps: + [ + combining the component scores without support, + calling the scores probabilities, + ] + + - id: risk-score-calibration-unknown + importance: load-bearing + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Nora does not know the score calibration, a score's failure probability, or whether different component scores are directly comparable. + expected_ir_homes: [unknowns, measures, validation] + traps: + [ + interpreting 80 as an 80 percent chance, + comparing score magnitudes across components as calibrated risk, + ] + + - id: red-line-belief-qualification + importance: load-bearing + epistemic_character: belief-with-qualification + discoverability: tension-probe + expert_can_answer: true + truth: Nora calls 80 the pull-it-now line, but it is a strong warning rather than a written no-dispatch rule. + expected_ir_homes: [policies, conflicts-or-qualification, situation-notes] + traps: + [ + hardening Nora's phrase into a formal dispatch ban, + omitting that she has made short-flat-run exceptions, + ] + + - id: highest-component-concern + importance: load-bearing + epistemic_character: explicit-practice + discoverability: direct-if-asked + expert_can_answer: true + truth: Nora bases concern on the highest-risk component rather than letting low readings on other components offset it. + expected_ir_homes: [policies, measures] + traps: [averaging the three component scores] + + - id: monday-risk-snapshot + importance: load-bearing + epistemic_character: explicit-initial-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + At Monday 06:00 the brake/engine/tyre scores are CR-12 82/38/41, CR-19 44/77/36, + CR-27 63/40/71, CR-34 58/52/49, CR-41 37/46/30, CR-53 31/46/35, CR-68 22/28/26, + and CR-72 65/34/39. + expected_ir_homes: [initial-state, inputs, quantities] + traps: + [ + treating the snapshot as stationary for the whole week, + changing values to fit the red threshold, + ] + + - id: monday-truck-planning-state + importance: load-bearing + epistemic_character: explicit-initial-state + discoverability: direct-if-asked + expert_can_answer: true + truth: >- + At Monday 06:00 CR-12 is at the depot and normally first choice for Tuesday's mountain + contract; CR-19 is assigned to Wednesday's loaded motorway contract; CR-27 is working urban + board loads; CR-34 returns from a mountain run Monday afternoon; CR-41 is available for + motorway or urban work; CR-53 is back after last month's roadside repair; CR-68 is recently + serviced and available; and CR-72 is at the depot after an urban night run with its driver's + hours nearly used. + expected_ir_homes: [initial-state, resources, demand, time] + traps: + [ + ranking trucks from risk scores without their assignments and availability, + assigning CR-12 elsewhere without preserving the Tuesday mountain conflict, + treating CR-72 as immediately dispatchable, + ] + + - id: score-change-behaviour + importance: useful + epistemic_character: qualitative-dynamics + discoverability: direct-if-asked + expert_can_answer: true + truth: Scores usually change gradually but can jump after a fault code or severe trip. + expected_ir_homes: [activities, time, failures] + traps: [inventing a deterministic score-update equation] + + - id: motorway-route-wear + importance: useful + epistemic_character: explicit-route-effect + discoverability: direct-if-asked + expert_can_answer: true + truth: A motorway run is about 420 km and mainly flat; long loaded runs stress engines while brake use is lower. + expected_ir_homes: [activities, flow, measures] + traps: [treating motorway work as uniformly low wear] + + - id: urban-route-wear + importance: useful + epistemic_character: explicit-route-effect + discoverability: direct-if-asked + expert_can_answer: true + truth: An urban run is about 180 km; stop-start driving raises brake wear and kerbs and rough streets are hard on tyres. + expected_ir_homes: [activities, flow, measures] + traps: [representing urban work with engine wear alone] + + - id: mountain-route-wear + importance: load-bearing + epistemic_character: explicit-route-effect + discoverability: direct-if-asked + expert_can_answer: true + truth: A mountain run is about 260 km and brake deterioration on descents is roughly 2.5 times the flat baseline. + expected_ir_homes: [activities, flow, measures] + traps: + [ + applying the multiplier to every component, + treating the rough multiplier as exact, + ] + + - id: trip-severity-multipliers-unknown + importance: useful + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: Nora lacks measured multipliers for weather and road roughness; telematics history may support later analysis. + expected_ir_homes: [unknowns, data-sources, measures] + traps: + [ + inventing weather multipliers, + treating route class as a complete explanation of wear, + ] + + - id: mountain-brake-belief-counterexamples + importance: useful + epistemic_character: belief-with-counterexamples + discoverability: tension-probe + expert_can_answer: true + truth: Nora believes the mountain is always the brake killer, but recalls engine-dominant motorway and tyre-dominant urban counterexamples. + expected_ir_homes: [conflicts-or-qualification, measures, situation-notes] + traps: + [ + preserving only the slogan, + generalizing either counterexample to all trips, + ] + + - id: committed-weekly-jobs + importance: load-bearing + epistemic_character: explicit-demand + discoverability: direct-if-asked + expert_can_answer: true + truth: The pilot must cover an early-Tuesday mountain contract, a Wednesday loaded motorway contract, and at least one urban round most weekdays. + expected_ir_homes: [triggers, demand, time] + traps: [treating every future job as freight-board demand] + + - id: freight-board-acceptance-window + importance: load-bearing + epistemic_character: explicit-time-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: Calder Ridge has 10 hours to accept and collect a posted freight-board load before it goes to a competitor. + expected_ir_homes: [triggers, time, policies] + traps: + [ + measuring the window from collection to delivery, + assuming every posted load is accepted, + ] + + - id: freight-board-arrivals-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Exact freight-board arrival times are unknown at the start of the week. + expected_ir_homes: [unknowns, triggers, scenarios] + traps: [inventing a measured arrival distribution] + + - id: delivery-window + importance: load-bearing + epistemic_character: explicit-time-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: After collection, a load's delivery window is roughly 2.2 times its normal driving time. + expected_ir_homes: [time, goals, constraints] + traps: + [ + starting the delivery window when the load is posted, + treating the factor as exact, + ] + + - id: late-delivery-penalty + importance: useful + epistemic_character: explicit-loss + discoverability: direct-if-asked + expert_can_answer: true + truth: Delivery outside the window costs about 30 percent of the load's revenue. + expected_ir_homes: [losses, goals, measures] + traps: + [ + treating the amount as exact, + applying it to a load that fails and cannot complete, + ] + + - id: roadside-load-loss + importance: load-bearing + epistemic_character: explicit-loss + discoverability: direct-if-asked + expert_can_answer: true + truth: If a truck fails mid-route and cannot complete, Calder Ridge loses the load and pays recovery costs. + expected_ir_homes: [failures, losses, goals] + traps: [substituting the late-delivery penalty for total load loss] + + - id: harrowell-priority + importance: load-bearing + epistemic_character: tacit-practiced-priority + discoverability: tacit + expert_can_answer: true + reveal_when: asked which work gets protected, how equal-revenue jobs differ, or what happens when commitments conflict + truth: Harrowell Foods' mountain contract is protected before spot-market work even when immediate revenue is similar. + expected_ir_homes: [goals, policies, validation] + traps: + [ + treating all loads as revenue-equivalent, + inventing a numeric Harrowell weight, + ] + + - id: commercial-tradeoff-weights-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: No agreed single monetary weights exist for lateness, refused loads, customer damage, early replacement, or roadside failure; finance only has separate line items. + expected_ir_homes: [unknowns, losses, validation] + traps: + [ + forcing a single measured objective function, + treating finance line items as agreed tradeoff weights, + ] + + - id: driver-hours-and-rest + importance: load-bearing + epistemic_character: explicit-policy + discoverability: direct-if-asked + expert_can_answer: true + truth: Dispatch blocks a truck-driver pairing at 9 driving hours until the driver has rested 11 hours at the depot. + expected_ir_homes: [resources, time, policies] + traps: + [ + attaching the rest state to the truck alone, + allowing immediate redispatch of the same pairing, + ] + + - id: cr72-driver-pairing-state + importance: useful + epistemic_character: explicit-initial-state + discoverability: direct-if-asked + expert_can_answer: true + truth: CR-72 can be physically available Monday while its night-driver pairing is unavailable because the driver has reached the hours limit. + expected_ir_homes: [initial-state, resources, time] + traps: [treating truck availability as sufficient dispatch capacity] + + - id: driver-roster-unknown + importance: load-bearing + epistemic_character: explicit-unknown-with-source + discoverability: direct-if-asked + expert_can_answer: false + truth: Nora cannot provide reliable driver absence or swap rates; the transport manager owns the roster. + expected_ir_homes: [unknowns, data-sources, resources] + traps: [assuming unlimited rested-driver substitution] + + - id: spare-truck-driver-tension + importance: load-bearing + epistemic_character: tacit-practice + discoverability: tacit + expert_can_answer: true + reveal_when: asked how dispatch verifies cover, what spare truck means in practice, or why a physically available unit cannot cover a run + truth: Nora checks for a legal rested driver as well as a spare truck before promising that another unit can cover a run. + expected_ir_homes: [policies, resources, situation-notes] + traps: [equating a spare unit with dispatchable cover] + + - id: workshop-bay-capacity + importance: load-bearing + epistemic_character: explicit-resource-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: The depot workshop has two service bays. + expected_ir_homes: [resources, constraints] + traps: [treating two bays as two independently feasible jobs] + + - id: technician-skill-matrix + importance: load-bearing + epistemic_character: explicit-resource-constraint + discoverability: direct-if-asked + expert_can_answer: true + truth: Priya handles most engine and electrical work, while Milo handles most brake and tyre work and is the certified recovery operator. + expected_ir_homes: [resources, policies] + traps: [treating technicians as interchangeable] + + - id: recovery-skill-contention + importance: load-bearing + epistemic_character: explicit-resource-contention + discoverability: direct-if-asked + expert_can_answer: true + truth: When Milo takes the sole recovery vehicle, brake and tyre work waits even if a bay is empty. + expected_ir_homes: [resources, flow, situation-notes] + traps: + [ + modelling recovery vehicle and Milo as independent resources, + using empty bays as proof that service can start, + ] + + - id: monday-parts-stock + importance: load-bearing + epistemic_character: explicit-initial-state + discoverability: direct-if-asked + expert_can_answer: true + truth: Monday pilot stock contains one brake kit, one matched steer-tyre set, and one engine sensor/actuator pack; routine fluids and filters are unconstrained. + expected_ir_homes: [initial-state, resources, quantities] + traps: + [ + treating all parts as unconstrained, + splitting the matched tyre set without support, + ] + + - id: planned-service-duration + importance: load-bearing + epistemic_character: explicit-duration + discoverability: direct-if-asked + expert_can_answer: true + truth: A straightforward planned service takes about 5 hours when the truck, suitable technician, bay, and part are ready. + expected_ir_homes: [activities, time, prerequisites] + traps: + [ + using 5 hours without its readiness conditions, + treating the rough duration as deterministic, + ] + + - id: planned-service-restoration + importance: useful + epistemic_character: explicit-state-transition + discoverability: direct-if-asked + expert_can_answer: true + truth: Planned service makes the truck fully fit for the components attended. + expected_ir_homes: [activities, flow, measures] + traps: + [ + resetting unattended components, + treating planned service as whole-truck replacement, + ] + + - id: roadside-service-flow + importance: load-bearing + epistemic_character: explicit-failure-flow + discoverability: direct-if-asked + expert_can_answer: true + truth: A roadside truck must be recovered and towed before a roughly 12-workshop-hour depot repair. + expected_ir_homes: [failures, activities, flow, time] + traps: + [ + starting depot repair at roadside failure, + using the planned-service duration, + ] + + - id: roadside-restoration-extent-unknown + importance: useful + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: The pack says roadside repair restores only part of lost condition but does not quantify the resulting component state. + expected_ir_homes: [unknowns, measures, assumptions] + traps: + [ + resetting the attended component to fully fit, + inventing a partial-restoration percentage, + ] + + - id: planned-roadside-resource-contention + importance: load-bearing + epistemic_character: explicit-resource-contention + discoverability: direct-if-asked + expert_can_answer: true + truth: Planned and roadside work compete for the same bays, technicians, and parts, including parts reserved for later planned jobs. + expected_ir_homes: [resources, flow, failures] + traps: [giving roadside work a separate workshop or parts pool] + + - id: parts-lead-time-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Parts usually arrive next day but specialized items may take several days, and Nora has no reliable delay profile by part. + expected_ir_homes: [unknowns, time, scenarios] + traps: [turning the qualitative statement into a fitted distribution] + + - id: two-bay-belief-qualification + importance: load-bearing + epistemic_character: belief-with-qualification + discoverability: tension-probe + expert_can_answer: true + truth: Nora's belief that two planned trucks can run together holds only when Priya and Milo can work independently and both parts are available. + expected_ir_homes: [resources, conflicts-or-qualification, situation-notes] + traps: + [ + equating bay count with simultaneous-job capacity, + omitting recovery and cross-skill interference, + ] + + - id: cr53-incident-trigger-and-outcome + importance: useful + epistemic_character: concrete-incident + discoverability: direct-if-asked + expert_can_answer: true + truth: CR-53 entered a loaded mountain run with brake score 74, seized a caliper on the second descent, and could not complete the load. + expected_ir_homes: [failures, scenarios, validation] + traps: + [ + changing the pre-run score to a red value, + claiming the dashboard predicted the seizure, + ] + + - id: cr53-incident-workshop-cascade + importance: useful + epistemic_character: concrete-incident + discoverability: direct-if-asked + expert_can_answer: true + truth: CR-53's recovery and repair consumed Milo, a bay, and the only brake kit, displacing CR-27's tyre job for two days. + expected_ir_homes: [failures, resources, flow, validation] + traps: + [ + treating the incident as isolated to CR-53, + omitting the deferred planned job, + ] + + - id: incident-recording-absence + importance: useful + epistemic_character: explicit-recording-absence + discoverability: direct-if-asked + expert_can_answer: true + truth: The incident invoice omits dispatcher time, deferred service, and board loads declined while CR-53 was unavailable. + expected_ir_homes: [losses, gaps, data-sources] + traps: [treating invoice totals as complete incident cost] + + - id: incident-score-predictiveness-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Nora does not know whether the dashboard should have predicted the seizure or whether score 74 was badly calibrated. + expected_ir_homes: [unknowns, validation, measures] + traps: [using the incident as proof that 74 predicts brake failure] + + - id: harrowell-brake-threshold + importance: load-bearing + epistemic_character: tacit-unwritten-rule + discoverability: tacit + expert_can_answer: true + reveal_when: asked how the CR-53 incident changed dispatch, what exceptions govern Harrowell, or which truck Nora would refuse for the mountain run + truth: Nora will not put a truck with brake score above 70 on Harrowell's mountain run. + expected_ir_homes: [policies, constraints, situation-notes] + traps: + [ + applying the rule to every route or customer, + calling it a written dispatch-system rule, + ] + + - id: route-reassignment-criteria-absence + importance: load-bearing + epistemic_character: relevant-absence + discoverability: tension-probe + expert_can_answer: not-established + truth: The pack does not establish general criteria for when reassignment is sufficient versus when a truck must be removed from service. + expected_ir_homes: [unknowns, policies, validation] + traps: + [ + promoting the Harrowell brake rule into a complete fleet-wide criterion, + inventing component-specific cutoffs, + ] + + - id: disruption-duration-distributions-unknown + importance: useful + epistemic_character: explicit-unknown + discoverability: direct-if-asked + expert_can_answer: false + truth: Nora cannot provide reliable best, typical, and worst failure, tow, repair, or parts-replenishment times. + expected_ir_homes: [unknowns, time, data-sources] + traps: + [ + fitting distributions from the CR-53 incident, + treating the incident's four-hour recovery absence as universal, + ] diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/ARCHIVE.md new file mode 100644 index 00000000000..8b82f227224 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/ARCHIVE.md @@ -0,0 +1,17 @@ +# Archived source resolution + +This frozen protocol retains the path names used when it ran. The temporary workbench was later removed after its decisions were promoted. + +Resolve historical source paths at base revision `5249a73f09977ad2ef007e08de7b7314f94568e1`, for example: + +```text +5249a73f09977ad2ef007e08de7b7314f94568e1:libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md +``` + +Because the executed worktree was dirty, the exact instrument is defined by +`docs/evidence/evaluations/flue-skill-composition-side-quest-v1/manifest.json`. The raw runs were +coherently retired from their expanded live-tree paths without editing them; the complete +compressed corpus, ordered path/content identities, and recovery procedure are in +[`flue-skill-composition-side-quest.md`](../../../docs/archive/evaluations/flue-skill-composition-side-quest.md). +Manifest `runs/...` references resolve inside that archive. Do not rewrite the protocol or manifest +to current paths. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/protocol.md new file mode 100644 index 00000000000..1d610895dc2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v1/protocol.md @@ -0,0 +1,103 @@ +# Flue skill-composition side-quest v1 + +## Claim + +This bounded probe compares two progressive-disclosure topologies through the production +`ChatAgent` composition seam. It tests mounting, routing precision, and whether the first +consequential action composes universal elicitation judgment with SDCPN operational-process +judgment. It does not establish general reliability or overall elicitation superiority. + +## Frozen inputs + +- Scenarios: `evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json` +- Evaluator-only rubric: `evaluations/oracles/flue-skill-composition-side-quest-v1.md` +- Universal source: + `packages/core/_drafts/ampcode/core/universal-elicitation.md` +- Plugin source: + `packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/` +- Always-on instructions: the current production core `SYSTEM.md` and plugin `useInstruction` + contribution. +- Model: the current production default, `anthropic/claude-haiku-4-5`. +- Construction tools: absent in every scenario. + +The manifest records content hashes from the source revision actually run. Any changed hash +creates a different instrument. + +## Candidates + +### A — independent core capability + +Mount `sdcpn-modelling` and `elicitation`. The `elicitation` skill uses the universal source as +its complete substantive instructions. Its wrapper contributes only the name and this activation +cue: + +> Use when progress on the active job requires knowledge that only a person can provide. Supplies +> adaptive elicitation judgment across domains and target formalisms; do not activate when the +> available evidence already supports the requested operation. + +The plugin skill replaces exactly one routing sentence: + +> Activate `elicitation` and read `sdcpn-elicitation.md` before substantive questions or revision. + +### B — plugin-packaged universal resource + +Mount only `sdcpn-modelling`. Package the same universal bytes as +`universal-elicitation.md`. Retain the source routing sentence: + +> Read `universal-elicitation.md` and `sdcpn-elicitation.md` before substantive questions or +> revision. + +No other substantive plugin difference is permitted. + +### A-missing — intentional misconfiguration + +Mount Candidate A's plugin skill without `elicitation`. Observe Flue's native behavior; add no +dependency framework or fallback protocol. + +## Hermetic phase + +Use the built production app with a pi-ai faux provider. Exercise S1–S5, prescribing calls only to +prove catalog mounting, activation, resource access, trace observability, absence of hidden +universal disclosure, and missing-capability behavior. Retain the raw snapshot and observed Flue +events. Faux outputs are not evidence of model judgment. + +The evaluator gate requires: + +1. candidate parity checks pass; +2. every run crosses the same built `ChatAgent`; +3. S1/S4 acquire universal content and S2/S3 do not; +4. S2 is accepted as sufficient for its first construction decision; +5. S5 records the native failure shape; +6. raw tool inputs, outputs, usage, and latency are recoverable; and +7. no production resource or frozen Mission 3 artifact changed. + +## Paid mechanism smoke + +After the hermetic gate, run S1 and S2 once per candidate in this order: A/S1, B/S1, A/S2, B/S2. +Each scenario is one evaluation run through `ChatAgent`; Flue may make multiple provider calls +within that run to service model-selected tools, all of which must be recorded. + +Stop each run after its first consequential question or construction decision. Before each run, +confirm fewer than four paid runs have been dispatched and recorded total provider cost is below +USD 1.00. Stop immediately on mechanical failure, candidate path asymmetry, missing raw trace, +shared-content defect, or when the next run cannot safely remain within the ceiling. + +No paid S3/S4 replication, repeated run, or model judge is authorized. + +## Evidence layout + +After the hermetic gate, write immutable evidence to +`docs/evidence/evaluations/flue-skill-composition-side-quest-v1/`: + +```text +manifest.json +runs/ + hermetic/<candidate>-<scenario>.json + paid/<candidate>-<scenario>.json +comparison.md +``` + +Each run records the raw Flue snapshot, observed events, first consequential output, tool calls +and results, resource paths, loaded-content hashes, provider usage, latency, cost, and failure +shape. The manifest records the source commit, dirty state, source and rendered hashes, runtime +configuration, fixture hashes, and all intentional differences. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/ARCHIVE.md new file mode 100644 index 00000000000..937bc925a7a --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/ARCHIVE.md @@ -0,0 +1,17 @@ +# Archived source resolution + +This frozen protocol inherits the path names used by v1. The temporary workbench was later removed after its decisions were promoted. + +Resolve historical source paths at base revision `5249a73f09977ad2ef007e08de7b7314f94568e1`, for example: + +```text +5249a73f09977ad2ef007e08de7b7314f94568e1:libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md +``` + +Because the executed worktree was dirty, the exact instrument is defined by +`docs/evidence/evaluations/flue-skill-composition-side-quest-v2/manifest.json`. The raw runs were +coherently retired from their expanded live-tree paths without editing them; the complete +compressed corpus, ordered path/content identities, and recovery procedure are in +[`flue-skill-composition-side-quest.md`](../../../docs/archive/evaluations/flue-skill-composition-side-quest.md). +Manifest `runs/...` references resolve inside that archive. Do not rewrite the protocol or +manifest to current paths. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/protocol.md new file mode 100644 index 00000000000..9c51bff0055 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v2/protocol.md @@ -0,0 +1,88 @@ +# Flue skill-composition side-quest v2 + +## Reason for a new instrument + +V1 proved both topologies mechanically, then stopped after both real-model S1 runs activated +`sdcpn-modelling` but ignored its required next disclosure. Repetition could not distinguish +topology. V2 is the user-authorized discrimination instrument: it strengthens only the shared +routing gate, freezes that change before use, and preserves v1 evidence unchanged. + +## Frozen inheritance + +V2 inherits without modification: + +- scenarios from + `evaluations/cases/flue-skill-composition-side-quest-v1/scenarios.json`; +- the evaluator-only rubric from + `evaluations/oracles/flue-skill-composition-side-quest-v1.md`; +- universal instructions and every plugin resource from the v1 source paths; +- production core and plugin always-on instructions; +- `anthropic/claude-haiku-4-5`; +- the production `ChatAgent` seam, tool availability, and stopping points; and +- the candidate topology difference: independent `activate_skill` versus packaged + `read_skill_resource`. + +## Shared routing-gate intervention + +In both candidates, replace the v1 interactive routing sentence with this block: + +> #### Required disclosure gate +> +> Before any consequential question, finding, revision, or construction decision, decide whether +> progress requires human knowledge that the available evidence does not contain. +> +> REQUIRED_ACTION +> +> If human knowledge is required, complete the required universal-disclosure action and read +> `sdcpn-elicitation.md` before asking one focused question. Do not ask first and load guidance +> later. +> +> If the supplied evidence already supports the requested operation, do not perform the +> universal-disclosure action. Continue with the relevant plugin construction or checking +> resources without asking an avoidable question. + +`REQUIRED_ACTION` is the only candidate-specific text: + +- A: `Required universal-disclosure action for this candidate: activate the mounted + \`elicitation\` skill.` +- B: `Required universal-disclosure action for this candidate: read + \`universal-elicitation.md\` from this skill.` + +Normalizing that line must make the plugin instructions byte-identical. The universal body must +remain byte-identical between A's skill instructions and B's resource. + +## Hermetic gate + +Exercise A and B on S1, S2, S3, and S4 through the built app with the faux provider. Require the +same mounting/access/absence proofs as v1 and additionally prove the rendered v2 plugin texts +differ only at `REQUIRED_ACTION`. + +## Paid design and budget + +V1 consumed 4 model invocations and USD 0.0241825. The user-authorized cumulative ceilings are 48 +model invocations and USD 1.00, leaving at most 44 calls and USD 0.9758175. + +Run in paired order: + +1. A/S1 and B/S1 once. Stop if both again fail before candidate-specific disclosure. +2. A/S4 and B/S4 once. +3. A/S2 and B/S2 once. +4. Repeat A/S1, B/S1, A/S4, and B/S4 once each if the first pair discriminates. + +Each Flue provider call counts as one invocation. Before dispatching another scenario, reserve +four calls for its expected activation/resource loop. Stop at the first consequential action and +stop immediately on a mechanical failure, path asymmetry, missing raw trace, shared-content +failure, 48th cumulative call, or USD 1.00 total cost. + +## Decision rule + +- A is viable and preferred if it passes both required-disclosure scenarios twice, passes S2 + restraint, and B does not materially outperform it. +- A is falsified with B as fallback if B passes those gates while A exhibits repeated + independent-activation or composition strain attributable to topology. +- Both are behaviorally viable with bounded uncertainty if both pass the paired gates. +- The probe remains invalid/inconclusive if both fail the shared gate or evidence cannot isolate + topology. + +No general reliability claim, paid S3/S5, model judge, post-run wording revision, or tie-breaking +campaign is authorized. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/ARCHIVE.md new file mode 100644 index 00000000000..e536345aaa3 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/ARCHIVE.md @@ -0,0 +1,17 @@ +# Archived source resolution + +This frozen protocol retains the path names used when it ran. The temporary workbench was later removed after its decisions were promoted. + +Resolve historical source paths at base revision `5249a73f09977ad2ef007e08de7b7314f94568e1`, for example: + +```text +5249a73f09977ad2ef007e08de7b7314f94568e1:libs/@hashintel/brunch-agent/packages/core/_drafts/ampcode/core/universal-elicitation.md +``` + +Because the executed worktree was dirty, the exact instrument is defined by +`docs/evidence/evaluations/flue-skill-composition-side-quest-v3/manifest.json`. The raw runs were +coherently retired from their expanded live-tree paths without editing them; the complete +compressed corpus, ordered path/content identities, and recovery procedure are in +[`flue-skill-composition-side-quest.md`](../../../docs/archive/evaluations/flue-skill-composition-side-quest.md). +Manifest `runs/...` references resolve inside that archive. Do not rewrite the protocol or +manifest to current paths. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/protocol.md new file mode 100644 index 00000000000..0fbed57ef61 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/flue-skill-composition-side-quest-v3/protocol.md @@ -0,0 +1,82 @@ +# Flue skill-composition side-quest v3 + +## Purpose + +V3 removes two confounds observed in v2 while preserving the topology manipulation. It does not +rewrite either candidate after seeing v3 output. + +## Frozen instrument + +- Core prompt for both candidates: + `packages/core/_drafts/ampcode/core/SYSTEM.md` +- Universal substance for A instructions and B resource: + `packages/core/_drafts/ampcode/core/universal-elicitation.md` +- Plugin job and resources: + `packages/core/_drafts/ampcode/plugin-sdcpn/sdcpn-modelling/` +- Shared append source: + `packages/core/_drafts/ampcode/plugin-sdcpn/APPEND_SYSTEM.md` +- Scenarios: + `evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json` +- Oracle: + `evaluations/oracles/flue-skill-composition-side-quest-v3.md` +- Model: `anthropic/claude-haiku-4-5` +- Boundary: the built production `ChatAgent`, selected only through side-quest environment values +- Tools: production read-only tools; no construction mutation tools +- Stop: first consequential question, finding, or construction decision + +The shared v3 append differs from the Ampcode source in one routing sentence only: + +> Activate the `sdcpn-modelling` skill before substantive elicitation, review, workpiece revision, +> or construction of an operational-process or SDCPN artifact. + +It contains no universal procedure or scenario answer. + +V3 uses the frozen v2 required-disclosure gate in the plugin job. The only A/B text difference is +its required action: + +- A activates the mounted `elicitation` skill. +- B reads `universal-elicitation.md` from `sdcpn-modelling`. + +Normalizing that line must make plugin instructions byte-identical. Both candidates receive +byte-identical core prompt, append, scenarios, model, tools, and stopping rules. + +## Hermetic gate + +Before paid execution: + +1. build the app and exercise A/B × S1–S4 with the faux provider; +2. prove catalog mounting, candidate-specific disclosure, and restraint paths; +3. capture the first model-visible request and prove it contains the complete compact Ampcode core + prompt and v3 append; +4. prove it does not contain the legacy production marker `## The role (core)`; +5. prove v3 scenario and candidate parity from hashes; and +6. run formatting, type, lint, unit, and architecture-boundary checks. + +## Exact paid order + +Every item is a fresh conversation. Complete or stop before starting the next pair. + +| Pair | Scenario | First | Second | +| --- | --- | --- | --- | +| 1 | S1 | A | B | +| 2 | S1 | B | A | +| 3 | S1 | A | B | +| 4 | S2 | B | A | +| 5 | S2 | A | B | +| 6 | S3 | B | A | +| 7 | S3 | A | B | +| 8 | S4 | B | A | +| 9 | S4 | A | B | +| 10 | S4 | B | A | + +V3 has at most 60 additional provider invocations and USD 1.00 additional cost. Count every Flue +model call. Before each pair, reserve eight calls and USD 0.15 unless observed completed pairs +establish a lower safe bound. Stop rather than leave an unpaired comparison. Stop on mechanical +failure, path asymmetry, missing raw trace, a shared non-discriminating prompt/router failure, or +either ceiling. Do not add scenarios or runs. + +## Adjudication + +Routing is primary. Apply the exact thresholds in the v3 amendment to `SIDE_QUEST.md`; score +question dosage, premature resource loading, integrated judgment, cost, and failure clarity +separately. Raw traces and usage are authoritative. No paid judge is used. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/APPEND_SYSTEM.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/APPEND_SYSTEM.md new file mode 100644 index 00000000000..8becfbf2f1c --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/APPEND_SYSTEM.md @@ -0,0 +1,11 @@ +# Software Behavior Specification in Gherkin + +Specialize the universal elicitation role to software behavior represented as Gherkin feature documents. Help a person make intended or current behavior explicit as purpose-bearing rules and concrete examples, maintain an evidence-faithful behavior workpiece, and author or revise Gherkin that the person would recognize. + +Activate the `gherkin-specification` skill before substantive interviewing, workpiece revision, Gherkin authoring, or review. + +During elicitation, speak about the software in the person's vocabulary—situations, events, actions, rules, and observable outcomes—rather than requiring `Feature`, `Rule`, `Background`, `Scenario`, `Given`, `When`, or `Then` phrasing. Keep current behavior distinct from proposed behavior. Target syntax may organize a draft; it must not supply behavior the person did not establish. + +The behavior workpiece is the recoverable source for authoring whenever authorship, conflict, uncertainty, or open matters remain. Do not treat a polished `.feature` document as evidence that those matters are resolved. + +Do not claim a document is parse-valid without parser evidence. Do not claim its steps bind to existing step definitions without an available step lexicon or codebase check, and do not call it executable merely because it parses. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/SKILL.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/SKILL.md new file mode 100644 index 00000000000..6f393b08e27 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/SKILL.md @@ -0,0 +1,52 @@ +--- +name: gherkin-specification +description: Elicit or revise software behavior, maintain a recoverable behavior workpiece, and author or review honest Gherkin feature documents. Use for a behavior-specification interview, Gherkin document, executable-specification draft, or review of any of them. +--- + +# Capability-aware specification lifecycle + +Use one conceptual lifecycle: orient, elicit or revise behavior, maintain the workpiece, author or revise Gherkin when useful, check, and deliver. Authoring is a thin projection and correction surface, not a separate modelling world. The current conversation may expose only part of the lifecycle; do not claim an unavailable check occurred. + +## Select the runtime branch + +### Interactive elicitation or revision + +Read `universal-elicitation.md` and `gherkin-elicitation.md` before substantive questions or revision. Interview in the person's software and product vocabulary. Read `workpiece-template.md` when creating or materially revising the behavior account. Read `gherkin-authoring-and-checks.md` before drafting, reviewing, or delivering target text. + +An early Gherkin draft may be offered after one coherent rule and example are understood when seeing the wording will help correction. Mark the wording as your rendering; agreement with it does not retroactively make every phrase person-originated evidence. + +### Render or check only + +Use the supplied behavior workpiece or Gherkin document as the complete input. Do not interview. Read `gherkin-authoring-and-checks.md`, preserve unaffected material, and perform only the checks the available capabilities support. If a consequential ambiguity prevents faithful authoring or review, report it and the smallest question a later interactive conversation must answer rather than inventing the behavior. + +## Procedure + +### Orient + +Establish enough purpose and context to select one useful behavior thread: who needs the capability, what it enables, whether the account is current or proposed, the relevant software boundary, the intended readers, and whether an existing feature document or step vocabulary is available. Do not administer these concerns as an opening form. + +### Elicit or revise behavior + +For a new account, follow one concrete example through its starting context, one focal event or action, and observable outcome. Use contrasts and boundary cases to expose the rule it illustrates. For an existing account, first locate the disputed rule, example, or feature narrative and the behavior it changes. Use both elicitation references without turning their registers or the workpiece headings into question order. + +### Maintain the workpiece + +Keep a near-target behavior account in the person's vocabulary. Record feature purpose, rules, examples, domain terms, current-versus-proposed status, authorship, and consequential open matters. A target-shaped draft does not replace these distinctions while they remain load-bearing. + +Whenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before render-only handoff and before workpiece-only delivery. A delta or a `.feature` document without its open matters is not the full recoverable account. + +### Author or revise Gherkin + +Read `gherkin-authoring-and-checks.md`. Translate only settled workpiece meaning into target structure. Preserve team language, localization, aliases, tags, and suite conventions when supplied. Do not invent step-definition bindings or implementation detail to make the document look executable. + +For revision, preserve unaffected features, rules, examples, descriptions, comments, tags, and phrasing unless the changed behavior or a named check requires a delta. + +### Check and deliver + +Apply the checks supported by the current capabilities. Deliver the current behavior workpiece when open matters or authorship distinctions remain material. Deliver Gherkin text with a plain account of whether it was only authored, parsed, checked against a supplied step vocabulary, or actually executed elsewhere. Name unillustrated rules, ambiguous behavior, new or unchecked step phrases, assumptions, and omitted cases. + +An explicit stop opens no new topic. Return the best useful workpiece and target draft with consequential gaps visible. + +## Resource discipline + +Read resources directly from this skill's advertised resource list. Do not treat Markdown links as includes, follow references recursively, or use target grammar as the sequence or vocabulary of ordinary interview questions. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-authoring-and-checks.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-authoring-and-checks.md new file mode 100644 index 00000000000..74d081c25fc --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-authoring-and-checks.md @@ -0,0 +1,95 @@ +# Gherkin Authoring and Checks + +Read this when drafting, revising, reviewing, or delivering Gherkin. Consume the current behavior workpiece or supplied feature document; do not reread the transcript as the primary behavior model. + +Authoring translates recorded software behavior into Gherkin structure. It may normalize wording, factor repeated setup, or choose an equivalent keyword alias. It may not invent behavior, step bindings, tags, examples, or suite conventions to make the document look complete. + +The [Cucumber Gherkin reference](https://cucumber.io/docs/gherkin/reference) is the public semantic authority for this draft. An installed parser and its version are the authority for a concrete project's syntax acceptance; this resource routes and interprets checks rather than replacing either source. + +## Authoring boundary + +Before authoring, confirm that the feature purpose is intelligible and each target example has a usable starting context, focal event or action, and observable outcome. If materially different behaviors remain possible because one distinction is missing, formulate the smallest resolving question. Ask it only when interactive elicitation is available; in render-only execution, report it and stop the unsupported rendering path. + +Honor the person's spoken language and supplied team conventions. If the document uses a non-English Gherkin locale, put the appropriate `# language: <code>` header on the first line. Otherwise English is the default. Do not translate the domain language merely to fit an assumed suite convention. + +## Document structure + +- Emit one `Feature:` per `.feature` document. Give it a short name and a description that preserves the capability's purpose and value when useful. +- Use `Rule:` only when it expresses a genuine business rule and groups examples that illustrate that rule. Gherkin permits examples directly under a feature; do not manufacture a rule solely to fill structure. +- Use `Scenario:` or its `Example:` synonym consistently with supplied team convention. Each example should tell one concise behavior story; Cucumber recommends three to five steps, but semantic clarity—not a step-count gate—decides when to split. +- Map supported starting context to `Given`, the focal event or action to `When`, and externally observable results to `Then`. Use `And` and `But` to continue the preceding semantic role. Do not use the keyword to disguise a second unrelated action or outcome. +- Keep implementation detail out of steps unless the interface or protocol is itself the behavior contract. Prefer the actor's goal and externally visible messages, reports, state, or responses over clicks, selectors, functions, and database inspection. +- A step's keyword is not part of Cucumber's definition match. Do not author identical step text under different semantic keywords as though they were distinct definitions. + +## Factoring and data + +### Background + +Use `Background:` only for context shared by every following example at the same `Feature` or `Rule` level. It runs before each example, after before hooks. Keep it short and vivid; the Cucumber reference recommends no more than four lines before considering higher-level phrasing or another grouping. Do not move behavior essential to understanding an example out of sight merely to remove repetition. + +Only one `Background` is allowed per `Feature` or `Rule`. Different setup families usually indicate separate rules, features, or explicit context in each example. + +### Scenario Outline and Examples + +Use `Scenario Outline:` when the same behavior structure is supported for several explicit value combinations. Every `<placeholder>` must name an `Examples:` table header, and the outline must have at least one data row. Do not turn materially different rules into one table merely because their sentences are similar. + +Parameters may appear in step text, descriptions, Doc Strings, and Data Tables. Preserve cell values exactly enough to discriminate the supported examples. + +### Step arguments + +Use a Data Table when one supported step consumes a list or record-shaped value, not as a substitute for several behavioral examples. Escape newline as `\n`, a literal pipe as `\|`, and a backslash as `\\` inside table cells. + +Use a Doc String for supported multiline text. Prefer `"""` delimiters for broad editor support; a content type may follow the opening delimiter when supplied or useful. Preserve indentation relative to the opening delimiter. + +## Descriptions, tags, and comments + +Free-form descriptions may follow `Feature`, `Rule`, `Background`, `Scenario` or `Example`, and `Scenario Outline`; Markdown is permitted and ignored during execution. Use descriptions for purpose or rationale that helps readers, not for unresolved claims presented as settled behavior. + +Tags are metadata rather than evidence of behavior; a test suite may use them for selection or conditional hooks. Preserve supplied tags or report the need for suite policy; do not invent organizational metadata during elicitation. + +Comments begin with `#` at the start of a new line after optional indentation. Gherkin has no block comments. Do not hide a second epistemic workpiece in comments; keep unsupported behavior and open matters in the companion workpiece. + +## Checks + +### Behavior fidelity + +- Each feature description, rule, example, and step traces to the current workpiece or supplied document; authoring choices remain distinguishable from person-supplied wording where consequential. +- Every `Rule` has at least one example that illustrates it, or the missing example is reported outside the target as a delivery gap. +- A reader can identify the starting state, focal event or action, and observable outcome of each example without guessing hidden implementation. +- Examples with apparently identical context and action do not assert different outcomes unless a named condition, rule, or unresolved conflict distinguishes them. +- Current behavior has not silently replaced proposed behavior or vice versa. + +### Gherkin structure + +- The first primary keyword is `Feature:` and the file contains exactly one feature. +- Keywords that require a colon have one; step keywords do not gain one. The parser, when available, is the authority for exact grammar. +- A `Background` appears before the first example at its level and no level has more than one. +- Every Scenario Outline placeholder is supplied by each applicable `Examples` table, and every table has a header and at least one row. +- Doc String delimiters close, Data Table rows are well formed, comments begin on their own lines, and localization is declared consistently. + +### Step language and execution claims + +- Step phrases use the team's domain language and do not differ accidentally in tense, synonyms, or incidental wording. +- When a step lexicon or codebase index is available, each phrase is classified as an exact known binding, an intentional new phrase, or an unresolved near match. Without such a source, binding remains unchecked. +- Parse validity proves only that a Gherkin parser accepts the document. Binding validity additionally requires matching step definitions. Executability additionally requires the relevant test runtime, hooks, fixtures, and system path. Never substitute one claim for another. + +### Revision + +- The changed behavior and target delta are named before editing. +- Unaffected descriptions, rules, examples, tags, comments, and team phrasing remain unchanged unless a supported factoring or check requires movement. +- Factoring repeated context into a Background or examples into an Outline preserves behavior and does not hide a meaningful distinction. +- New or changed phrases have an explicit binding status rather than silently inheriting an old step's implementation. + +## Delivery + +Deliver each target document as a complete `.feature` text, labeled with its intended filename when known. Also deliver the current behavior workpiece when unresolved authorship, assumptions, conflicts, unillustrated rules, unchecked bindings, or other consequential gaps remain. + +State plainly: + +- what behavior was elicited, revised, authored, or merely reformatted; +- which examples illustrate which rules and what remains unsupported; +- whether each document was authored only, parser-checked, binding-checked against a named source, or executed elsewhere; +- which assumptions, authoring normalizations, new step phrases, omissions, and open matters remain; and +- the smallest consequential question, reference source, or capability needed next. + +Do not replace that account with a closed outcome label or call a parser-valid document an executable specification without the corresponding evidence. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-elicitation.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-elicitation.md new file mode 100644 index 00000000000..46a4a3693cf --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/gherkin-elicitation.md @@ -0,0 +1,153 @@ +# Software-Behavior and Gherkin Elicitation + +This reference adds software-behavior and Gherkin-specific guidance to `universal-elicitation.md`. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance. + +The registers are not a questionnaire or phase sequence. **Recognition** suggests behavior distinctions that may be present. **Operations** select ways to investigate an active gap. **Coverage** says what a signable behavior account may need. **Verification** checks the current interview and workpiece. Gherkin grammar and document checks live in `gherkin-authoring-and-checks.md`. + +## Directives + +### Specify behavior in the person's language + +Ask about situations, actors or external systems, events, actions, rules, and observable outcomes in the vocabulary used by the people who need the behavior. Keep `Given`/`When`/`Then`, file structure, automation code, fixtures, and selectors backstage until target authoring. + +### Keep intended and current behavior distinct + +Normative language may be the desired product, not a defective report of practice. Establish whether the person is describing what happens now, what should happen, or a discrepancy that matters. Do not force a proposed rule through a last-occurrence test as though only observed behavior were legitimate. + +### Let examples illustrate rules + +Use concrete examples to discriminate and correct a general rule. Do not promote one memorable case into a universal rule without checking its boundary, and do not leave a load-bearing rule with no example showing how a reader would decide whether it held. + +### Keep observable behavior separate from implementation + +Describe outcomes visible to a person or external system. Interface gestures, database records, function calls, selectors, and test fixtures are not the behavior unless the stated purpose specifically makes that interface or integration contract observable. + +### Do not invent suite integration + +Step definitions, tags, locale, aliases, and naming conventions may be supplied by a team or available project. Without that source, preserve the intended phrase and mark its binding or convention as new, unavailable, or unchecked. + +## Recognition + +Recognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread. + +### Rule-shaped language + +“Always,” “never,” “only,” “unless,” “whenever,” “must,” “may,” and “cannot” may state a business rule, permission, invariant, or exception. Determine its scope and find a concrete example that would distinguish it from a slogan. + +### Example-shaped stories + +“Last time,” “for example,” “one user,” and narratives with specific values may already contain a context, event, and outcome. Preserve the details before generalizing. + +### Same action, different outcome + +Two accounts with the same apparent context and action but different outcomes may expose a missing state, actor distinction, business rule, or genuine conflict. + +### Expected but unobservable result + +“It works,” “it is handled,” “it succeeds,” and “the record is updated” may name an intention or hidden implementation state rather than an outcome a user or external system can observe. + +### Gesture-shaped description + +Clicks, screens, buttons, fields, API methods, and internal components may be the person's natural route into the example while obscuring the capability or externally meaningful event. Preserve them when they are contractually observable; otherwise ask what the actor is trying to accomplish or what another system sends or receives. + +### Boundary and partition language + +Thresholds, ranges, roles, lifecycle states, permissions, dates, limits, and categories may divide behavior into equivalence classes. Boundaries just below, at, and above a threshold may deserve separate examples when the rule changes there. + +### Repeated context or data shape + +Context repeated across several examples may become a `Background`; examples differing only by named values may become a `Scenario Outline`. These are target-authoring possibilities, not behavior facts and not reasons to manufacture repetition. + +## Operations + +Use the universal Operations as the primary interviewing repertoire. These additions bind them to software-behavior specification. + +### Follow one behavior end to end + +Choose one concrete case and establish the relevant starting context, one focal event or action, and what a person or external system can observe afterward. Keep incidental setup and multiple downstream behaviors out of the example unless they are necessary to understand the rule. + +### State the candidate rule for correction + +After one or more examples expose a stable relationship, offer the general rule in domain language and ask for correction. Mark it as your proposed normalization until the person settles it. + +### Contrast satisfaction and violation + +For a consequential rule, ask for a nearby example where it does not apply, is refused, or yields another outcome. Vary one relevant condition so the contrast reveals the rule rather than creating an unrelated story. + +### Probe a boundary + +When behavior changes at a threshold, ask which cases just below, at, and just above matter. Record only supported values and outcomes; the familiar boundary triad is a prompt for attention, not an automatic requirement. + +### Separate current from proposed with the same example + +When the person is changing behavior, ask what the selected example does now and what it should do. Preserve both statuses without presenting the desired result as observed or the current result as accepted. + +### Ground reusable step language + +When an actual step lexicon or repository is available, compare the intended phrase with known team language. Ask whether a near match expresses the same behavior or a distinct one. Without a lexicon, retain the domain phrase and defer binding; do not ask the person to remember hidden implementation names as a substitute for inspection. + +### Sweep rules and examples + +After a concrete slice exposes the feature's structure, sweep one concern: rules without illustrating examples, examples without observable outcomes, edge classes without coverage, current/proposed ambiguity, or phrases whose binding remains unchecked. Do not traverse target keywords merely because they exist. + +## Coverage + +Coverage identifies what the behavior workpiece may need for its purpose and downstream Gherkin authoring. It is neither question order nor a demand to populate irrelevant categories. + +### Capability, value, boundary, and status + +Preserve who or what benefits, what the capability enables, why it matters, what software or interaction boundary is in scope, and whether each account describes current or proposed behavior. + +### Business rules and rationale + +Preserve each rule generally enough to apply beyond one story, its scope and exceptions, why the distinction matters when useful, and at least one example that illustrates it or a visible gap where none is yet supported. + +### Concrete behavior examples + +Preserve the starting context that selects the behavior, the focal event or action, and the externally observable outcome. Retain specific values, actors, states, channels, and timing only where they discriminate the rule. + +### Contrasts, failures, and boundaries + +Preserve supported unhappy paths, refusals, absent permissions, invalid inputs, failures, state-dependent results, and threshold cases that materially define the rule. Do not demand one example from every familiar test-design category. + +### Actors, external systems, and domain language + +Preserve roles and systems whose differences change behavior, consequential terms in the team's language, and any supplied step vocabulary or naming convention. Do not turn every noun into an independently elicited target element. + +### Shared context and tabular variation + +Preserve repeated preconditions and repeated value dimensions so authoring can decide whether `Background`, `Scenario Outline`, or separate examples communicate them best. The target structure is a later choice; the workpiece keeps the behavior readable before factoring. + +### Target-document conventions and integration inputs + +When supplied, preserve spoken-language locale, preferred keyword aliases, tags, file naming, step lexicon, and the source against which binding or execution could be checked. Suite organization carries no behavior by itself and should not consume interview time without a delivery need. + +## Verification + +Apply these checks while eliciting and maintaining the workpiece. Grammar, authoring, parse, and binding checks live in `gherkin-authoring-and-checks.md`. + +### Purpose, rules, and examples + +- The feature purpose states who or what benefits, what capability is enabled, and why it matters at the depth the intended readers need. +- Each load-bearing rule is stated generally and has a supported concrete example or a visible gap. +- Each example has enough starting context to select the behavior, one focal event or action, and an observable outcome. +- Contrasting examples differ on a named consequential condition rather than accidentally contradicting each other. + +### Behavior and authorship + +- Current and proposed behavior remain distinguishable. +- An outcome names something visible to a person or external system rather than an intention or hidden implementation state. +- Agent-supplied rules, phrasings, values, examples, and partitions retain agent authorship until settled. +- A single story has not silently become a universal rule, and a familiar test pattern has not generated unsupported cases. +- Unknown behavior, open conflicts, unillustrated rules, and unchecked step bindings remain visible rather than disappearing into polished target text. + +### Failure signals and repairs + +- **Syntax-led interview:** questions traverse `Feature`, `Rule`, `Scenario`, or step keywords. Return to one concrete behavior in the person's language. +- **Story without rule:** examples accumulate but no one can say what behavior each discriminates. Propose the smallest candidate rule for correction. +- **Rule without witness:** a general rule has no concrete example. Ask for a supported case or mark the gap. +- **Outcome restates action:** “when it saves, then it is saved” supplies no observable result. Ask what a person or external system notices. +- **Implementation capture:** steps become clicks, selectors, function calls, or database assertions without a purpose that makes them observable. Translate back to behavior or name the interface contract explicitly. +- **Missing selector:** apparently identical context and action yield different outcomes. Preserve both and investigate the state, rule, or conflict that distinguishes them. +- **Desired-as-observed:** proposed behavior is presented as current evidence. Restore status and authorship. +- **Invented binding:** a phrase is described as an existing executable step without a lexicon or code check. Mark it new or unchecked. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/workpiece-template.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/workpiece-template.md new file mode 100644 index 00000000000..550b66e7915 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/instrument/gherkin-specification/workpiece-template.md @@ -0,0 +1,102 @@ +# Behavior-Specification Workpiece and Recording Contract + +The workpiece is the shared, recoverable software-behavior account. Elicitation and revision maintain it; Gherkin authoring consumes it. The transcript remains evidence, and the `.feature` document remains a target rendering rather than the sole home of unresolved meaning. + +Follow the person's thread during conversation and file material into the workpiece afterward. Its headings are recording homes, not question order. The structure is near the target because software-behavior examples map closely to Gherkin, but it does not require target keywords or step decomposition. + +## Recording distinctions + +Use the authorship and uncertainty distinctions from `universal-elicitation.md`; do not redeclare them as a Gherkin ontology. This workpiece adds only distinctions the software-behavior/Gherkin pairing requires: + +- **Behavior status** — Whether an account describes current or proposed behavior, or a discrepancy between them. +- **Rule and example** — The general behavior and the concrete case illustrating it. A case does not become a rule merely because target text can be written for it. +- **Behavior content and authoring choice** — What the software must do versus how the agent names, groups, phrases, or factors it in Gherkin. +- **Integration status** — Whether target text is only authored, accepted by a parser, matched against a named step-definition source, or executed through a named runtime path. + +## Workpiece template + +```markdown +# Behavior-Specification Workpiece + +## Purpose and scope + +### Feature value narrative + +Who or what benefits, what the capability enables, and why it matters. + +### Current, proposed, or mixed account + +### Intended readers and use + +### Software boundary and deliberate non-goals + +### What the result must not claim + +## Domain language and integration context + +### Actors and external systems + +### Consequential terms and meanings + +### Supplied locale, conventions, tags, or step lexicon + +## Rules and examples + +### Rule: <person's words for the rule> + +#### Working statement, behavior status, and authorship + +#### Why this distinction matters + +#### Example: <memorable behavior name> + +##### Starting context + +##### Focal event or action + +##### Observable outcome + +##### Rule distinction, boundary, or contrast demonstrated + +##### Exact person evidence and authoring choices where needed + +#### Contrasting or boundary example: <name> + +Add only when it exposes a consequential condition the first example does not. + +Repeat rules and examples as needed. An example may remain directly under the feature when no separate business rule is useful; state what it demonstrates. + +## Authoring candidates + +### Context shared across examples + +### Value dimensions that may form an outline + +### Candidate target files and feature grouping + +### New, known, and unchecked step phrases + +## Open matters and authorship + +For each consequential matter, record its universal state—agent proposal or assumption, unknown, not yet asked, declined, deferred, conflict, correction, contextual coexistence, or deliberate omission—plus what it affects and what would resolve or re-enter it. + +Record target-formalism and integration gaps separately from unknown behavior. A supported rule can be clear while its phrase binding or runtime capability remains unavailable. + +## Delivery status + +### What this workpiece currently supports + +### Consequential gaps + +### Gherkin status and check evidence +``` + +## Maintenance + +- Prefer the person's domain terms for rules, examples, actors, and outcomes. +- Keep one authoritative home for each active rule and example. Record corrections without leaving the obsolete and current forms as competing behavior. +- Do not force a person-supplied example into step lines while interviewing. Context, event or action, and outcome are enough for the workpiece; authoring owns target decomposition. +- Keep current and proposed versions side by side only when their contrast is the subject; otherwise state which account is active and preserve the old one as correction history. +- Remove irrelevant empty sections. Record an unresolved state only when it matters to later work. +- If authoring requires transcript archaeology to recover a load-bearing rule or outcome, the workpiece is incomplete at that boundary. +- Record separately whether target text was not authored, authored only, parser-checked, binding-checked against a named source, or executed by an external test path. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/protocol.md new file mode 100644 index 00000000000..64c5b06961f --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/gherkin-shape-c-paper-v1/protocol.md @@ -0,0 +1,9 @@ +# Gherkin Shape C paper instrument v1 + +Status: retained design instrument; not production or execution authority. + +This protocol preserves the selected paper hypothesis for software behavior × Gherkin after the temporary Ampcode workbench was removed. It contains no runner and proves no Flue route, parser acceptance, step-definition binding, runtime execution, or behavioral adequacy. + +Instrument files are under [`instrument/`](instrument/). They were relocated without content changes from `packages/core/_drafts/ampcode/plugin-gherkin/`. The historical workbench is reconstructible at commit `5249a73f09977ad2ef007e08de7b7314f94568e1`. + +Any future campaign must define a new protocol and freeze the exact production candidate independently rather than treating this paper instrument as landed behavior. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/harness-run.ts b/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/harness-run.ts deleted file mode 100644 index 2023319a9d8..00000000000 --- a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/harness-run.ts +++ /dev/null @@ -1,954 +0,0 @@ -/** - * Baseline condition 5 — the harness in the loop. - * - * The same simulated expert, probes, and turn budget as conditions 1–4, but - * the interviewer is the shipped SDCPN elicitor running in the Flue runtime - * with the binding's machinery: the `ask` suspension, the settlement nudge, - * the private `sweep` extraction into the capture store, and the harness's - * computed completion. The runner plays the expert and the clock. It reads - * every harness fact from durable history and the capture store, never - * interpolates into the interviewer's instructions, and needs no delivery - * classifier: the deliverable is the capture store, folded, and the - * interviewer ends its own turn-taking by replying without a question. - * - * This is the JS-API workflow pattern the Flue routing table names for a loop - * that drives an agent through turns: `start()`, then `send()`/`wait()`/ - * `history()` through the SDK client over the app's own router. - * - * This retained historical runner has no supported operator command. Restore it only through - * an explicitly scoped historical investigation. - * - * Environment: - * - * ANTHROPIC_API_KEY both models (pi-ai reads it for the interviewer) - * BRUNCH_SDCPN_MODEL interviewer model id; this runner defaults it to claude-opus-5 - * BRUNCH_BASELINE_ANTHROPIC_MODULE test-only stand-in for the expert's Anthropic client - * BRUNCH_BASELINE_INTERVIEWER_PROVIDER_MODULE test-only pi provider module (default export) for the interviewer - * BRUNCH_BASELINE_HARD_STOP positive interviewer-turn limit; defaults to 24 - * BRUNCH_BASELINE_OUTPUT_DIR optional run-specific production output directory - * BRUNCH_BASELINE_TEST_OUTPUT_DIR test-only output directory; requires both stand-ins - * - * Artifacts (beside the other conditions' transcripts unless the test directory is set): - * - * condition-5.md the readable transcript, harness facts interleaved - * condition-5.raw.json every turn record, the Flue history snapshot, the store, and usage - * condition-5-model.md the capture store folded into the elicited model, with the completion report - * condition-5-captures.json the capture-store snapshot verbatim - * condition-5-system.md the interviewer's instructions, reconstructed with the binding's own functions - * condition-5.timings.jsonl each observed Flue model call, tagged by interviewer-turn purpose - */ - -import { - appendFile, - cp, - mkdir, - mkdtemp, - readFile, - rm, - 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 { start } from "@flue/runtime/node"; -import { - createFlueClient, - type FlueConversationMessage, - type FlueConversationSnapshot, -} from "@flue/sdk"; - -import { - askProtocolInstructionFragments, - buildCompletionCueSignal, - buildSweepList, - completionDemands, - evaluateCompletion, - foldElicitedModel, - pendingAskAffordanceId, - renderInstructions, - settlementProtocolInstructionFragments, - toolName, - type CaptureStoreSnapshot, - type CompletionReport, - type ElicitedModel, - type SlotState, -} from "@hashintel/brunch-agent"; -import { - createLocalCaptureStore, - projectFlueHistoryForSweep, -} from "@hashintel/brunch-agent-binding-flue"; -import { sdcpn, sdcpnDefinition } from "@hashintel/brunch-agent-plugin-sdcpn"; -import { repertoire } from "@hashintel/brunch-agent/prompts"; - -import { - createTurnTimingRecorder, - type TurnTimingPurpose, - type TurnTimingRecord, -} from "./turn-timing.ts"; - -import type Anthropic from "@anthropic-ai/sdk"; -import type { Provider } from "@earendil-works/pi-ai"; - -// --------------------------------------------------------------------------- -// Protocol constants — identical to conditions 1, 2, and 4 (run.ts). -// --------------------------------------------------------------------------- - -const CONDITION = "5"; -const EXPERT_MODEL = "claude-sonnet-5"; -const DEFAULT_INTERVIEWER_MODEL = "claude-opus-5"; -const FORCE_WRAP_AT = 20; -const DEFAULT_HARD_STOP_AT = 24; -const IMPATIENCE_AT = 8; -const IMPATIENCE_LINE = - "(Sorry — I've just seen the time, I have the floor huddle in ten minutes. How much more do you need?)"; -const FORCED_WRAP_MESSAGE = - "I really do have to stop here. Please produce the model now with everything you have."; -/** Consecutive interviewer turns without a question, before the wrap, that end the run. */ -const STALL_AFTER_TURNS_WITHOUT_ASK = 3; -const EXPERT_MAX_TOKENS = 1_500; - -// --------------------------------------------------------------------------- -// Environment and stand-ins. -// --------------------------------------------------------------------------- - -const outputDirectory = process.env["BRUNCH_BASELINE_OUTPUT_DIR"]; -const testOutputDirectory = process.env["BRUNCH_BASELINE_TEST_OUTPUT_DIR"]; -const expertClientModule = process.env["BRUNCH_BASELINE_ANTHROPIC_MODULE"]; -const interviewerProviderModule = - process.env["BRUNCH_BASELINE_INTERVIEWER_PROVIDER_MODULE"]; -const apiKey = process.env["ANTHROPIC_API_KEY"]; -const configuredHardStop = process.env["BRUNCH_BASELINE_HARD_STOP"]; -const hardStopAt = - configuredHardStop === undefined - ? DEFAULT_HARD_STOP_AT - : Number(configuredHardStop); - -if (!Number.isSafeInteger(hardStopAt) || hardStopAt <= 0) { - throw new Error("BRUNCH_BASELINE_HARD_STOP must be a positive integer"); -} - -if (testOutputDirectory && !(expertClientModule && interviewerProviderModule)) { - console.error( - "BRUNCH_BASELINE_TEST_OUTPUT_DIR requires BRUNCH_BASELINE_ANTHROPIC_MODULE and BRUNCH_BASELINE_INTERVIEWER_PROVIDER_MODULE", - ); - process.exit(1); -} -if (!apiKey && !(expertClientModule && interviewerProviderModule)) { - console.error("ANTHROPIC_API_KEY is not set"); - process.exit(1); -} - -// The elicitor pins its model at module load, so the override must be in the -// environment before the agent module is imported (below, dynamically). -process.env["BRUNCH_SDCPN_MODEL"] ||= DEFAULT_INTERVIEWER_MODEL; -const interviewerModel = process.env["BRUNCH_SDCPN_MODEL"]; - -// The capture store lands in a run-private directory; the snapshot is copied -// out as an artifact at the end. Set before the agent's first render. -const targetDocumentDirectory = await mkdtemp( - join(tmpdir(), "brunch-baseline-c5-"), -); -process.env["BRUNCH_DEV_TARGET_DOCUMENT_DIR"] = targetDocumentDirectory; - -const caseDir = fileURLToPath( - new URL("../../cases/vestera-scheduling/", import.meta.url), -); -const transcriptDir = - testOutputDirectory ?? - outputDirectory ?? - fileURLToPath( - new URL( - "../../../docs/evidence/evaluations/vestera-legacy-baseline/transcripts/", - import.meta.url, - ), - ); -await mkdir(transcriptDir, { recursive: true }); -const timingsPath = join(transcriptDir, `condition-${CONDITION}.timings.jsonl`); -await writeFile(timingsPath, ""); - -// --------------------------------------------------------------------------- -// Records. -// --------------------------------------------------------------------------- - -interface ExpertMessage { - readonly role: "user" | "assistant"; - readonly content: string; -} - -interface Usage { - input: number; - output: number; - cacheRead: number; - cacheWrite: number; - calls: number; -} - -export interface HarnessSweepRecord { - readonly status: string; - readonly appliedCaptureIds?: readonly string[]; - readonly skippedDedupKeys?: readonly string[]; - readonly advisories?: readonly unknown[]; - readonly refusal?: unknown; - readonly completion?: unknown; -} - -export interface HarnessCompletionRecord { - readonly captures: number; - readonly complete: boolean; - readonly unsatisfied: number; - readonly outsideSlice: number; - readonly unmapped: number; - readonly revision: string; - readonly cue: string; -} - -export interface HarnessTurnRecord { - readonly turn: number; - /** Assistant text parts, in order, across every response in the turn. */ - readonly text: readonly string[]; - readonly asks: readonly { - readonly question: string; - readonly toolCallId: string; - readonly rejected?: string; - }[]; - readonly sweeps: readonly HarnessSweepRecord[]; - readonly signals: readonly { - readonly tagName: string; - readonly excerpt: string; - }[]; - readonly toolErrors: readonly { - readonly toolName: string; - readonly errorText: string; - }[]; - readonly settlement?: "failed" | "aborted"; - /** The one question left open for the expert, if any. */ - readonly pendingQuestion?: string; - /** Flue model-call timings observed while this interviewer turn ran. */ - readonly timings: readonly TurnTimingRecord[]; - /** The harness's read-time completion over the capture store after this turn. */ - readonly completion: HarnessCompletionRecord; - /** What the expert was then sent: their reply, or a stimulus. */ - readonly expert?: { - readonly content: string; - readonly stimulus?: string; - readonly truncated?: boolean; - }; -} - -export interface HarnessRunRecord { - readonly startedAt: string; - readonly condition: typeof CONDITION; - readonly interviewerModel: string; - readonly expertModel: string; - readonly conversationId: string; - readonly stopReason: string; - readonly turns: readonly HarnessTurnRecord[]; - readonly timings: readonly TurnTimingRecord[]; - readonly usage: { readonly interviewer: Usage; readonly expert: Usage }; - readonly history: FlueConversationSnapshot; - readonly store: CaptureStoreSnapshot; -} - -// --------------------------------------------------------------------------- -// The expert (unchanged from run.ts: same model, same pack, thinking off). -// --------------------------------------------------------------------------- - -interface BaselineAnthropicClient { - messages: { - create( - request: Anthropic.MessageCreateParamsNonStreaming, - ): Promise<Anthropic.Message>; - }; -} - -let anthropic: BaselineAnthropicClient | undefined; -async function getAnthropic(): Promise<BaselineAnthropicClient> { - if (anthropic) return anthropic; - anthropic = expertClientModule - ? ((await import(expertClientModule)).default as BaselineAnthropicClient) - : (new (await import("@anthropic-ai/sdk")).default({ - apiKey, - maxRetries: 5, - timeout: 30 * 60 * 1000, - }) as BaselineAnthropicClient); - return anthropic; -} - -const expertUsage: Usage = { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - calls: 0, -}; - -async function callExpert( - system: string, - messages: readonly ExpertMessage[], -): Promise<{ text: string; truncated: boolean }> { - let tokenBudget = EXPERT_MAX_TOKENS; - for (let attempt = 1; attempt <= 5; attempt++) { - const response = await ( - await getAnthropic() - ).messages.create({ - model: EXPERT_MODEL, - max_tokens: tokenBudget, - thinking: { type: "disabled" }, - system, - messages: messages.map((message) => ({ ...message })), - }); - expertUsage.calls += 1; - expertUsage.input += response.usage.input_tokens; - expertUsage.output += response.usage.output_tokens; - expertUsage.cacheRead += response.usage.cache_read_input_tokens ?? 0; - expertUsage.cacheWrite += response.usage.cache_creation_input_tokens ?? 0; - const text = response.content - .filter((block) => block.type === "text") - .map((block) => block.text) - .join("\n"); - if (text.trim() === "") { - tokenBudget *= 2; - console.error( - ` expert: empty text, retrying with max_tokens=${tokenBudget}`, - ); - continue; - } - return { text, truncated: response.stop_reason === "max_tokens" }; - } - throw new Error("expert: exhausted retries"); -} - -// --------------------------------------------------------------------------- -// Reading harness facts out of durable history and the store. -// --------------------------------------------------------------------------- - -const ASK_TOOL = toolName("ask"); -const SWEEP_TOOL = toolName("sweep"); - -const excerpt = (text: string, length = 240): string => - text.length > length ? `${text.slice(0, length)}…` : text; - -const questionOf = (output: unknown): string | undefined => - typeof output === "object" && - output !== null && - "payload" in output && - typeof output.payload === "object" && - output.payload !== null && - "question" in output.payload && - typeof output.payload.question === "string" - ? output.payload.question - : undefined; - -const sweepRecordOf = (output: unknown): HarnessSweepRecord => { - const record = ( - typeof output === "object" && output !== null ? output : {} - ) as Record<string, unknown>; - return { - status: typeof record["status"] === "string" ? record["status"] : "unknown", - ...(Array.isArray(record["appliedCaptureIds"]) - ? { appliedCaptureIds: record["appliedCaptureIds"] as string[] } - : {}), - ...(Array.isArray(record["skippedDedupKeys"]) - ? { skippedDedupKeys: record["skippedDedupKeys"] as string[] } - : {}), - ...(Array.isArray(record["advisories"]) - ? { advisories: record["advisories"] as unknown[] } - : {}), - ...("refusal" in record ? { refusal: record["refusal"] } : {}), - ...("completion" in record ? { completion: record["completion"] } : {}), - }; -}; - -/** Everything the interviewer did between two of our dispatches. */ -function readTurn( - messages: readonly FlueConversationMessage[], -): Omit< - HarnessTurnRecord, - "turn" | "completion" | "pendingQuestion" | "timings" -> { - const text: string[] = []; - const asks: HarnessTurnRecord["asks"][number][] = []; - const sweeps: HarnessSweepRecord[] = []; - const signals: HarnessTurnRecord["signals"][number][] = []; - const toolErrors: HarnessTurnRecord["toolErrors"][number][] = []; - let settlement: HarnessTurnRecord["settlement"]; - for (const message of messages) { - if (message.settlement) settlement = message.settlement.outcome; - if (message.role === "system") { - const tagName = message.signal?.tagName ?? message.purpose; - const body = message.parts - .flatMap((part) => (part.type === "text" ? [part.text] : [])) - .join(""); - signals.push({ tagName, excerpt: excerpt(body) }); - continue; - } - if (message.role !== "assistant") continue; - for (const part of message.parts) { - if (part.type === "text") { - if (part.text.trim().length > 0) text.push(part.text); - continue; - } - if (part.type !== "dynamic-tool") continue; - if (part.toolName === ASK_TOOL) { - const input = part.input as { question?: unknown } | undefined; - const question = - (part.state === "output-available" && questionOf(part.output)) || - (typeof input?.question === "string" ? input.question : ""); - asks.push({ - question, - toolCallId: part.toolCallId, - ...(part.state === "output-error" - ? { rejected: part.errorText } - : {}), - }); - } else if (part.toolName === SWEEP_TOOL) { - if (part.state === "output-available") { - sweeps.push(sweepRecordOf(part.output)); - } else if (part.state === "output-error") { - toolErrors.push({ - toolName: part.toolName, - errorText: part.errorText, - }); - } - } else if (part.state === "output-error") { - toolErrors.push({ toolName: part.toolName, errorText: part.errorText }); - } - } - } - return { - text, - asks, - sweeps, - signals, - toolErrors, - ...(settlement === undefined ? {} : { settlement }), - }; -} - -const demands = completionDemands(sdcpnDefinition); - -function readCompletion(store: CaptureStoreSnapshot): { - model: ElicitedModel; - report: CompletionReport; - record: HarnessCompletionRecord; -} { - const model = foldElicitedModel(store, sdcpnDefinition); - const report = evaluateCompletion(model, demands); - const sweepList = buildSweepList(model, report, sdcpnDefinition.patterns); - return { - model, - report, - record: { - captures: model.activeCaptureIds.size, - complete: report.complete, - unsatisfied: report.failures.length, - outsideSlice: report.outsideSlice.length, - unmapped: model.unmapped.length, - revision: report.revision, - cue: buildCompletionCueSignal(model, report, sweepList).body, - }, - }; -} - -// --------------------------------------------------------------------------- -// Rendering. -// --------------------------------------------------------------------------- - -const yesNo = (value: boolean): string => (value ? "yes" : "no"); - -function renderSlot(slot: SlotState): string { - switch (slot.state) { - case "value": - return `${JSON.stringify(slot.value)} — ${slot.precision}, ${slot.status}${ - slot.sourceRegime ? `, ${slot.sourceRegime}` : "" - }${slot.evidenced ? "" : ", unevidenced"}${ - slot.rationale ? ` — _${slot.rationale}_` : "" - }`; - case "absence": - return `absence: ${slot.absence}${slot.pointer ? ` → ${slot.pointer}` : ""} (${slot.status})`; - case "conflict": - return `conflict — ${slot.readings.length} readings`; - case "divergence": - return `divergence — prescribed ${JSON.stringify( - slot.prescribed.assertion.assertion, - )}; practiced ${JSON.stringify(slot.practiced.assertion.assertion)}`; - } -} - -function renderModel( - model: ElicitedModel, - report: CompletionReport, - completion: HarnessCompletionRecord, -): string { - const lines: string[] = [ - "# Condition 5 — the elicited model, folded from the capture store", - "", - "The harness's own deliverable: `foldElicitedModel` over the active captures, then", - "`evaluateCompletion` against the sdcpn definition. Nothing here was written by the", - "interviewer; every value is a capture the sweep extracted and the store admitted.", - "", - `- Plugin version: \`${model.pluginVersion}\``, - `- Revision: \`${model.revision}\``, - `- Active captures: ${completion.captures}`, - `- Complete: **${yesNo(report.complete)}** — ${report.failures.length} unsatisfied, ${report.outsideSlice.length} node(s) outside every objective's slice, ${model.unmapped.length} unmapped capture(s)`, - "", - "## Nodes", - ]; - const order = new Map( - sdcpnDefinition.kinds.map((row, index) => [row.kind, index] as const), - ); - const byKind = new Map<string, ElicitedModel["nodes"][number][]>(); - for (const node of model.nodes) { - const list = byKind.get(node.kind) ?? []; - list.push(node); - byKind.set(node.kind, list); - } - const kinds = [...byKind.keys()].sort( - (a, b) => (order.get(a) ?? 99) - (order.get(b) ?? 99), - ); - if (kinds.length === 0) lines.push("", "_No nodes._"); - for (const kind of kinds) { - const nodes = byKind.get(kind)!; - lines.push("", `### ${kind} (${nodes.length})`); - for (const node of nodes) { - lines.push("", `#### \`${node.id}\``); - for (const [slot, state] of Object.entries(node.slots)) { - lines.push(`- **${slot}** — ${renderSlot(state)}`); - } - } - } - lines.push("", "## Completion report", ""); - if (report.failures.length === 0) lines.push("_No unsatisfied demands._"); - for (const failure of report.failures) { - lines.push( - `- [${failure.diagnostic}] ${failure.message}${ - failure.nodeId - ? ` (\`${failure.nodeId}\`${failure.slot ? ` — ${failure.slot}` : ""})` - : "" - }`, - ); - } - if (report.outsideSlice.length > 0) { - lines.push("", "## Outside every objective's slice", ""); - for (const node of report.outsideSlice) { - lines.push(`- \`${node.nodeId}\` — ${node.open.length} open`); - } - } - if (model.unmapped.length > 0) { - lines.push("", "## Unmapped captures", ""); - for (const unmapped of model.unmapped) { - lines.push(`- \`${unmapped.captureId}\` — ${unmapped.reason}`); - } - } - lines.push( - "", - "## The harness's cue at close", - "", - "```", - completion.cue, - "```", - ); - return `${lines.join("\n")}\n`; -} - -const formatUsage = (usage: Usage): string => - `${usage.input} in (+${usage.cacheWrite} cache write, +${usage.cacheRead} cache read) / ${usage.output} out across ${usage.calls} calls`; - -const formatPurposeTiming = ( - timings: readonly TurnTimingRecord[], - purpose: TurnTimingPurpose, -): string => { - const matchingTimings = timings.filter( - (timing) => timing.purpose === purpose, - ); - if (matchingTimings.length === 0) return "—"; - const durationMs = matchingTimings.reduce( - (total, timing) => total + timing.durationMs, - 0, - ); - return `${durationMs} ms (${matchingTimings.length} call${matchingTimings.length === 1 ? "" : "s"})`; -}; - -function renderTranscript( - run: HarnessRunRecord, - openingMessage: string, - sweepTally: { applied: number; refused: number; noRange: number }, -): string { - const last = run.turns.at(-1)?.completion; - const header = [ - "# Baseline control — condition 5 (the harness in the loop)", - "", - `- Run started: ${run.startedAt}`, - `- Interviewer: ${run.interviewerModel} as the shipped SDCPN elicitor in the Flue runtime — binding-flue's ask, settlement nudge, sweep, fold, and completion (instructions reconstructed in condition-5-system.md)`, - `- Simulated expert: ${run.expertModel} + situation-pack.md`, - `- Interviewer turns: ${run.turns.length} (impatience probe at ${IMPATIENCE_AT}, forced wrap at ${FORCE_WRAP_AT}, hard stop ${hardStopAt})`, - `- Stop reason: ${run.stopReason}`, - last === undefined - ? "- Harness at close: no turn completed" - : `- Harness at close: ${last.captures} active captures; complete ${yesNo(last.complete)}; ${last.unsatisfied} unsatisfied; ${last.unmapped} unmapped; sweeps applied ${sweepTally.applied}, refused ${sweepTally.refused}, no settled range ${sweepTally.noRange}`, - `- Tokens: interviewer ${formatUsage(run.usage.interviewer)}; expert ${formatUsage(run.usage.expert)}`, - "", - "Harness facts are set off as `> harness —` lines: tool calls the interviewer made, signals the", - "harness appended, and the read-time completion over the capture store after each turn. The", - "expert never sees them.", - "", - "---", - "**Opening message**:", - "", - openingMessage, - ]; - const body = run.turns.map((turn) => { - const parts: string[] = [ - "---", - "", - `**Interviewer — turn ${turn.turn}** | interview ${formatPurposeTiming(turn.timings, "interview")} | sweep ${formatPurposeTiming(turn.timings, "sweep")} | repair ${formatPurposeTiming(turn.timings, "repair")}`, - "", - ]; - if (turn.text.length === 0 && turn.asks.length === 0) { - parts.push("_(no visible text this turn)_"); - } - parts.push(...turn.text.flatMap((text) => [text, ""])); - for (const signal of turn.signals) { - parts.push( - `> harness — signal \`${signal.tagName}\`: ${signal.excerpt.replaceAll("\n", " ")}`, - ); - } - for (const sweep of turn.sweeps) { - const completion = sweep.completion as - | { complete?: boolean; unsatisfied?: number } - | undefined; - parts.push( - `> harness — sweep ${sweep.status}${ - sweep.appliedCaptureIds - ? `; applied ${sweep.appliedCaptureIds.length}` - : "" - }${sweep.skippedDedupKeys?.length ? `; skipped ${sweep.skippedDedupKeys.length}` : ""}${ - sweep.advisories?.length - ? `; advisories ${sweep.advisories.length}` - : "" - }${sweep.refusal ? `; refusal ${JSON.stringify(sweep.refusal)}` : ""}${ - completion - ? `; completion complete=${yesNo(completion.complete === true)} unsatisfied=${completion.unsatisfied ?? "?"}` - : "" - }`, - ); - } - for (const error of turn.toolErrors) { - parts.push( - `> harness — tool error \`${error.toolName}\`: ${error.errorText}`, - ); - } - for (const ask of turn.asks.filter((candidate) => candidate.rejected)) { - parts.push( - `> harness — ask rejected: ${ask.rejected} (question: ${excerpt(ask.question, 120)})`, - ); - } - if (turn.settlement) - parts.push(`> harness — submission ${turn.settlement}`); - parts.push( - `> harness — completion after turn ${turn.turn}: ${turn.completion.captures} captures; complete ${yesNo(turn.completion.complete)}; ${turn.completion.unsatisfied} unsatisfied; ${turn.completion.unmapped} unmapped`, - ); - if (turn.pendingQuestion !== undefined) { - parts.push("", "**Ask**:", "", turn.pendingQuestion); - } - if (turn.expert) { - parts.push("", "---", ""); - if ( - turn.expert.stimulus && - turn.expert.content === turn.expert.stimulus - ) { - parts.push( - "**Injected experiment stimulus (not expert evidence)**:", - "", - turn.expert.stimulus, - ); - } else { - parts.push( - "**Expert (Marta)**:", - "", - turn.expert.content, - ...(turn.expert.stimulus - ? [ - "", - "**Injected experiment stimulus (not expert evidence)**:", - "", - turn.expert.stimulus, - ] - : []), - ...(turn.expert.truncated - ? ["", "_(expert reply truncated at its token budget)_"] - : []), - ); - } - } - parts.push(""); - return parts.join("\n"); - }); - return `${[...header, "", ...body].join("\n")}`; -} - -// --------------------------------------------------------------------------- -// The run. -// --------------------------------------------------------------------------- - -const startedAt = new Date().toISOString(); -const situationPack = await readFile(`${caseDir}situation-pack.md`, "utf8"); -const openingRaw = await readFile(`${caseDir}opening-message.md`, "utf8"); -const openingSeparator = openingRaw.indexOf("\n---\n"); -const openingMessage = ( - openingSeparator === -1 ? openingRaw : openingRaw.slice(openingSeparator + 5) -).trim(); - -// The app modules are imported after the environment is set: the elicitor -// reads its model id and the target-document directory at module load. -const [ - { SdcpnElicitor }, - { default: app }, - { SDCPN_AGENT_ROUTE }, - { targetDocumentPath }, -] = await Promise.all([ - import("../../../../../../../apps/brunch-agent/src/agents/sdcpn-elicitor.ts"), - import("../../../../../../../apps/brunch-agent/src/app.ts"), - import("../../../../../../../apps/brunch-agent/src/routes.ts"), - import("../../../../../../../apps/brunch-agent/src/target-document-path.ts"), -]); - -const provider: Provider = interviewerProviderModule - ? ((await import(interviewerProviderModule)).default as Provider) - : ( - await import("@earendil-works/pi-ai/providers/anthropic") - ).anthropicProvider(); - -const interviewerUsage: Usage = { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - calls: 0, -}; -const turnTimingRecorder = createTurnTimingRecorder(); -const stopObserving = observe((event) => { - turnTimingRecorder.observe(event); - if (event.type !== "turn") return; - interviewerUsage.calls += 1; - const usage = event.response.usage; - if (!usage) return; - interviewerUsage.input += usage.input; - interviewerUsage.output += usage.output; - interviewerUsage.cacheRead += usage.cacheRead; - interviewerUsage.cacheWrite += usage.cacheWrite; -}); - -const flue = await start({ agents: [SdcpnElicitor], providers: [provider] }); - -const conversationId = `baseline-condition-5-${startedAt.replaceAll(/[:.]/gu, "-")}`; -const targetDocumentId = conversationId; -const fetchApp: typeof fetch = (input, init) => - Promise.resolve( - app.fetch(input instanceof Request ? input : new Request(input, init)), - ); -const client = createFlueClient({ - url: `http://brunch.local/agents/${SDCPN_AGENT_ROUTE}/${conversationId}`, - fetch: fetchApp, -}); -const store = createLocalCaptureStore(targetDocumentPath(targetDocumentId)); - -const turns: HarnessTurnRecord[] = []; -const expertView: ExpertMessage[] = []; -let stopReason = "hard-stop"; -let turnsWithoutAsk = 0; -let wrapSent = false; -let consumedMessages = 0; - -async function dispatch(body: string, initial = false): Promise<void> { - const admission = await client.send({ - message: { kind: "user", body }, - ...(initial ? { initialData: { targetDocumentId } } : {}), - }); - await client.wait(admission); -} - -async function writeArtifacts(): Promise<void> { - const history = await client.history(); - const storeSnapshot = await store.read(); - const { model, report, record } = readCompletion(storeSnapshot); - const run: HarnessRunRecord = { - startedAt, - condition: CONDITION, - interviewerModel, - expertModel: EXPERT_MODEL, - conversationId, - stopReason, - turns, - timings: turnTimingRecorder.all(), - usage: { interviewer: interviewerUsage, expert: expertUsage }, - history, - store: storeSnapshot, - }; - const sweepTally = { applied: 0, refused: 0, noRange: 0 }; - for (const sweep of turns.flatMap((turn) => turn.sweeps)) { - if (sweep.status === "applied") sweepTally.applied += 1; - else if (sweep.status === "refused") sweepTally.refused += 1; - else if (sweep.status === "no-settled-range") sweepTally.noRange += 1; - } - await mkdir(transcriptDir, { recursive: true }); - const stem = join(transcriptDir, `condition-${CONDITION}`); - await writeFile(`${stem}.raw.json`, `${JSON.stringify(run, null, 2)}\n`); - await writeFile( - `${stem}.md`, - renderTranscript(run, openingMessage, sweepTally), - ); - await writeFile(`${stem}-model.md`, renderModel(model, report, record)); - await writeFile( - `${stem}-captures.json`, - `${JSON.stringify(storeSnapshot, null, 2)}\n`, - ); - await writeFile( - `${stem}-system.md`, - [ - "# Condition 5 — the interviewer's instructions", - "", - "Reconstructed with the same functions the binding composes them from", - "(`askProtocolInstructionFragments`, `settlementProtocolInstructionFragments`,", - "`renderInstructions(repertoire, sdcpnDefinition)`), so this is the text the", - "elicitor rendered, minus whatever Flue prepends about its own tools.", - "", - "---", - "", - [ - ...askProtocolInstructionFragments(sdcpn.targetFormalism), - ...settlementProtocolInstructionFragments(), - renderInstructions(repertoire, sdcpnDefinition), - ].join("\n\n"), - "", - ].join("\n"), - ); -} - -try { - console.error( - `condition ${CONDITION}: interviewer ${interviewerModel}, expert ${EXPERT_MODEL}`, - ); - let outgoing = openingMessage; - let initial = true; - while (turns.length < hardStopAt) { - const turnNumber = turns.length + 1; - console.error(`turn ${turnNumber} (interviewer)`); - turnTimingRecorder.startInterviewerTurn(turnNumber); - await dispatch(outgoing, initial); - initial = false; - - const history = await client.history(); - const fresh = history.messages.slice(consumedMessages); - consumedMessages = history.messages.length; - const observed = readTurn(fresh); - const pendingId = pendingAskAffordanceId( - projectFlueHistoryForSweep(history), - ); - const pendingQuestion = - pendingId === undefined - ? undefined - : observed.asks.find( - (ask) => - !ask.rejected && `affordance_${ask.toolCallId}` === pendingId, - )?.question; - const { record: completion } = readCompletion(await store.read()); - const turnTimings = turnTimingRecorder.forInterviewerTurn(turnNumber); - const turn: HarnessTurnRecord = { - turn: turnNumber, - ...observed, - ...(pendingQuestion === undefined ? {} : { pendingQuestion }), - timings: turnTimings, - completion, - }; - turns.push(turn); - await appendFile( - timingsPath, - `${turnTimings.map((timing) => JSON.stringify(timing)).join("\n")}\n`, - ); - console.error( - ` harness: ${completion.captures} captures, complete ${yesNo(completion.complete)}, ${completion.unsatisfied} unsatisfied; sweeps ${observed.sweeps.map((sweep) => sweep.status).join(",") || "none"}; ask ${pendingQuestion === undefined ? "none" : "pending"}`, - ); - - if (observed.settlement) { - stopReason = `submission-${observed.settlement}`; - break; - } - if (pendingQuestion === undefined) { - turnsWithoutAsk += 1; - if (completion.complete) { - stopReason = "closed-complete"; - break; - } - if (wrapSent) { - stopReason = "closed-incomplete"; - break; - } - if (turnsWithoutAsk >= STALL_AFTER_TURNS_WITHOUT_ASK) { - stopReason = "stalled"; - break; - } - } else { - turnsWithoutAsk = 0; - } - if (turns.length >= hardStopAt) break; - - // What the expert sees: the interviewer's visible text and its question. - const visible = [ - ...observed.text, - ...(pendingQuestion === undefined ? [] : [pendingQuestion]), - ] - .join("\n\n") - .trim(); - expertView.push({ - role: "user", - content: - visible.length > 0 - ? visible - : "[The interviewer said nothing this turn.]", - }); - - if (turnNumber >= FORCE_WRAP_AT) { - wrapSent = true; - outgoing = FORCED_WRAP_MESSAGE; - expertView.push({ role: "assistant", content: FORCED_WRAP_MESSAGE }); - turns[turns.length - 1] = { - ...turn, - expert: { content: FORCED_WRAP_MESSAGE, stimulus: FORCED_WRAP_MESSAGE }, - }; - continue; - } - - console.error(`turn ${turnNumber} (expert)`); - const reply = await callExpert(situationPack, expertView); - const stimulus = turnNumber === IMPATIENCE_AT ? IMPATIENCE_LINE : undefined; - outgoing = stimulus ? `${reply.text}\n\n${stimulus}` : reply.text; - expertView.push({ role: "assistant", content: outgoing }); - turns[turns.length - 1] = { - ...turn, - expert: { - content: reply.text, - ...(stimulus ? { stimulus } : {}), - ...(reply.truncated ? { truncated: true } : {}), - }, - }; - } - await writeArtifacts(); - const last = turns.at(-1)?.completion; - console.error( - `done: ${stopReason} after ${turns.length} interviewer turns; ${last?.captures ?? 0} captures, complete ${yesNo(last?.complete === true)}; interviewer ${formatUsage(interviewerUsage)}; expert ${formatUsage(expertUsage)}`, - ); -} catch (error) { - stopReason = `runner-error: ${error instanceof Error ? error.message : String(error)}`; - console.error(error); - await writeArtifacts().catch((writeError: unknown) => - console.error(writeError), - ); - process.exitCode = 1; -} finally { - stopObserving(); - await flue.stop(); - await rm(targetDocumentDirectory, { recursive: true, force: true }); -} diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/protocol.md index 1b79e76a4ca..2a577a337c6 100644 --- a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/protocol.md +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/protocol.md @@ -15,8 +15,7 @@ harness around that text. **Retirement (2026-08-28).** This is a retained historical instrument, not a supported path for new evaluation runs. The current prospective path is [`../prospective-runbook-v1/`](../prospective-runbook-v1/); its evidence is graded with -[`../ir-quality-ruler-v1/`](../ir-quality-ruler-v1/). The runner and hermetic tests remain only -until a separately scoped removal verifies no remaining operational dependency. +[`../ir-quality-ruler-v1/`](../ir-quality-ruler-v1/). The unsupported runners and their hermetic timing test were removed after verifying that no current command or protocol depended on them. The exact executed sources remain reconstructible at commit `b59b323bf1b26eee9a2345a8412ca466f5d6e851`. ## Conditions @@ -50,7 +49,7 @@ are tiered: freely given, _(tacit)_ (surfaces only under reaching questions), _( (honest perspective error), _(doesn't know)_ (genuine absences the interviewer should record rather than fill). -## Mechanics ([run.ts](run.ts) for conditions 1, 2, and 4; [harness-run.ts](harness-run.ts) for 5) +## Mechanics (historical runners at commit `b59b323bf1b26eee9a2345a8412ca466f5d6e851`) - Alternating API calls; each side sees only its own history. The interviewer never sees the situation pack; the expert never sees the v0 prompt. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/run.ts b/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/run.ts deleted file mode 100644 index b2ece1339c9..00000000000 --- a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/run.ts +++ /dev/null @@ -1,636 +0,0 @@ -// Baseline interview runner (FE-1361) for the prompt-only conditions. -// -// Conditions 1 and 2 preserve the reviewed FE-1361 controls: bare Claude and the v0 elicitation -// prompt. Condition 4 is the ADR-0007 teaching layer as prompt only. Condition 5 — the shipped -// harness in the loop — runs from `harness-run.ts`. Condition 3 (the FE-1404 preregistered -// completion-and-guidance instrument with a test-only operator projection) was retired without a -// run and its code removed on 2026-08-26; `condition-3-preregistration.md` and -// `condition-3-prompt.md` remain as the record of what was planned. -// -// Usage: ANTHROPIC_API_KEY=... node --experimental-strip-types run.ts <1|2|4> [--resume|--continue-final] -// Condition 4's interviewer system prompt is condition-4-prompt.md plus the harness's rendering of -// the repertoire and the SDCPN plugin definition (contract, guidance, construct runbook), with no -// harness machinery behind it. It reads the rendering from `@hashintel/brunch-agent`'s built -// output, so run `turbo build` first. -// --resume continue an interrupted run from its checkpoint -// --continue-final ask the interviewer to finish a final delivery that was cut off at max_tokens -// -// Production outputs, under docs/evidence/evaluations/vestera-legacy-baseline/transcripts/: -// condition-<n>.md readable transcript with run metadata -// condition-<n>.raw.json full message arrays + per-call token usage (also the checkpoint) -// condition-<n>-model.txt the final delivery message, verbatim (delivered runs only) - -import { existsSync } from "node:fs"; -import { mkdir, readFile, writeFile } from "node:fs/promises"; -import { fileURLToPath } from "node:url"; - -import type Anthropic from "@anthropic-ai/sdk"; - -const INTERVIEWER_MODEL = "claude-opus-5"; -const EXPERT_MODEL = "claude-sonnet-5"; -const CLASSIFIER_MODEL = "claude-haiku-4-5-20251001"; - -// Interviewer turns, not exchanges. ReqElicitGym budgets 20; we force a wrap-up at 20 and -// hard-stop at 24 in case the model keeps talking instead of delivering. -const FORCE_WRAP_AT = 20; -const HARD_STOP_AT = 24; -// The scripted impatience probe (LLMREI: interviewers end too readily on impatience cues). -// Appended to the expert's reply on this exchange, identically in every condition. -const IMPATIENCE_AT = 8; -const IMPATIENCE_LINE = - "(Sorry — I've just seen the time, I have the floor huddle in ten minutes. How much more do you need?)"; -const FORCED_WRAP_MESSAGE = - "I really do have to stop here. Please produce the model now with everything you have."; -const CONTINUE_MESSAGE = - "You were cut off mid-document. Continue exactly from where you stopped — no preamble, no repetition."; - -type ChatMessage = Omit<Anthropic.MessageParam, "content"> & { - content: Extract<Anthropic.MessageParam["content"], string>; - // Present only when the API ended this model-generated message at its token limit. - // Older checkpoints and human-authored messages legitimately omit it. - truncated?: true; -}; - -type Usage = Pick<Anthropic.Usage, "input_tokens" | "output_tokens"> & - Partial< - Pick< - Anthropic.Usage, - "cache_creation_input_tokens" | "cache_read_input_tokens" - > - >; - -interface CallRecord { - agent: "interviewer" | "expert" | "classifier"; - model: Anthropic.Model; - usage: Usage; -} - -interface CallResult { - text: string; - truncated: boolean; -} - -interface RawCheckpoint { - startedAt: string; - condition: "1" | "2" | "4"; - stopReason: string; - calls: CallRecord[]; - interviewerMessages: ChatMessage[]; -} - -function usage(): never { - console.error("usage: node run.ts <1|2|4> [--resume|--continue-final]"); - process.exit(1); -} - -const conditionArg = process.argv[2]; -const mode = process.argv[3] ?? "fresh"; -if (conditionArg !== "1" && conditionArg !== "2" && conditionArg !== "4") - usage(); -if (mode !== "fresh" && mode !== "--resume" && mode !== "--continue-final") - usage(); -const condition = conditionArg; -const clientModule = process.env["BRUNCH_BASELINE_ANTHROPIC_MODULE"]; -const testOutputDirectory = process.env["BRUNCH_BASELINE_TEST_OUTPUT_DIR"]; -const apiKey = process.env["ANTHROPIC_API_KEY"]; -if (testOutputDirectory && !clientModule) { - console.error( - "BRUNCH_BASELINE_TEST_OUTPUT_DIR requires BRUNCH_BASELINE_ANTHROPIC_MODULE", - ); - process.exit(1); -} -if (!apiKey && !clientModule) { - console.error("ANTHROPIC_API_KEY is not set"); - process.exit(1); -} -interface BaselineAnthropicClient { - messages: { - create( - request: Anthropic.MessageCreateParamsNonStreaming, - ): Promise<Anthropic.Message>; - }; -} - -let anthropic: BaselineAnthropicClient | undefined; - -async function getAnthropic(): Promise<BaselineAnthropicClient> { - if (anthropic) return anthropic; - const resolvedClient = clientModule - ? ((await import(clientModule)).default as BaselineAnthropicClient) - : (new (await import("@anthropic-ai/sdk")).default({ - apiKey, - maxRetries: 5, - timeout: 30 * 60 * 1000, - }) as BaselineAnthropicClient); - anthropic = resolvedClient; - return resolvedClient; -} - -const baseDir = fileURLToPath(new URL(".", import.meta.url)); -const caseDir = fileURLToPath( - new URL("../../cases/vestera-scheduling/", import.meta.url), -); -const transcriptDir = - testOutputDirectory ?? - fileURLToPath( - new URL( - "../../../docs/evidence/evaluations/vestera-legacy-baseline/transcripts/", - import.meta.url, - ), - ); -const calls: CallRecord[] = []; - -async function callClaude( - agent: CallRecord["agent"], - model: CallRecord["model"], - system: string | undefined, - messages: ChatMessage[], - maxTokens: number, - options: { allowThinking?: boolean } = {}, -): Promise<CallResult> { - // The interviewer keeps the model's default (adaptive) thinking — that is part of "vanilla - // Claude". The expert and classifier have it disabled: a thinking block that consumes the - // whole token budget yields an empty text message, which the API then rejects on re-send. - // Transport-level retries (429/5xx/network, retry-after) live in the SDK; this loop only - // handles the empty-text case, which is a budget problem rather than a transport one. - let tokenBudget = maxTokens; - for (let attempt = 1; attempt <= 5; attempt++) { - const response = await ( - await getAnthropic() - ).messages.create({ - model, - max_tokens: tokenBudget, - ...(options.allowThinking - ? {} - : { thinking: { type: "disabled" as const } }), - ...(system ? { system } : {}), - // Project persistence metadata out of the provider request. - messages: messages.map((message) => ({ - role: message.role, - content: message.content, - })), - }); - calls.push({ - agent, - model: response.model, - usage: { - input_tokens: response.usage.input_tokens, - output_tokens: response.usage.output_tokens, - cache_creation_input_tokens: - response.usage.cache_creation_input_tokens ?? 0, - cache_read_input_tokens: response.usage.cache_read_input_tokens ?? 0, - }, - }); - const text = response.content - .filter((block) => block.type === "text") - .map((block) => block.text) - .join("\n"); - if (text.trim() === "") { - // Adaptive thinking can consume the entire budget before any text is emitted. - tokenBudget *= 2; - console.error( - ` ${agent}: empty text (blocks: ${response.content.map((block) => block.type).join(",")}), ` + - `retrying with max_tokens=${tokenBudget}`, - ); - continue; - } - return { text, truncated: response.stop_reason === "max_tokens" }; - } - throw new Error(`${agent}: exhausted retries`); -} - -// The interviewer's final delivery can exceed one response budget; stitch continuations into -// a single message so the transcript holds the complete deliverable. The truncation flag of -// the *last* piece survives the stitching: a message still cut off after the piece cap must -// be reported as incomplete, not silently written as if it were whole. -async function callInterviewer( - system: string | undefined, - messages: ChatMessage[], -): Promise<CallResult> { - let result = await callClaude( - "interviewer", - INTERVIEWER_MODEL, - system, - messages, - 16_000, - { - allowThinking: true, - }, - ); - let text = result.text; - for (let piece = 1; result.truncated && piece <= 4; piece++) { - console.error(` interviewer: truncated, requesting continuation ${piece}`); - result = await callClaude( - "interviewer", - INTERVIEWER_MODEL, - system, - [ - ...messages, - { role: "assistant", content: text }, - { role: "user", content: CONTINUE_MESSAGE }, - ], - 16_000, - { allowThinking: true }, - ); - // No separator at the seam: the cut usually lands mid-line or mid-token - // and the model is instructed to continue exactly from where it stopped, - // so an injected newline would corrupt the merged document. - text += result.text; - } - return { text, truncated: result.truncated }; -} - -async function loadSection(file: string): Promise<string> { - const raw = await readFile( - file.startsWith("/") ? file : baseDir + file, - "utf8", - ); - const separatorIndex = raw.indexOf("\n---\n"); - return separatorIndex === -1 - ? raw.trim() - : raw.slice(separatorIndex + 5).trim(); -} - -async function isFinalModel(message: string): Promise<boolean> { - const verdict = await callClaude( - "classifier", - CLASSIFIER_MODEL, - "You label messages from an AI assistant that was asked to interview a user and then " + - "produce a process model. Answer with exactly YES or NO.", - [ - { - role: "user", - content: - "Does the following message contain the final model deliverable (a complete model " + - "artifact such as a JSON document or a full structured model specification), as " + - "opposed to only questions, discussion, or interim summaries?\n\n<message>\n" + - message + - "\n</message>", - }, - ], - 16, - ); - return verdict.text.trim().toUpperCase().startsWith("YES"); -} - -const openingMessage = await loadSection(`${caseDir}opening-message.md`); - -/** - * Condition 4's system prompt: the hand-written framing for a prompt-only run, - * then the harness's own rendering of the repertoire and the SDCPN definition — - * the same text the binding would put in front of the interviewer, minus the - * preamble about machinery this run does not have. - */ -async function renderCondition4System(): Promise<string> { - const harness = (await import("@hashintel/brunch-agent")) as { - readPluginDefinition: (yaml: string) => unknown; - readRepertoire: (yaml: string) => unknown; - renderContract: (definition: unknown) => string[]; - renderGuidance: (repertoire: unknown, definition: unknown) => string[]; - renderRunbook: ( - repertoire: unknown, - definition: unknown, - job: "construct" | "review-and-revise", - ) => string; - }; - const packagesDir = fileURLToPath( - new URL("../../../../packages/", import.meta.url), - ); - const definition = harness.readPluginDefinition( - await readFile(`${packagesDir}plugin-sdcpn/plugin.yaml`, "utf8"), - ); - const repertoire = harness.readRepertoire( - await readFile(`${packagesDir}repertoire/repertoire.yaml`, "utf8"), - ); - const rendered = [ - ...harness.renderContract(definition), - ...harness.renderGuidance(repertoire, definition), - harness.renderRunbook(repertoire, definition, "construct"), - ].join("\n\n"); - return `${await loadSection("condition-4-prompt.md")}\n\n${rendered}`; -} - -const interviewerSystem = - condition === "2" - ? await loadSection("v0-prompt.md") - : condition === "4" - ? await renderCondition4System() - : undefined; -const situationPack = await readFile(`${caseDir}situation-pack.md`, "utf8"); - -let interviewerMessages: ChatMessage[] = [ - { role: "user", content: openingMessage }, -]; -let stopReason = "hard-stop"; - -// The expert sees the same conversation from the other side: everything after -// the opening message, roles flipped. Derived on demand rather than kept as a -// parallel array every push had to maintain and resume had to rebuild. -function expertView(): ChatMessage[] { - return interviewerMessages.slice(1).map((message) => ({ - role: - message.role === "assistant" ? ("user" as const) : ("assistant" as const), - content: message.content, - })); -} -let interviewerTurns = 0; -let startedAt = new Date().toISOString(); -await mkdir(transcriptDir, { recursive: true }); -const artifactStem = `condition-${condition}`; -const rawPath = `${transcriptDir}/${artifactStem}.raw.json`; - -if (mode === "fresh" && existsSync(rawPath)) { - // The checkpoint is also the run's only record; an unguarded fresh run - // overwrites hours of paid transcript on its first in-progress write. - console.error( - `${rawPath} already exists — a fresh run would overwrite it. ` + - "Use --resume (or --continue-final), or move the transcripts for this condition aside first.", - ); - process.exit(1); -} - -if (mode !== "fresh") { - const checkpoint = JSON.parse( - await readFile(rawPath, "utf8"), - ) as RawCheckpoint; - interviewerMessages = checkpoint.interviewerMessages; - calls.push(...checkpoint.calls); - interviewerTurns = interviewerMessages.filter( - (message) => message.role === "assistant", - ).length; - startedAt = checkpoint.startedAt; - stopReason = checkpoint.stopReason; -} - -function writeCheckpoint(reason: string): Promise<void> { - const checkpoint: RawCheckpoint = { - startedAt, - condition, - stopReason: reason, - calls, - interviewerMessages, - }; - return writeFile(rawPath, JSON.stringify(checkpoint, null, 2)); -} - -async function writeArtifacts(): Promise<void> { - // input_tokens excludes cache reads and writes, so summing it alone - // undercounts what the run actually paid for. Count all three. - const totals = calls.reduce( - (accumulator, call) => { - accumulator.input += call.usage.input_tokens; - accumulator.cacheWrite += call.usage.cache_creation_input_tokens ?? 0; - accumulator.cacheRead += call.usage.cache_read_input_tokens ?? 0; - accumulator.output += call.usage.output_tokens; - return accumulator; - }, - { input: 0, cacheWrite: 0, cacheRead: 0, output: 0 }, - ); - - const header = [ - `# Baseline control — condition ${condition} (${condition === "1" ? "bare" : condition === "2" ? "v0 prompt" : "rendered repertoire + plugin definition, prompt only"})`, - "", - `- Run started: ${startedAt}`, - `- Interviewer: ${INTERVIEWER_MODEL}${ - condition === "2" - ? " + v0-prompt.md" - : condition === "4" - ? " + condition-4-prompt.md + rendered repertoire.yaml + plugin-sdcpn/plugin.yaml (see condition-4-system.md)" - : " (no system prompt)" - }`, - `- Simulated expert: ${EXPERT_MODEL} + situation-pack.md`, - `- Interviewer turns: ${interviewerTurns} (impatience probe at ${IMPATIENCE_AT}, forced wrap at ${FORCE_WRAP_AT})`, - `- Stop reason: ${stopReason}`, - `- Tokens: ${totals.input} in (+${totals.cacheWrite} cache write, +${totals.cacheRead} cache read) / ${totals.output} out across ${calls.length} calls`, - "", - "---", - "", - ].join("\n"); - - const body = interviewerMessages - .map((message, index) => { - const speaker = - message.role === "assistant" - ? "**Interviewer**" - : index === 0 - ? "**Opening message**" - : "**Expert (Marta)**"; - return `${speaker}:\n\n${message.content}`; - }) - .join("\n\n---\n\n"); - - await writeFile(`${transcriptDir}/${artifactStem}.md`, header + body + "\n"); - if (condition === "4" && interviewerSystem !== undefined) { - await writeFile( - `${transcriptDir}/${artifactStem}-system.md`, - `# Condition 4 — assembled interviewer system prompt\n\n${interviewerSystem}\n`, - ); - } - await writeCheckpoint(stopReason); - - // The model artifact is the interviewer's final delivery message, verbatim. - // Extracting "the model" out of it (the old largest-fenced-block heuristic) - // depended on the delivery's formatting whims — one run fenced its whole - // model, the other delivered structured markdown with small illustrative - // fences, and the heuristic shipped a 517-byte fragment as that run's - // artifact. The delivery document is self-describing; readers compare the - // conditions' documents directly. - const finalMessage = interviewerMessages.at(-1); - if ( - stopReason.startsWith("delivered") && - finalMessage?.role === "assistant" - ) { - await writeFile( - `${transcriptDir}/${artifactStem}-model.txt`, - finalMessage.content, - ); - } else if (stopReason.startsWith("delivered")) { - // The transcript header claims a delivery, so a missing artifact must be - // loud — hours of paid run otherwise end with the main deliverable - // silently absent. - console.error( - `⚠ stop reason is '${stopReason}' but the transcript does not end with an interviewer ` + - `message — ${artifactStem}-model.txt was NOT written`, - ); - } - - console.error( - `done: ${stopReason} after ${interviewerTurns} interviewer turns; ` + - `${totals.input} in (+${totals.cacheWrite} cache write, +${totals.cacheRead} cache read) / ` + - `${totals.output} out`, - ); -} - -if (mode === "--continue-final") { - const final = interviewerMessages.at(-1); - if ( - final?.role !== "assistant" || - !final.truncated || - !stopReason.endsWith("-incomplete") - ) { - console.error( - "checkpoint does not end with a truncated interviewer message; nothing to continue", - ); - process.exit(1); - } - const priorMessages = interviewerMessages.slice(0, -1); - const continued = await callInterviewer(interviewerSystem, [ - ...priorMessages, - { role: "assistant", content: final.content }, - { role: "user", content: CONTINUE_MESSAGE }, - ]); - final.content += continued.text; - if (continued.truncated) { - console.error( - "⚠ still truncated after this continuation — run --continue-final again", - ); - } else { - delete final.truncated; - stopReason = stopReason.slice(0, -"-incomplete".length); - } - await writeArtifacts(); - process.exit(0); -} - -if (mode === "--resume") { - // A delivered checkpoint must never resume: doing so would pop and regenerate the paid - // final delivery, then overwrite the transcript. Check the durable reason rather than the - // trailing role because a capped non-final interviewer turn also ends with an assistant. - if (stopReason.startsWith("delivered")) { - console.error( - `condition ${condition} already ended '${stopReason}' — resuming would regenerate and ` + - "overwrite its final delivery. Use --continue-final to finish a truncated delivery, " + - "or move the transcripts aside to rerun from scratch.", - ); - process.exit(1); - } - if (stopReason === "expert-truncated") { - const partialExpertReply = interviewerMessages.at(-1); - if (partialExpertReply?.role !== "user" || !partialExpertReply.truncated) { - console.error( - "checkpoint says 'expert-truncated' but does not end with a truncated expert reply", - ); - process.exit(1); - } - // The partial text remains in the stopped checkpoint as evidence, but must never be fed - // to the interviewer as a complete answer. Resume removes it and retries the expert call - // against the same preceding interviewer question. - interviewerMessages.pop(); - console.error( - `regenerating truncated expert reply at interviewer turn ${interviewerTurns}`, - ); - const expertResult = await callClaude( - "expert", - EXPERT_MODEL, - situationPack, - expertView(), - 1_500, - ); - let expertText = expertResult.text; - if (interviewerTurns === IMPATIENCE_AT) { - expertText = `${expertText}\n\n${IMPATIENCE_LINE}`; - } - if (expertResult.truncated) { - interviewerMessages.push({ - role: "user", - content: expertText, - truncated: true, - }); - console.error( - "⚠ the regenerated expert reply is still truncated — checkpointed the partial reply " + - "without sending it to the interviewer; rerun with --resume to try again", - ); - await writeArtifacts(); - process.exit(0); - } - interviewerMessages.push({ role: "user", content: expertText }); - await writeCheckpoint("in-progress"); - } - // Checkpoints are written after complete exchanges only, but tolerate a trailing - // assistant message by regenerating that turn. - stopReason = "hard-stop"; - if (interviewerMessages.at(-1)?.role === "assistant") { - interviewerMessages.pop(); - } - interviewerTurns = interviewerMessages.filter( - (message) => message.role === "assistant", - ).length; - console.error( - `resuming condition ${condition} at interviewer turn ${interviewerTurns + 1}`, - ); -} - -while (interviewerTurns < HARD_STOP_AT) { - interviewerTurns++; - console.error(`turn ${interviewerTurns} (interviewer)`); - const interviewer = await callInterviewer( - interviewerSystem, - interviewerMessages, - ); - interviewerMessages.push({ - role: "assistant", - content: interviewer.text, - ...(interviewer.truncated ? { truncated: true as const } : {}), - }); - - if (await isFinalModel(interviewer.text)) { - stopReason = - interviewerTurns >= FORCE_WRAP_AT - ? "delivered-after-forced-wrap" - : "delivered"; - if (interviewer.truncated) { - stopReason += "-incomplete"; - console.error( - "⚠ the final delivery is still truncated after stitching — " + - "rerun with --continue-final to finish it", - ); - } - break; - } - - if (interviewer.truncated) { - stopReason = "interviewer-truncated"; - console.error( - "⚠ the non-final interviewer reply is truncated after the continuation cap — " + - "checkpointed it without sending the partial question to the expert; rerun with " + - "--resume to regenerate the interviewer turn", - ); - break; - } - - let expertText: string; - let expertTruncated = false; - if (interviewerTurns >= FORCE_WRAP_AT) { - expertText = FORCED_WRAP_MESSAGE; - } else { - console.error(`turn ${interviewerTurns} (expert)`); - const expertResult = await callClaude( - "expert", - EXPERT_MODEL, - situationPack, - expertView(), - 1_500, - ); - expertText = expertResult.text; - expertTruncated = expertResult.truncated; - if (interviewerTurns === IMPATIENCE_AT) { - expertText = `${expertText}\n\n${IMPATIENCE_LINE}`; - } - } - interviewerMessages.push({ - role: "user", - content: expertText, - ...(expertTruncated ? { truncated: true as const } : {}), - }); - if (expertTruncated) { - stopReason = "expert-truncated"; - console.error( - "⚠ the expert reply is truncated — checkpointed the partial reply without sending it " + - "to the interviewer; rerun with --resume to regenerate it", - ); - break; - } - await writeCheckpoint("in-progress"); -} - -await writeArtifacts(); diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/turn-timing.ts b/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/turn-timing.ts deleted file mode 100644 index 92a18a778d3..00000000000 --- a/libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/turn-timing.ts +++ /dev/null @@ -1,162 +0,0 @@ -import type { FlueObservation, ModelRequest } from "@flue/runtime"; - -export const TURN_TIMING_PURPOSES = ["interview", "sweep", "repair"] as const; - -export type TurnTimingPurpose = (typeof TURN_TIMING_PURPOSES)[number]; - -export interface TurnTimingRecord { - readonly interviewerTurn: number; - readonly flueTurnId: string; - readonly purpose: TurnTimingPurpose; - readonly durationMs: number; -} - -export interface TurnTimingRecorder { - startInterviewerTurn(interviewerTurn: number): void; - observe(event: FlueObservation): void; - forInterviewerTurn(interviewerTurn: number): readonly TurnTimingRecord[]; - all(): readonly TurnTimingRecord[]; -} - -const signalPurpose = ( - request: ModelRequest, -): TurnTimingPurpose | undefined => { - const latestMessage = request.input.messages.at(-1); - if (latestMessage?.role !== "user") return undefined; - const content = - typeof latestMessage.content === "string" - ? latestMessage.content - : latestMessage.content - .filter((part) => part.type === "text") - .map((part) => part.text) - .join("\n"); - if (content.startsWith("<sweep-repair")) return "repair"; - if (content.startsWith("<settlement-check")) return "sweep"; - return undefined; -}; - -const toolResultStatus = (event: FlueObservation): string | undefined => { - if (event.type !== "tool") return undefined; - const result = event.effectiveResult ?? event.result; - if (typeof result !== "object" || result === null) return undefined; - const output = "output" in result ? result.output : result; - if (typeof output !== "object" || output === null) return undefined; - return "status" in output && typeof output.status === "string" - ? output.status - : undefined; -}; - -export const createTurnTimingRecorder = (): TurnTimingRecorder => { - let currentInterviewerTurn: number | undefined; - const activePromptOperationIds: string[] = []; - const nestedPromptOperationIds = new Set<string>(); - const repairingPromptOperationIds = new Set<string>(); - const purposeByOperation = new Map<string, TurnTimingPurpose>(); - const purposeByFlueTurn = new Map<string, TurnTimingPurpose>(); - const timingRecords: TurnTimingRecord[] = []; - - return { - startInterviewerTurn(interviewerTurn) { - currentInterviewerTurn = interviewerTurn; - }, - observe(event) { - if ( - event.type === "operation_start" && - event.operationKind === "prompt" - ) { - const parentOperationId = activePromptOperationIds.at(-1); - if (parentOperationId !== undefined) { - nestedPromptOperationIds.add(event.operationId); - purposeByOperation.set( - event.operationId, - repairingPromptOperationIds.has(parentOperationId) || - purposeByOperation.get(parentOperationId) === "repair" - ? "repair" - : "sweep", - ); - } - activePromptOperationIds.push(event.operationId); - return; - } - if (event.type === "operation") { - const activeIndex = activePromptOperationIds.lastIndexOf( - event.operationId, - ); - if (activeIndex !== -1) activePromptOperationIds.splice(activeIndex, 1); - nestedPromptOperationIds.delete(event.operationId); - repairingPromptOperationIds.delete(event.operationId); - purposeByOperation.delete(event.operationId); - return; - } - if (event.type === "tool" && event.toolName === "brunch_sweep") { - const activeOperationId = activePromptOperationIds.at(-1); - if (activeOperationId === undefined) return; - const status = toolResultStatus(event); - if (status === undefined) return; - if (status === "refused") { - repairingPromptOperationIds.add(activeOperationId); - purposeByOperation.set(activeOperationId, "repair"); - } else { - purposeByOperation.set( - activeOperationId, - repairingPromptOperationIds.has(activeOperationId) - ? "repair" - : "sweep", - ); - repairingPromptOperationIds.delete(activeOperationId); - } - return; - } - if (event.type === "turn_request") { - const operationId = - event.operationId !== undefined && - purposeByOperation.has(event.operationId) - ? event.operationId - : activePromptOperationIds.at(-1); - const operationPurpose = - operationId === undefined - ? undefined - : purposeByOperation.get(operationId); - const purpose = - operationId !== undefined && nestedPromptOperationIds.has(operationId) - ? (operationPurpose ?? "sweep") - : event.purpose === "agent" - ? (signalPurpose(event.request) ?? - operationPurpose ?? - "interview") - : (operationPurpose ?? "interview"); - purposeByFlueTurn.set(event.turnId, purpose); - if ( - event.operationId !== undefined && - !nestedPromptOperationIds.has(event.operationId) - ) { - purposeByOperation.set(event.operationId, purpose); - } - return; - } - if (event.type !== "turn" || currentInterviewerTurn === undefined) { - return; - } - timingRecords.push({ - interviewerTurn: currentInterviewerTurn, - flueTurnId: event.turnId, - purpose: - purposeByFlueTurn.get(event.turnId) ?? - (event.operationId === undefined - ? undefined - : purposeByOperation.get(event.operationId)) ?? - "interview", - durationMs: event.durationMs, - }); - purposeByFlueTurn.delete(event.turnId); - }, - forInterviewerTurn(interviewerTurn) { - return timingRecords.filter( - (timingRecord) => timingRecord.interviewerTurn === interviewerTurn, - ); - }, - all() { - return timingRecords; - }, - }; -}; diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/instrument-manifest.json b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/instrument-manifest.json new file mode 100644 index 00000000000..a0346052c95 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/instrument-manifest.json @@ -0,0 +1,176 @@ +{ + "version": 1, + "campaign": "mission-4-proof-of-life-v1", + "instrumentCommit": "ce2fbde9d96faaaf52ecf532e1071d3d9e952f1a", + "models": { + "elicitor": { + "provider": "anthropic", + "model": "claude-sonnet-4-6" + }, + "persona": { + "provider": "openai", + "model": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "model": "claude-opus-4-6", + "thinking": "high" + } + }, + "host": "none", + "logicalCeiling": { + "conversationAttempts": 10, + "brunchSubmissions": 32, + "personaContinuations": 28, + "adjudications": 10 + }, + "proposedCurrencyCeilingUsd": 10, + "controlledPromptSha256": { + "S3": "ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729", + "S4": "64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635" + }, + "verification": { + "command": "yarn exec turbo run build lint:tsc lint:eslint test:unit --filter '@hashintel/brunch-agent' --filter '@hashintel/brunch-agent-binding-flue' --filter '@hashintel/brunch-agent-transport-aisdk' --filter '@hashintel/brunch-agent-plugin-sdcpn' --filter '@hashintel/brunch-agent-plugin-gherkin' --filter '@hashintel/brunch-agent-plugin-dafny' --filter '@apps/brunch-agent'", + "tasks": { + "successful": 30, + "total": 30 + }, + "mission3EvidenceUnchangedFrom": "4c11c7a6c4e1df26c9d76cec30e32af8f013042d", + "universalGuidanceMatchesSourceAt": "ca57b45729260cc657f89b718fc505997a4e1b3c" + }, + "files": [ + { + "path": "apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts", + "sha256": "f20f922e4d85ca7abd199dfc276d174f04b4c413d2b1f515a4ee6135a30272a6" + }, + { + "path": "apps/brunch-agent/.pi/extensions/brunch-persona-testing/SYSTEM.md", + "sha256": "1a21e60de561161ef3d26ff72de42c2f7abbb1b05f5236f60ac03cfef06ace51" + }, + { + "path": "apps/brunch-agent/src/agents/chat-agent/agent.ts", + "sha256": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e" + }, + { + "path": "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts", + "sha256": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/brunch-turn.ts", + "sha256": "669e1c40acde9dd903034725d850d8f99cb78376efa9d8a8879df7f2c0549e02" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/client-tool-hosts.ts", + "sha256": "f7757866b58592c0933e091832b21337eb26225f9b4517180a68d07668839ed3" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/proof-artifacts.ts", + "sha256": "4d3c4878b6941636343df69acfca95c9cd2ba145eaaa5763b00fa09198fb347e" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/refresh-proof-manifest.ts", + "sha256": "eb43c845159c902c3c9b7d89de034151d76ea214fc8e9ea37b95fa8800dc4374" + }, + { + "path": "libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-preflight-2026-09-03.md", + "sha256": "9760d05bf4771f325c02d4989c2618c7307f2de9d9ec393fe43588bd91198f68" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/opening-message.md", + "sha256": "bf46a637d029f34758c398f3d1f5ac70c2d10bfd169b05b23497215792a16ef8" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/situation-pack.md", + "sha256": "ab5f7f701265e4a01eea181c27327891c208b82ce75235cec0344f90fd2fe5ce" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json", + "sha256": "1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/opening-message.md", + "sha256": "75ac100dc0f16771feced90563f4c57a5c9e918bbd2cc6fd7af0cd5de7165659" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/situation-pack.md", + "sha256": "07f36060ebbb1cccd2c20ccd2a44ae732b23d69b82e2b018c4b05e6844d6d55e" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md", + "sha256": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md", + "sha256": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v1.md", + "sha256": "eb83027f2316a2fc27a1f165b0738afbe3bbc0136d52b33e5be1b9e366dcd780" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/protocol.md", + "sha256": "6ff0f56f8f267db901f15c6411e4e10cf58dd95de93b7c1de2f064a797daff17" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/prompts/SYSTEM.md", + "sha256": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/SKILL.md", + "sha256": "68b7fa27c2ba8401a97272e63c17d0ad6c6fdb9b3c81d9aa02e7ec3120e0aacc" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/skill.ts", + "sha256": "5ab4a1cd714b6b819e51864d48ec2fe655fc6a335a73eb51c7126ebdd9c631f2" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts", + "sha256": "f1ceb78f5e503032fa324f62cab0f44021bc2ebf6c46e098d56ea0cd492175ed" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts", + "sha256": "5feb06b4571f36e7a1998c0fff431bb6d6dfe6acfe62e95252ff32d0e5cabda6" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/prompts/APPEND_SYSTEM.md", + "sha256": "8826b85d32d6c24fbc8f678ec394600676ec61c4a0b83d0a9cfa16be87fc5d76" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts", + "sha256": "8ab4c314d9824d521f5d375c71353c42011be943091e05732f9b55f305133af5" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md", + "sha256": "ff0d9351bf6f130188c325d0fd158bd5b874b3eb18d3a4f195e8487dc811dde9" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/checks.md", + "sha256": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/pn-construction.md", + "sha256": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/profile.md", + "sha256": "cf37161ee79cace2d96ee6d473e9751cab65cc050ebae79d94530a01705b2b8e" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts", + "sha256": "201fbf3cb4655f9eaee23e07dc289e58f348e967df455195fdd35d4757371b73" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md", + "sha256": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts", + "sha256": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts", + "sha256": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/protocol.md new file mode 100644 index 00000000000..9824913839d --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v1/protocol.md @@ -0,0 +1,131 @@ +# Mission 4 proof-of-life protocol v1 + +Status: **freeze candidate prepared from owner decisions recorded on 2026-09-03; not frozen and not authorized for paid execution until the owner accepts the subsequent machine-readable manifest and $10 USD ceiling.** Canonical snapshot/trace/workpiece/hash retention is landed, all focused checks pass, direct-provider catalog entries and credential presence are confirmed without a model call, and current official prices and estimates are recorded. + +## Claim + +This protocol can support only the bounded claim in [`../../../MISSION.md`](../../../MISSION.md): the implemented independent `elicitation` capability activates with its SDCPN job skill before substantive interviewing across three named case families, conditionally reads the SDCPN profile before reliance, avoids an opening Battery, refrains on the exact S3 resolvable-review input, activates on the exact S4 knowledge-gap input, and yields one attributable but unpromoted downstream workpiece candidate. + +It does not estimate general reliability, grade workpiece quality, accept the topology-neutral portfolio, compare against Mission 3, prove Petrinaut `/api/chat` or browser behavior, or promote any artifact to a fixture or database seed. + +## Accepted oracle + +Grade only with [`../../oracles/mission-4-activation-and-restraint-ruler-v1.md`](../../oracles/mission-4-activation-and-restraint-ruler-v1.md). The freeze manifest records its exact SHA-256. The ruler is evaluator-only and never enters Brunch or persona context. + +## Model and host allocation + +| Role | Requested configuration | Pre-freeze requirement | +| --- | --- | --- | +| Elicitor | `BRUNCH_CHAT_MODEL=claude-sonnet-4-6`, resolving through the production Anthropic provider | Record the provider-reported exact model id and verify the built app uses it. No fallback. | +| Persona | Pi `--model openai/gpt-5.6-sol --thinking medium`, using the direct OpenAI provider | Record requested and provider-reported ids. No fallback or router substitution. | +| Adjudicator | `anthropic/claude-opus-4-6`, high thinking, one fresh context per technically usable attempt | Record requested/provider-reported ids. No fallback. | +| Client-tool host | `none` for every slot | Any client-tool suspension is technical invalidity; do not service it with a different host. | + +OpenAI elicitor comparison is outside Mission 4 and must not be inserted into this campaign. Results are reported per elicitor model and never pooled. + +## Fixed slot allocation and order + +Run serially in this order, validating and adjudicating each retained attempt before admitting the next slot: + +1. `vestera-probe`: Vestera Scheduling, first-Substantive probe, at most three visible user turns. +2. `data-centre-probe`: Data Centre Thermal Operations, first-Substantive probe, at most three visible user turns. +3. `s3-review`: exact controlled S3 resolvable-review prompt, one visible user turn through settlement. +4. `s4-knowledge-gap`: exact controlled S4 knowledge-gap prompt, one visible user turn through settlement. +5. `industrial-gas-full`: Industrial Gas VMI, six-turn minimum and eight-turn maximum, producing the only possible handoff candidate. + +A valid behavioral failure stops the campaign for owner adjudication. Do not run later slots to average it away. + +### Interactive inputs + +The first visible user message is exactly the text below the `---` separator in the named `opening-message.md`; headers and private instructions are not sent to Brunch. + +| Slot | Opening path | Current SHA-256 | Private situation pack | Current SHA-256 | +| --- | --- | --- | --- | --- | +| `vestera-probe` | `../../cases/vestera-scheduling/opening-message.md` | `84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54` | `../../cases/vestera-scheduling/situation-pack.md` | `4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083` | +| `data-centre-probe` | `../../cases/data-centre-thermal-operations/opening-message.md` | `bf46a637d029f34758c398f3d1f5ac70c2d10bfd169b05b23497215792a16ef8` | `../../cases/data-centre-thermal-operations/situation-pack.md` | `ab5f7f701265e4a01eea181c27327891c208b82ce75235cec0344f90fd2fe5ce` | +| `industrial-gas-full` | `../../cases/industrial-gas-vmi/opening-message.md` | `75ac100dc0f16771feced90563f4c57a5c9e918bbd2cc6fd7af0cd5de7165659` | `../../cases/industrial-gas-vmi/situation-pack.md` | `07f36060ebbb1cccd2c20ccd2a44ae732b23d69b82e2b018c4b05e6844d6d55e` | + +The machine-readable freeze manifest recomputes and binds these identities. + +Probe private objective: + +> Act only as the person in the supplied situation pack. Send the exact opening message first, then answer Brunch naturally from the pack. Stop immediately after Brunch's first Substantive operational question. If none appears, stop after three visible user turns. Do not seek a workpiece or construction. + +Industrial Gas private objective: + +> Establish enough to represent the Alder outage-response decision and compare its immediate replenishment and dispatch options while preserving unresolved parameters. Seek a recoverable operational workpiece, not net construction. Send the exact opening message first and answer only from the supplied situation pack. Continue for at least six visible user turns. From turn six onward, stop when Brunch supplies a recoverable `runbook-ir` workpiece without leaving a direct question unanswered. Stop after eight visible user turns regardless. + +### Controlled review inputs + +Use the exact S3 and S4 `prompt` strings in [`../../cases/flue-skill-composition-side-quest-v3/scenarios.json`](../../cases/flue-skill-composition-side-quest-v3/scenarios.json), file SHA-256 `1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb`. S3 prompt-string SHA-256 is `ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729`; S4 prompt-string SHA-256 is `64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635`. The freeze manifest binds all three. These fixed, explicitly cued inputs are controlled mechanism checks only. + +## Attempt identities and replacement rule + +Reserve these ids; never reuse an admitted id: + +| Slot | Primary | Sole permitted replacement | +| --- | --- | --- | +| Vestera probe | `m4-pol-v1-vestera-p1` | `m4-pol-v1-vestera-r1` | +| Data Centre probe | `m4-pol-v1-data-centre-p1` | `m4-pol-v1-data-centre-r1` | +| S3 review | `m4-pol-v1-s3-p1` | `m4-pol-v1-s3-r1` | +| S4 knowledge gap | `m4-pol-v1-s4-p1` | `m4-pol-v1-s4-r1` | +| Industrial Gas full | `m4-pol-v1-industrial-gas-p1` | `m4-pol-v1-industrial-gas-r1` | + +Retain every admitted attempt. Permit the replacement only when the primary is technically invalid under the ruler or, for an interactive slot, technically valid but reaches no Substantive text within budget. A replacement repeats the same frozen inputs and settings under its reserved fresh id. A second invalid or no-Substantive result stops the campaign. Never replace a valid behavioral failure or a full run that reaches substance but fails to emit a recoverable workpiece. + +## Paid ceiling and stop rule + +The hard logical ceiling is 10 conversation attempts, 32 visible user submissions to Brunch, 28 persona continuations, and 10 fresh adjudications. Internal Sonnet provider continuations caused by skill/resource calls are metered and reported but are not falsely equated with visible submissions. Normal success is five conversation attempts, approximately 14 Brunch submissions, approximately 12 persona continuations, and five adjudications. + +This ceiling is not spending authorization. The non-billable [model and cost preflight](../../../docs/evidence/decisions/mission-4-proof-of-life-preflight-2026-09-03.md) records direct-provider catalog/credential presence, official prices, a $3.16 normal estimate, a $7.65 worst-case planning estimate, and a proposed $10 USD hard campaign ceiling. Before the first model call, obtain explicit owner authorization for the frozen instrument and that currency ceiling. Exceeding any logical or authorized currency ceiling stops execution. + +## Required mechanism before freeze + +No run may begin until focused tests prove all of the following against canonical Flue `history()`: + +1. A raw settled snapshot writer retains the exact JSON used for grading. +2. A deterministic trace derives visible user turn indices, ordered skill activations and outcomes, conditional resource reads and outcomes, other tool/executor events, and workpiece-bearing text events. +3. Canonical ordering distinguishes a profile/template read before text from one after text in the same turn. +4. Workpiece recovery records source message id and binds it to the raw snapshot. +5. Construct-only results expose activated skill names so ruler item 4c is decidable. +6. A protocol-owned `run.json` records source/frozen commit, slot, attempt, models, reasoning settings, and host; `validity.json` records validity and stop reason; a refreshable manifest hashes both records and every other retained artifact. + +Mechanism code may not classify semantic turns or decide pass/fail. The independent adjudicator applies the ruler to the trace and visible text. + +## Context isolation + +- Brunch receives only visible user messages and its production-mounted prompt, skills, resources, and tools. +- Interactive personas receive only the persona system policy, their situation pack, private objective, turn budget, and Brunch text returned by `brunch_turn`. They receive no ruler, oracle, target answer, repository tools, or evaluation-side tool details. +- S3/S4 are sent directly as fixed user inputs and use no persona model. +- Each adjudicator context receives the accepted ruler, one raw snapshot, its derived trace, formatted transcript, slot/attempt identity, and mechanical validity record. It receives no private situation pack or case oracle. It must quote the text supporting every semantic classification or finding. +- The owner sees all retained attempts and adjudications when deciding the bounded claim. + +## Per-attempt retention + +Write each admitted attempt under `docs/evidence/evaluations/mission-4-proof-of-life-v1/runs/<attempt-id>/`: + +- `run.json` — protocol-owned attempt identity, source/frozen commit, exact requested/reported models, reasoning settings, host, budgets, and launch time, written before admission; +- `snapshot.json` — canonical settled `history()` snapshot; +- `transcript.md` — formatted projection of that snapshot; +- `trace.json` and `trace.md` — mechanically equivalent ordered events; +- `validity.json` — mechanical validity and stop reason; +- `adjudication.md` — fresh-context quoted ruler application when technically usable; +- `workpiece.md` — only when mechanically recovered from a `runbook-ir` block; +- `manifest.json` — SHA-256 for every sibling artifact; refresh it after adding or changing validity/adjudication records with `yarn workspace @apps/brunch-agent proof:manifest -- <attempt-directory>`. + +Campaign root files must include the frozen protocol/instrument manifest, attempt ledger, spend/usage ledger, and final adjudication. Invalid and non-qualifying attempts remain visible in the ledger and are never included in the `3/3` numerator or denominator. + +The Industrial Gas workpiece, if recovered, is labelled `evaluation-run` and `handoff-candidate`. Its manifest must say that it is not an accepted workpiece, reusable fixture, database seed, product conversation, Petrinaut witness, or quality result. + +## Freeze sequence + +1. Land and verify the evidence mechanism without changing model-facing production text. +2. Confirm exact model availability and restricted persona launch configuration in the unsandboxed environment. +3. Select one clean source commit containing the owner-accepted inlining repair and evidence mechanism. +4. Recompute every input, oracle, model-facing file, and protocol hash into a machine-readable instrument manifest; verify S3/S4 prompt-string hashes independently. +5. Run the focused topology, packaging, app, trace, snapshot, construct-only, type, lint, and unit checks at that commit. +6. Record current prices and normal/worst-case currency estimates. +7. Obtain explicit owner acceptance of the exact freeze manifest and paid ceiling. +8. Commit the freeze alone. Only then admit `m4-pol-v1-vestera-p1`. + +Any file or model-setting change after freeze creates a new protocol version; do not patch v1 in place after observing behavior. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/instrument-manifest.json b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/instrument-manifest.json new file mode 100644 index 00000000000..cf7859275fe --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/instrument-manifest.json @@ -0,0 +1,191 @@ +{ + "version": 2, + "campaign": "mission-4-proof-of-life-v2", + "instrumentCommit": "95954b494308fbba384cc4ce169a813916f164f9", + "models": { + "elicitor": { + "provider": "anthropic", + "model": "claude-sonnet-4-6" + }, + "persona": { + "provider": "openai", + "model": "gpt-5.6-sol", + "thinking": "medium" + }, + "adjudicator": { + "provider": "anthropic", + "model": "claude-opus-4-6", + "thinking": "high" + } + }, + "host": "none", + "logicalCeiling": { + "conversationAttempts": 10, + "brunchSubmissions": 32, + "personaContinuations": 28, + "adjudications": 10 + }, + "spendGate": { + "currencyGate": "suspended-by-owner", + "authority": "libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md", + "knownRoundedV1PersonaAndAdjudicatorUsd": 0.636, + "v1SonnetUsd": null, + "usageReporting": "required" + }, + "controlledPromptSha256": { + "S3": "ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729", + "S4": "64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635" + }, + "probeObjectiveSha256": "27396ce3e6e5ed36aa21adbb00d93129af179535c3fb34accca459733dddaa13", + "verification": { + "command": "yarn exec turbo run build lint:tsc lint:eslint test:unit --filter '@hashintel/brunch-agent' --filter '@hashintel/brunch-agent-binding-flue' --filter '@hashintel/brunch-agent-transport-aisdk' --filter '@hashintel/brunch-agent-plugin-sdcpn' --filter '@hashintel/brunch-agent-plugin-gherkin' --filter '@hashintel/brunch-agent-plugin-dafny' --filter '@apps/brunch-agent'", + "tasks": { + "successful": 30, + "total": 30 + }, + "mission3EvidenceUnchangedFrom": "4c11c7a6c4e1df26c9d76cec30e32af8f013042d", + "universalGuidanceMatchesSourceAt": "ca57b45729260cc657f89b718fc505997a4e1b3c" + }, + "files": [ + { + "path": "apps/brunch-agent/.pi/extensions/brunch-persona-testing.ts", + "sha256": "f20f922e4d85ca7abd199dfc276d174f04b4c413d2b1f515a4ee6135a30272a6" + }, + { + "path": "apps/brunch-agent/.pi/extensions/brunch-persona-testing/SYSTEM.md", + "sha256": "1a21e60de561161ef3d26ff72de42c2f7abbb1b05f5236f60ac03cfef06ace51" + }, + { + "path": "apps/brunch-agent/src/agents/chat-agent/agent.ts", + "sha256": "e87ebbd611dd87f897c0ab15c704e6604bc032f99a96ee1a3bb827c03344300e" + }, + { + "path": "apps/brunch-agent/src/agents/chat-agent/tools/ping.ts", + "sha256": "a6f50f65f1bb4f1b62a2bf5fa23b343b973dd007be720a33dfbf5cd4a0595744" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/brunch-turn.ts", + "sha256": "669e1c40acde9dd903034725d850d8f99cb78376efa9d8a8879df7f2c0549e02" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/client-tool-hosts.ts", + "sha256": "f7757866b58592c0933e091832b21337eb26225f9b4517180a68d07668839ed3" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/proof-artifacts.ts", + "sha256": "4d3c4878b6941636343df69acfca95c9cd2ba145eaaa5763b00fa09198fb347e" + }, + { + "path": "apps/brunch-agent/src/evaluations/persona/refresh-proof-manifest.ts", + "sha256": "eb43c845159c902c3c9b7d89de034151d76ea214fc8e9ea37b95fa8800dc4374" + }, + { + "path": "libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-preflight-2026-09-03.md", + "sha256": "4a0d33d4c6314fa4eb1080b046daed941ffd6e505e3a13964faff057733bfe88" + }, + { + "path": "libs/@hashintel/brunch-agent/docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md", + "sha256": "6e7f5dd81b76a174c331d8543345b24d15788dc91b54e77bc5c9e08039d081db" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/opening-message.md", + "sha256": "bf46a637d029f34758c398f3d1f5ac70c2d10bfd169b05b23497215792a16ef8" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/data-centre-thermal-operations/situation-pack.md", + "sha256": "ab5f7f701265e4a01eea181c27327891c208b82ce75235cec0344f90fd2fe5ce" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/flue-skill-composition-side-quest-v3/scenarios.json", + "sha256": "1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/opening-message.md", + "sha256": "75ac100dc0f16771feced90563f4c57a5c9e918bbd2cc6fd7af0cd5de7165659" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/industrial-gas-vmi/situation-pack.md", + "sha256": "07f36060ebbb1cccd2c20ccd2a44ae732b23d69b82e2b018c4b05e6844d6d55e" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md", + "sha256": "84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md", + "sha256": "4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/oracles/mission-4-activation-and-restraint-ruler-v2.md", + "sha256": "08a679e7b4f596653df6d8f4b31ee5aa05b095f4c49322fb0c8b0a8a8a725309" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/persona-probe-objective.md", + "sha256": "27396ce3e6e5ed36aa21adbb00d93129af179535c3fb34accca459733dddaa13" + }, + { + "path": "libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/protocol.md", + "sha256": "ef81a6e30b2b69f7ac2888b49d51e42c5bb8bab86fc1031affca914382d0c8ee" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/prompts/SYSTEM.md", + "sha256": "82b035268c07cc8ee4736b5ffdd392c8f153793d2ca6b9ed57271c9b6146de10" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/SKILL.md", + "sha256": "68b7fa27c2ba8401a97272e63c17d0ad6c6fdb9b3c81d9aa02e7ec3120e0aacc" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/skill.ts", + "sha256": "5ab4a1cd714b6b819e51864d48ec2fe655fc6a335a73eb51c7126ebdd9c631f2" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts", + "sha256": "f1ceb78f5e503032fa324f62cab0f44021bc2ebf6c46e098d56ea0cd492175ed" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts", + "sha256": "5feb06b4571f36e7a1998c0fff431bb6d6dfe6acfe62e95252ff32d0e5cabda6" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/prompts/APPEND_SYSTEM.md", + "sha256": "8826b85d32d6c24fbc8f678ec394600676ec61c4a0b83d0a9cfa16be87fc5d76" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts", + "sha256": "8ab4c314d9824d521f5d375c71353c42011be943091e05732f9b55f305133af5" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md", + "sha256": "ff0d9351bf6f130188c325d0fd158bd5b874b3eb18d3a4f195e8487dc811dde9" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/checks.md", + "sha256": "43dbc9adc9168ae9984321895fca441386ee3c44ab8691ba92baec0f7c43f400" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/pn-construction.md", + "sha256": "57edbdfebd733ed6c1d5eb02f81dc13e6d5b9c7357f00018c1d0f2b1b1a3f694" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/profile.md", + "sha256": "cf37161ee79cace2d96ee6d473e9751cab65cc050ebae79d94530a01705b2b8e" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts", + "sha256": "201fbf3cb4655f9eaee23e07dc289e58f348e967df455195fdd35d4757371b73" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md", + "sha256": "6c26ed3808ccbb7133ea7c370779e63885dfdc342c6594f5cb3730b467e2b1da" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts", + "sha256": "7a4a03b62267531ba65b0f27128d804ad7fc70acf333266910010cc999f19d40" + }, + { + "path": "libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts", + "sha256": "9e020d8bee5e6c9902b5e5b609abc5930d3e27647d0f4d0d4679f3e31097dfbb" + } + ] +} diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/persona-probe-objective.md b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/persona-probe-objective.md new file mode 100644 index 00000000000..2d2c673297f --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/persona-probe-objective.md @@ -0,0 +1,3 @@ +# Interactive probe objective + +Act only as the person in the supplied situation pack. Send the exact opening message first, then answer Brunch naturally from the pack. Make exactly three visible user submissions, counting the opening as the first, unless `brunch_turn` reports a genuine orchestration error. After each of the first two Brunch replies, answer its direct question naturally from the pack. Stop after the third submission settles. The turn count alone owns the normal stop. Do not seek a workpiece or construction. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/protocol.md new file mode 100644 index 00000000000..2c3180a1351 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/mission-4-proof-of-life-v2/protocol.md @@ -0,0 +1,132 @@ +# Mission 4 proof-of-life protocol v2 + +Status: **freeze candidate authorized for preparation by the owner on 2026-09-03; not frozen or authorized for paid execution until the owner accepts the exact v2 ruler and machine-readable manifest.** V1 remains immutable evidence of an instrument failure: both Vestera attempts stopped after one Orientation question because the persona was asked to apply an evaluator-owned semantic stop category that its isolated context did not define. V2 changes only probe control, the ruler language that describes probe extent, and fresh identities; production elicitor text, cases, models, semantic classifications and thresholds, other slot behavior, replacement rules, and evidence mechanisms remain unchanged. + +## Claim + +This protocol can support only the bounded claim in [`../../../MISSION.md`](../../../MISSION.md): the implemented independent `elicitation` capability activates with its SDCPN job skill before substantive interviewing across three named case families, conditionally reads the SDCPN profile before reliance, avoids an opening Battery, refrains on the exact S3 resolvable-review input, activates on the exact S4 knowledge-gap input, and yields one attributable but unpromoted downstream workpiece candidate. + +It does not estimate general reliability, grade workpiece quality, accept the topology-neutral portfolio, compare against Mission 3, prove Petrinaut `/api/chat` or browser behavior, or promote any artifact to a fixture or database seed. + +## Candidate oracle + +Grade only with [`../../oracles/mission-4-activation-and-restraint-ruler-v2.md`](../../oracles/mission-4-activation-and-restraint-ruler-v2.md) after the owner accepts its exact frozen hash. It preserves v1's semantic classifications and thresholds while describing fixed three-submission probes graded at their first Substantive text. The ruler is evaluator-only and never enters Brunch or persona context. + +## Model and host allocation + +| Role | Requested configuration | Pre-freeze requirement | +| --- | --- | --- | +| Elicitor | `BRUNCH_CHAT_MODEL=claude-sonnet-4-6`, resolving through the production Anthropic provider | Record the provider-reported exact model id and verify the built app uses it. No fallback. | +| Persona | Pi `--model openai/gpt-5.6-sol --thinking medium`, using the direct OpenAI provider | Record requested and provider-reported ids. No fallback or router substitution. | +| Adjudicator | `anthropic/claude-opus-4-6`, high thinking, one fresh context per technically usable attempt | Record requested/provider-reported ids. No fallback. | +| Client-tool host | `none` for every slot | Any client-tool suspension is technical invalidity; do not service it with a different host. | + +OpenAI elicitor comparison is outside Mission 4 and must not be inserted into this campaign. Results are reported per elicitor model and never pooled. + +## Fixed slot allocation and order + +Run serially in this order, validating and adjudicating each retained attempt before admitting the next slot: + +1. `vestera-probe`: Vestera Scheduling, fixed three-submission probe. +2. `data-centre-probe`: Data Centre Thermal Operations, fixed three-submission probe. +3. `s3-review`: exact controlled S3 resolvable-review prompt, one visible user turn through settlement. +4. `s4-knowledge-gap`: exact controlled S4 knowledge-gap prompt, one visible user turn through settlement. +5. `industrial-gas-full`: Industrial Gas VMI, six-turn minimum and eight-turn maximum, producing the only possible handoff candidate. + +A valid behavioral failure stops the campaign for owner adjudication. Do not run later slots to average it away. + +### Interactive inputs + +The first visible user message is exactly the text below the `---` separator in the named `opening-message.md`; headers and private instructions are not sent to Brunch. + +| Slot | Opening path | Current SHA-256 | Private situation pack | Current SHA-256 | +| --- | --- | --- | --- | --- | +| `vestera-probe` | `../../cases/vestera-scheduling/opening-message.md` | `84ec5faa5fd46699c008b3b2aad49eb9988b8c2ab039c8e147fdb077d562ef54` | `../../cases/vestera-scheduling/situation-pack.md` | `4dbeb44a881c4675ec0ce7a5f068ea46ce1a4968a405b2dd692f92816d33e083` | +| `data-centre-probe` | `../../cases/data-centre-thermal-operations/opening-message.md` | `bf46a637d029f34758c398f3d1f5ac70c2d10bfd169b05b23497215792a16ef8` | `../../cases/data-centre-thermal-operations/situation-pack.md` | `ab5f7f701265e4a01eea181c27327891c208b82ce75235cec0344f90fd2fe5ce` | +| `industrial-gas-full` | `../../cases/industrial-gas-vmi/opening-message.md` | `75ac100dc0f16771feced90563f4c57a5c9e918bbd2cc6fd7af0cd5de7165659` | `../../cases/industrial-gas-vmi/situation-pack.md` | `07f36060ebbb1cccd2c20ccd2a44ae732b23d69b82e2b018c4b05e6844d6d55e` | + +The machine-readable freeze manifest recomputes and binds these identities. + +Probe private objective: + +Use [`persona-probe-objective.md`](persona-probe-objective.md) verbatim. It gives the persona a mechanically observable three-submission stop and no evaluator-owned semantic category. The fresh adjudicator locates the first Substantive text after settlement. Later retained turns cannot alter ordering before that text and are outside the activation-before-substance decision. + +Industrial Gas private objective: + +> Establish enough to represent the Alder outage-response decision and compare its immediate replenishment and dispatch options while preserving unresolved parameters. Seek a recoverable operational workpiece, not net construction. Send the exact opening message first and answer only from the supplied situation pack. Continue for at least six visible user turns. From turn six onward, stop when Brunch supplies a recoverable `runbook-ir` workpiece without leaving a direct question unanswered. Stop after eight visible user turns regardless. + +### Controlled review inputs + +Use the exact S3 and S4 `prompt` strings in [`../../cases/flue-skill-composition-side-quest-v3/scenarios.json`](../../cases/flue-skill-composition-side-quest-v3/scenarios.json), file SHA-256 `1844df940b8de9d10d28e9537f966920aba581b36956a9ac26a3767824ca96cb`. S3 prompt-string SHA-256 is `ff5755c7ffced45741f791d1ec433386ee4052d301d0065dbff46dc8c36de729`; S4 prompt-string SHA-256 is `64db8fd28e3b62b244ded0de703cc1609bcda9cb2387287efea4887764720635`. The freeze manifest binds all three. These fixed, explicitly cued inputs are controlled mechanism checks only. + +## Attempt identities and replacement rule + +Reserve these ids; never reuse an admitted id: + +| Slot | Primary | Sole permitted replacement | +| --- | --- | --- | +| Vestera probe | `m4-pol-v2-vestera-p1` | `m4-pol-v2-vestera-r1` | +| Data Centre probe | `m4-pol-v2-data-centre-p1` | `m4-pol-v2-data-centre-r1` | +| S3 review | `m4-pol-v2-s3-p1` | `m4-pol-v2-s3-r1` | +| S4 knowledge gap | `m4-pol-v2-s4-p1` | `m4-pol-v2-s4-r1` | +| Industrial Gas full | `m4-pol-v2-industrial-gas-p1` | `m4-pol-v2-industrial-gas-r1` | + +Retain every admitted attempt. Permit the replacement only when the primary is technically invalid under the ruler or, for an interactive slot, technically valid but reaches no Substantive text within budget. A replacement repeats the same frozen inputs and settings under its reserved fresh id. A second invalid or no-Substantive result stops the campaign. Never replace a valid behavioral failure or a full run that reaches substance but fails to emit a recoverable workpiece. + +## Logical ceiling and usage reporting + +The hard logical ceiling is 10 conversation attempts, 32 visible user submissions to Brunch, 28 persona continuations, and 10 fresh adjudications. Internal Sonnet provider continuations caused by skill/resource calls are metered and reported but are not falsely equated with visible submissions. Normal success is five conversation attempts, approximately 14 Brunch submissions, approximately 12 persona continuations, and five adjudications. + +The non-billable [v2 model and cost preflight](../../../docs/evidence/decisions/mission-4-proof-of-life-v2-preflight-2026-09-03.md) records the unchanged $3.16 normal and $7.65 worst-case planning estimates, $0.636 known rounded v1 persona/adjudicator spend, and the unavailable v1 Sonnet usage. The owner subsequently [suspended currency gating](../../../docs/evidence/decisions/mission-4-proof-of-life-v2-budget-suspension-2026-09-03.md); v2 has no active USD stop threshold. Retain and report every available usage value. Before the first v2 model call, obtain explicit owner acceptance of the frozen v2 instrument. Exceeding any logical ceiling stops execution. + +## Required mechanism before freeze + +No run may begin until focused tests prove all of the following against canonical Flue `history()`: + +1. A raw settled snapshot writer retains the exact JSON used for grading. +2. A deterministic trace derives visible user turn indices, ordered skill activations and outcomes, conditional resource reads and outcomes, other tool/executor events, and workpiece-bearing text events. +3. Canonical ordering distinguishes a profile/template read before text from one after text in the same turn. +4. Workpiece recovery records source message id and binds it to the raw snapshot. +5. Construct-only results expose activated skill names so ruler item 4c is decidable. +6. A protocol-owned `run.json` records source/frozen commit, slot, attempt, models, reasoning settings, and host; `validity.json` records validity and stop reason; a refreshable manifest hashes both records and every other retained artifact. +7. A focused regression test reads the exact probe objective, requires the fixed three-submission and turn-count stop language, and rejects evaluator-owned semantic classification terms. + +Mechanism code and the persona may not classify semantic turns or decide pass/fail. The independent adjudicator applies the ruler to the trace and visible text after settlement. + +## Context isolation + +- Brunch receives only visible user messages and its production-mounted prompt, skills, resources, and tools. +- Interactive personas receive only the persona system policy, their situation pack, private objective, turn budget, and Brunch text returned by `brunch_turn`. Probe personas receive a mechanical submission-count stop and never decide evaluator categories. They receive no ruler, oracle, target answer, repository tools, or evaluation-side tool details. +- S3/S4 are sent directly as fixed user inputs and use no persona model. +- Each adjudicator context receives the frozen v2 ruler, one raw snapshot, its derived trace, formatted transcript, slot/attempt identity, and mechanical validity record. It receives no private situation pack or case oracle. It must quote the text supporting every semantic classification or finding. +- The owner sees all retained attempts and adjudications when deciding the bounded claim. + +## Per-attempt retention + +Write each admitted attempt under `docs/evidence/evaluations/mission-4-proof-of-life-v2/runs/<attempt-id>/`: + +- `run.json` — protocol-owned attempt identity, source/frozen commit, exact requested/reported models, reasoning settings, host, budgets, and launch time, written before admission; +- `snapshot.json` — canonical settled `history()` snapshot; +- `transcript.md` — formatted projection of that snapshot; +- `trace.json` and `trace.md` — mechanically equivalent ordered events; +- `validity.json` — mechanical validity and stop reason; +- `adjudication.md` — fresh-context quoted ruler application when technically usable; +- `workpiece.md` — only when mechanically recovered from a `runbook-ir` block; +- `manifest.json` — SHA-256 for every sibling artifact; refresh it after adding or changing validity/adjudication records with `yarn workspace @apps/brunch-agent proof:manifest -- <attempt-directory>`. + +Campaign root files must include the frozen protocol/instrument manifest, attempt ledger, spend/usage ledger, and final adjudication. Invalid and non-qualifying attempts remain visible in the ledger and are never included in the `3/3` numerator or denominator. + +The Industrial Gas workpiece, if recovered, is labelled `evaluation-run` and `handoff-candidate`. Its manifest must say that it is not an accepted workpiece, reusable fixture, database seed, product conversation, Petrinaut witness, or quality result. + +## Freeze sequence + +1. Land and verify the evidence mechanism without changing model-facing production text. +2. Confirm exact model availability and restricted persona launch configuration in the unsandboxed environment. +3. Select one clean source commit containing the owner-accepted inlining repair and evidence mechanism. +4. Recompute every input, oracle, model-facing file, and protocol hash into a machine-readable instrument manifest; verify S3/S4 prompt-string hashes independently. +5. Run the focused topology, packaging, app, trace, snapshot, construct-only, type, lint, and unit checks at that commit. +6. Record current prices and normal/worst-case currency estimates. +7. Obtain explicit owner acceptance of the exact freeze manifest; record any active currency gate or its suspension separately. +8. Commit the freeze alone. Only then admit `m4-pol-v2-vestera-p1`. + +Any file or model-setting change after freeze creates a new protocol version; do not patch v2 in place after observing behavior. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v1/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v1/ARCHIVE.md new file mode 100644 index 00000000000..0ed459fa64f --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v1/ARCHIVE.md @@ -0,0 +1,7 @@ +# Archived executed control + +The Mission 3 prospective baseline is immutable and must not be rerun or overwritten. Its live runner, package command, and runner test were retired after the campaign evidence was retained. + +The exact instrument and runner are reconstructible from the source revision and embedded manifests under `docs/evidence/evaluations/vestera-prospective-baseline-v1/`. The frozen `protocol.md` remains unchanged because it is part of that evidence. + +The v4 and v5 successors were discarded by the owner on 2026-09-02; no live successor protocol exists. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/ARCHIVE.md new file mode 100644 index 00000000000..bf8b3e092f2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/ARCHIVE.md @@ -0,0 +1,7 @@ +# Archived protocol + +The v2 campaign was aborted and must not be rerun. Its live runner, package command, and runner test were deleted after the immutable failure artifacts were retained. + +The exact executed runner and instrument are reconstructible from source commit `605e681cebfaeaa3fcdd0502f50ab28adc7ac63d` and the embedded manifest in `docs/evidence/evaluations/vestera-prospective-candidate-v2/`. The frozen `protocol.md` is retained unchanged because its hash is part of that evidence. + +The v4 and v5 successors were discarded by the owner on 2026-09-02; no live successor protocol exists. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/protocol.md new file mode 100644 index 00000000000..6d2b50297ed --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v2/protocol.md @@ -0,0 +1,107 @@ +# Prospective runbook candidate v2 + +Status: **aborted after two immutable invalid members** + +Protocol id: `prospective-runbook-v2` +Observed-output namespace: `vestera-prospective-candidate-v2` + +This protocol drives the built production `ChatAgent` against the same Vestera case and frozen ruler as the immutable Mission 3 control. It creates three independent candidate members for comparison with that control. It does not alter, replace, or add members to `vestera-prospective-baseline-v1`, and it does not authorize paid calls or grading. + +Execution stopped after replication 1 inherited a stale credential and replication 2 encountered a simulated-expert refusal before workpiece delivery. The owner subsequently narrowed the Mission 4 question to workpiece-quality scoring against the latest valid flat-prompt controls. Preserve the v2 artifacts as operational evidence; do not run replication 3 or use v2 as the quality campaign. The replacement is [`prospective-runbook-v3`](../prospective-runbook-v3/protocol.md). + +## Frozen campaign configuration + +| Setting | Value | +| --- | --- | +| Case | `vestera-scheduling` | +| Opening message | `../../cases/vestera-scheduling/opening-message.md` | +| Expert pack | `../../cases/vestera-scheduling/situation-pack.md` | +| Prospective ledger | `../../oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml` | +| Quality ruler | `../../oracles/ir-quality-ruler-v1.md` | +| Interviewer | built production `ChatAgent`, requested model `claude-sonnet-4-5` | +| Simulated expert | requested model `claude-sonnet-4-5` | +| Interview turns | 8 before the final workpiece request | +| Per-logical-turn latency stop | 180,000 ms | +| Replications | 3 independent conversations, numbered 1–3 | +| Sampling | provider default; no seed | +| Omniscient grader | frozen `../ir-quality-ruler-v1/omniscient-grader.md`, fresh context per run | +| Cold reviewer | frozen `../ir-quality-ruler-v1/cold-ir-reviewer.md`, separate fresh context per run | +| Observed output | `../../../docs/evidence/evaluations/vestera-prospective-candidate-v2/` | + +Changing any frozen value requires a new protocol and output namespace. The 180-second stop is retained from v1 because no accepted evidence requires changing it. + +## Instrument freeze and paid-run preconditions + +The runner records SHA-256 hashes for the exact promoted core prompt and universal resource; the plugin append, Flue mount, assembled skill definition, and every assembled skill resource; the application and campaign runner paths; the Vestera inputs and prospective ledger; the frozen ruler and grader prompts; this protocol; and the root `yarn.lock`. It also records the source commit and a deterministically path-sorted manifest containing the path and SHA-256 hash of every built server `apps/brunch-agent/dist/*.mjs` artifact. The campaign fingerprint covers the complete source/lock hash map, complete built-artifact manifest, requested models, and stop configuration. + +Before any paid invocation, the owner must separately approve the paid-call and cost ceiling and confirm: + +1. Every scoped instrument file is committed and clean. +2. The app was built from that clean scope. +3. `ANTHROPIC_API_KEY` is set and no hermetic model module or dirty-instrument override is set. +4. The output directory resolves, after lexical normalization and symlink resolution, to exactly `vestera-prospective-candidate-v2`. +5. Models, turn stop, and latency stop exactly match the table above. +6. The requested replication number is 1, 2, or 3 and has no prior artifact. + +Both the relocated v1 runner and this runner canonicalize output paths through the nearest existing real path. They categorically reject the immutable `vestera-prospective-baseline-v1` directory and every descendant, including `/.`, symlink, nonexistent-descendant, and hermetic-override aliases. The relocated v1 runner can no longer add a baseline member under any configuration. + +The first observed candidate member fixes the campaign fingerprint. Later members must match its exact source/lock hashes, complete built artifact manifest, requested models, and stop configuration. A runtime or integrity failure still consumes its replication: retain it as an invalid member rather than replacing it. + +## Paid commands + +Do not run these commands until the owner authorizes the paid budget. Once authorized, run them sequentially from the repository root: + +```sh +BRUNCH_RUNBOOK_REPLICATION=1 yarn workspace @apps/brunch-agent runbook:elicit:candidate-v2 +BRUNCH_RUNBOOK_REPLICATION=2 yarn workspace @apps/brunch-agent runbook:elicit:candidate-v2 +BRUNCH_RUNBOOK_REPLICATION=3 yarn workspace @apps/brunch-agent runbook:elicit:candidate-v2 +``` + +The package script builds the application before each invocation. Each command uses a fresh Flue conversation identity and temporary database and writes a unique immutable artifact stem. + +## Stop, validity, and evidence semantics + +- The opening dispatch counts as interview turn 1. +- The runner alternates the production interviewer and simulated expert until eight interviewer turns have settled, unless a logical interviewer turn exceeds 180 seconds or yields no visible text. +- It then sends a labelled evaluation stop instruction that is not expert evidence. The instruction requests only the current `runbook-ir` workpiece and forbids another question, construction, and construction-resource reads. +- A member is `completed` only when it contains a recoverable workpiece and the ordinary path has no declared violation. +- Reading `pn-construction.md` or `checks.md`, using a construction or capture tool, using any other tool outside `activate_skill` and `read_skill_resource`, reading any resource outside the three declared elicitation/workpiece resources, or omitting the workpiece makes the member `invalid`. The artifact is retained and the runner exits nonzero. +- A simulated-expert, interviewer, application, artifact-write, or other runtime failure writes an `invalid` record with `invalidReason: runtime-failure` and exits nonzero. +- A cleanup error writes a separate `invalid` record with `invalidReason: cleanup-failure`, is printed to stderr, and forces a nonzero exit. Its presence invalidates the member even if a completed record was written first. +- The runner does not interpret model self-report as completion. + +## Artifacts and immutability + +The candidate namespace is: + +```text +docs/evidence/evaluations/vestera-prospective-candidate-v2/ +``` + +A completed or ordinary-path-invalid run writes: + +- `<run-id>.json` — the raw run record, exact raw Flue `history()` snapshot, snapshot hash, readable transcript, expert exchange, call metadata, usage, resource/tool traces, violations, configuration, and exact instrument manifest; +- `<run-id>.md` — readable transcript and run metadata; +- `<run-id>.ir.md` — recovered workpiece, when one was emitted. + +The JSON record binds the selected workpiece to its SHA-256 hash, source Flue message id, and source-message SHA-256 hash. It retains the exact raw snapshot object and the SHA-256 hash of its compact JSON serialization; the readable transcript is a projection, not the source record. + +Every expert call records the requested model, provider-reported response model when present, an explicit `unavailable` source when absent, and the provider stop reason when present. Every interviewer call records requested model, provider id/name/API metadata, provider-reported response model when present, and normalized/provider stop reasons. Requested identity is never reported as observed identity without provider evidence. + +A runtime failure writes `<run-id>.failure-<nonce>.json`; cleanup failures write `<run-id>.cleanup-failure-<nonce>.json`. Failure records use paths distinct from `<run-id>.json`, so a prior successful write or collision cannot mask the originating error through a second `wx` attempt. Retention failures are reported alongside the original failure. All observed-member writes use create-new semantics. Never edit, overwrite, delete, or replace an observed member. + +## Hermetic verification + +Hermetic execution requires both model overrides, admits only the canonical real paths of the checked-in `runbook-elicitation-faux-expert.ts` and `runbook-elicitation-faux-provider.ts` fixtures, rejects any `ANTHROPIC_API_KEY`, rejects the candidate and immutable-control namespaces, and permits the dirty-instrument escape hatch only for this free path. Arbitrary executable override modules are not accepted. + +Focused tests prove exact, `/.`, descendant, and symlink immutability guards; approved-module and API-key gates; full built-server and root-lock fingerprinting; stable manifests and fingerprints across two identical hermetic runs; raw-snapshot and workpiece/source-message hashing; requested-versus-observed model metadata; adversarial construction-resource, unexpected-tool, capture/construction classification, and missing-workpiece invalidation; distinct artifact-write failure retention; and visible retained cleanup failures. No paid or network model call occurs. + +## Grading after campaign execution + +Do not add graders or grade anything while preparing this protocol. After all three paid members exist, grade each valid workpiece exactly as v1: + +1. Give a fresh omniscient context exactly the frozen omniscient prompt, situation pack, prospective ledger, exact source conversation, and recovered workpiece. +2. Give a separate fresh cold context exactly the frozen cold-review prompt, opening message, and recovered workpiece. +3. Record exact requested and observed grader provider/model identity and retain both reports beside the run. +4. Human-review hard failures, genuine disagreement, and every `NEW-*` mistake. +5. Adjudicate score vectors, gate rates, mistake counts, cost/turn/token/latency observations, and failures against Mission 3's observed range. Do not collapse either campaign to a mean. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/ARCHIVE.md b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/ARCHIVE.md new file mode 100644 index 00000000000..99f30136ff9 --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/ARCHIVE.md @@ -0,0 +1,7 @@ +# Archived invalid protocol execution + +The v3 campaign executed but produced no Mission 4-valid member: its only completed workpiece was created before the required template read, and both cold reviews were incomplete. Its live runner, package command, grader, and runner test were deleted after the immutable artifacts were retained. + +The exact scored runner and instrument are reconstructible from source commit `794fe2fbf1eaeba3fc816c6e3d1755d7b444125d` and the embedded manifest in `docs/evidence/evaluations/vestera-architecture-candidate-v3/`. The frozen `protocol.md` is retained unchanged because its hash is part of that evidence. See that directory's `campaign-adjudication.md` for the invalidation and arithmetic errata. + +The v4 and v5 successors were discarded by the owner on 2026-09-02; no live successor protocol exists. diff --git a/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md new file mode 100644 index 00000000000..74d96ae098d --- /dev/null +++ b/libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v3/protocol.md @@ -0,0 +1,87 @@ +# Mission 4 architecture scoring v3 + +Status: **frozen scoring protocol; not executed** + +Protocol id: `prospective-runbook-v3` +Observed-output namespace: `vestera-architecture-candidate-v3` + +This protocol scores the owner-selected Mission 4 prompt, skill, progressive-disclosure, and workpiece architecture against only the latest valid flat-prompt control workpieces. It does not aggregate historical side quests, draft families, or every previous runbook design. The aborted v2 campaign remains operational evidence and is not a quality baseline. + +## Comparison target + +The immutable quality control is exactly these two valid members of `vestera-prospective-baseline-v1`: + +- `runbook-elicitation-2026-08-31T10-50-28-709Z-20a4817f` +- `runbook-elicitation-2026-08-31T10-56-34-754Z-4b75737c` + +Their frozen omniscient range is `66.3–80.0 / 100`; their cold-utility range is `3.3–3.5 / 4`; both have conditional downstream readiness and no hard-failure gate. Their workpieces, grade reports, and campaign adjudication are hashed into the v3 instrument. + +Quality scoring is conditional on a recoverable, valid workpiece on both sides. Runtime validity, simulator refusal, cost, and latency are reported separately and may not be converted into workpiece-quality points or used to alter either quality population. + +## Frozen campaign configuration + +| Setting | Value | +| --- | --- | +| Case | `vestera-scheduling` | +| Opening message | `../../cases/vestera-scheduling/opening-message.md` | +| Expert pack | `../../cases/vestera-scheduling/situation-pack.md` | +| Prospective ledger | `../../oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml` | +| Quality ruler | `../../oracles/ir-quality-ruler-v1.md` | +| Interviewer | built production `ChatAgent`, requested model `claude-sonnet-4-5` | +| Simulated expert | requested model `claude-sonnet-4-5` | +| Interview turns | 8 before the final workpiece request | +| Per-logical-turn latency stop | 180,000 ms | +| Replications | 3 independent conversations, numbered 1–3 | +| Sampling | provider default; no seed | +| Omniscient grader | frozen `../ir-quality-ruler-v1/omniscient-grader.md`, fresh context per valid run | +| Cold reviewer | frozen `../ir-quality-ruler-v1/cold-ir-reviewer.md`, separate fresh context per valid run | +| Observed output | `../../../docs/evidence/evaluations/vestera-architecture-candidate-v3/` | + +This mirrors the flat-prompt control's three-invocation shape and seeks a two-workpiece quality population. It does not replace an invalid replication to reach that population. Changing a frozen value requires a new protocol and namespace. + +## Preconditions and freeze + +The owner-approved Mission 4 budget ceiling is US$10 across candidate and grader calls. Before creating a replication, the runner applies every free path, configuration, clean-instrument, fingerprint, and namespace guard, then makes a one-token credential/model-availability preflight outside campaign membership. A failed preflight creates no member. Successful preflight cost still counts against the owner ceiling. + +The runner hashes the complete candidate source and lock scope, complete built-server `dist/*.mjs` manifest, comparison-target artifacts, case, ledger, ruler, grader prompts, and this protocol. The first member fixes the fingerprint. Later members must match it exactly. + +The runner categorically rejects the immutable flat-prompt namespace and descendants after real-path canonicalization. Hermetic overrides remain restricted to the checked-in faux fixtures and cannot receive an API key. + +## Paid commands + +After a clean post-commit hermetic proof, run sequentially: + +```sh +BRUNCH_RUNBOOK_REPLICATION=1 yarn workspace @apps/brunch-agent runbook:elicit:architecture-v3 +BRUNCH_RUNBOOK_REPLICATION=2 yarn workspace @apps/brunch-agent runbook:elicit:architecture-v3 +BRUNCH_RUNBOOK_REPLICATION=3 yarn workspace @apps/brunch-agent runbook:elicit:architecture-v3 +``` + +Each command builds the application, preflights the provider, uses a fresh Flue conversation and temporary database, and writes an immutable artifact stem. + +## Validity and evidence semantics + +- The opening dispatch counts as interview turn 1. +- After eight interviewer turns or an earlier latency/empty-text stop, a labelled non-evidence stop instruction requests the current `runbook-ir` workpiece. +- A valid quality member must contain a recoverable workpiece and no ordinary-path violation. +- Construction/capture tool use, construction-resource reads, undeclared tools/resources, or a missing workpiece make the candidate member invalid. +- Provider, simulator, interviewer, application, persistence, and cleanup failures remain immutable operational evidence. +- Human adjudication attributes failures by observed boundary. A simulated-expert refusal is not silently charged to candidate workpiece quality; a candidate failure is not silently relabelled as simulator failure. +- No invalid member is deleted, replaced, or graded as a workpiece. + +## Artifacts + +Completed and invalid runs retain the exact raw Flue snapshot and hash, readable transcript, selected workpiece and source-message binding when available, expert exchange, requested/observed model and stop metadata, costs, resource/tool traces, violations, configuration, comparison target, and complete instrument manifest. Runtime and cleanup failures use distinct nonce-bearing records so retention cannot mask the originating failure. + +The v2 artifacts remain in `vestera-prospective-candidate-v2/` with an abort adjudication. They are neither moved into v3 nor used as flat-prompt controls. + +## Grading and adjudication + +For each valid v3 workpiece: + +1. Give a fresh omniscient context exactly the frozen omniscient prompt, situation pack, prospective ledger, exact source conversation, and workpiece. +2. Give a separate fresh cold context exactly the frozen cold prompt, opening message, and workpiece. +3. Retain exact requested and observed grader identity and both reports. +4. Human-review hard failures, genuine disagreement, every `NEW-*` mistake, and failure attribution. +5. Compare candidate score vectors, mistake classes, cold utility, and readiness only with the two named flat-prompt controls. Report ranges and individual members; do not collapse either side to a mean. +6. Report completion, simulator/provider failures, cost, token use, and latency in a separate operational section. diff --git a/libs/@hashintel/brunch-agent/packages/binding-flue/package.json b/libs/@hashintel/brunch-agent/packages/binding-flue/package.json index d7a4463edbb..53c9b49b507 100644 --- a/libs/@hashintel/brunch-agent/packages/binding-flue/package.json +++ b/libs/@hashintel/brunch-agent/packages/binding-flue/package.json @@ -2,7 +2,7 @@ "name": "@hashintel/brunch-agent-binding-flue", "version": "0.0.0-private", "private": true, - "description": "The Flue binding: implements the substrate-capability list and owns the storage-port implementation.", + "description": "Active Flue history, reply-projection, and local capture-store adapters; generalized typed elicitation remains suspended.", "license": "AGPL-3.0", "type": "module", "exports": { @@ -21,8 +21,7 @@ "dependencies": { "@flue/runtime": "2.0.3", "@flue/sdk": "2.0.3", - "@hashintel/brunch-agent": "workspace:*", - "valibot": "1.4.2" + "@hashintel/brunch-agent": "workspace:*" }, "devDependencies": { "@types/node": "22.18.13", diff --git a/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts b/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts index 4885df836ec..51365e67f81 100644 --- a/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts +++ b/libs/@hashintel/brunch-agent/packages/binding-flue/src/index.ts @@ -1,76 +1,9 @@ /** - * `@hashintel/brunch-agent-binding-flue` — the Flue binding. - * - * One binding per substrate. It implements the substrate-capability list - * (spec §10), owns the local capture-store/session-log storage-port - * implementation (spec §9.6), and is the - * only shell allowed to know Flue's dialect: **the harness imports no - * substrate; a binding imports both** (spec §4). - * - * Every time mechanism wants to land in here, the second-binding test applies - * (spec §14.2): genuinely substrate-specific, or mechanism leaking into Flue's - * dialect? + * Active Flue adapters for conversation history, reply projection, and the + * local capture store. The generalized typed elicitation hook is retained + * under `suspended/` but deliberately absent from this public surface. */ -import { - useAgentFinish, - useAgentStart, - useDataWriter, - useDelivery, - usePersistentState, - useTool, -} from "@flue/runtime"; -import * as v from "valibot"; - -import { - ASK_TOOL_DESCRIPTION, - AskInput, - FreeTextAffordance, - SWEEP_RESULT_STATUSES, - advanceSweepHighWater, - askProtocolInstructionFragments, - buildCompletionCueSignal, - buildSettlementCheckSignal, - buildReplyBindingSignalPayload, - buildSweepExtractionPrompt, - buildSweepList, - buildSweepRepairSignal, - completionDemands, - computeUnaccountedAskAdvisories, - createSweepExtractionResultSchema, - createInitialSweepState, - decidePendingAffordance, - decideSettlementTrigger, - deriveCaptureStatus, - evaluateCompletion, - foldElicitedModel, - mintAskAffordance, - parseSweepState, - pendingSweepRepair, - renderInstructions, - reopenSweepAfterRefusal, - settlementProtocolInstructionFragments, - slotAssertionExtractionGuidance, - sweepableRange, - toolName, - type CaptureStore, - type CaptureStoreSnapshot, - type FreeTextAffordanceValue, - type Plugin, - type SweepState, -} from "@hashintel/brunch-agent"; -import { repertoire } from "@hashintel/brunch-agent/prompts"; - -import { capturedUserEntryIdsForSession } from "./capture-accounting"; -import { - projectFlueHistoryForSweep, - type FlueHistoryReader, -} from "./history-reader"; - -const SweepToolOutput = v.looseObject({ - status: v.picklist(SWEEP_RESULT_STATUSES), -}); - export { CAPABILITIES, type Capability, type Provision } from "./capabilities"; export { createFlueHistoryReader, @@ -83,232 +16,3 @@ export { type FlueReplyProjectorOptions, } from "./reply-projector"; export { createLocalCaptureStore } from "./local-capture-store"; - -export interface ElicitationSession { - readonly sessionId: string; - readonly captureStore: CaptureStore; - readonly historyReader: FlueHistoryReader; -} - -/** - * Mount the elicitation harness in a Flue agent. - * - * Flue has no ask-the-user primitive, so the harness owns the turn-suspension - * protocol: a `terminate: true` ask tool, the pending affordance in - * per-session state, and the answer arriving as a fresh dispatch (spec §7.4). - */ -export function useElicitation( - plugin: Plugin, - session: ElicitationSession, -): string { - const delivery = useDelivery(); - const [pending, setPending] = - usePersistentState<FreeTextAffordanceValue | null>( - "pendingAffordance", - null, - ); - const [storedSweepState, setSweepState] = usePersistentState<SweepState>( - "sweepHighWater", - createInitialSweepState(), - ); - let pendingAtFinish = pending; - let sweepState = parseSweepState(storedSweepState); - const extractionResult = createSweepExtractionResultSchema(plugin); - const { definition } = plugin; - // The fold and the completion cue know slot assertions; a definition whose - // proposals do not include them has a model the harness cannot yet fold. - const slotModel = - definition?.proposals.some((p) => p.type === "slot-asserted") === true - ? definition - : undefined; - const demands = - slotModel === undefined ? undefined : completionDemands(slotModel); - // Read-time derivation, never stored: fold the active captures, evaluate - // completion over the objective slices, and render the cue (ADR-0003, - // ADR-0006). Returned as a tool result so the model sees a harness fact - // without any state reaching the instructions. - const completionCue = (snapshot: CaptureStoreSnapshot) => { - if (slotModel === undefined || demands === undefined) return undefined; - const model = foldElicitedModel(snapshot, slotModel); - const report = evaluateCompletion(model, demands); - const sweepList = buildSweepList(model, report, slotModel.patterns); - return { - ...report, - unsatisfied: report.failures.length, - unmapped: model.unmapped, - cue: buildCompletionCueSignal(model, report, sweepList).body, - }; - }; - const writeAffordance = useDataWriter("affordance", { - schema: FreeTextAffordance, - }); - - useAgentStart((ctx) => { - if (delivery.kind !== "user" || pending === null) return; - - pendingAtFinish = null; - setPending(null); - ctx.append({ kind: "signal", ...buildReplyBindingSignalPayload(pending) }); - }); - - useTool({ - name: toolName("ask"), - description: ASK_TOOL_DESCRIPTION, - input: AskInput, - output: FreeTextAffordance, - run({ data, toolCallId }) { - const affordance = mintAskAffordance(data.question, toolCallId); - - setPending((current) => { - const decision = decidePendingAffordance(current, affordance); - if (!decision.ok) throw new Error(decision.reason); - pendingAtFinish = decision.pending; - return decision.pending; - }); - writeAffordance(affordance); - - return { output: affordance, terminate: true }; - }, - }); - - useTool({ - name: toolName("sweep"), - description: - "Apply or replay the settled conversation prefix. The harness privately extracts quote-anchored captures, refreshes durable history immediately before atomic application, and advances sweep state only on success.", - input: v.strictObject({}), - output: SweepToolOutput, - harness: true, - durable: true, - async run({ harness, signal, step }) { - const historyAtJudgment = await step.do("read-settled-range", async () => - projectFlueHistoryForSweep( - await session.historyReader.peek(session.sessionId), - ), - ); - const range = sweepableRange(historyAtJudgment); - const throughUserEntryId = range.at(-1)?.id; - if (!throughUserEntryId) { - return { output: { status: "no-settled-range" as const } }; - } - - const extraction = await step.do( - "extract-sweep-proposals", - async () => - ( - await harness.prompt( - buildSweepExtractionPrompt( - { - targetFormalism: plugin.targetFormalism, - proposalNames: plugin.proposalCatalog.map( - (proposal) => proposal.name, - ), - ...(slotModel === undefined - ? {} - : { guidance: slotAssertionExtractionGuidance(slotModel) }), - }, - range, - ), - { result: extractionResult, signal }, - ) - ).data, - ); - - // This read is intentionally adjacent to application: its binding-owned - // archive write makes every quote resolvable before the store sees it. - await step.do("refresh-history-before-apply", async () => - projectFlueHistoryForSweep( - await session.historyReader.read(session.sessionId), - ), - ); - const applied = await step.do("apply-sweep", () => - session.captureStore.execute( - { - type: "apply-sweep", - // The plugin schema narrows the existing envelope here; the store - // repeats envelope validation and owns anchoring at apply. - proposals: extraction.proposals, - }, - { sessionId: session.sessionId }, - ), - ); - if (!applied.ok) { - sweepState = reopenSweepAfterRefusal(sweepState); - setSweepState(sweepState); - return { - output: { status: "refused" as const, refusal: applied.refusal }, - }; - } - - sweepState = advanceSweepHighWater(sweepState, throughUserEntryId); - setSweepState(sweepState); - const accountedEntryIds = await capturedUserEntryIdsForSession( - session.captureStore, - applied.snapshot, - session.sessionId, - ); - const appliedCaptureIds = - "appliedCaptureIds" in applied.value - ? applied.value.appliedCaptureIds - : []; - const completion = - slotModel === undefined ? undefined : completionCue(applied.snapshot); - return { - output: { - status: "applied" as const, - appliedCaptureIds, - skippedDedupKeys: - "skippedDedupKeys" in applied.value - ? applied.value.skippedDedupKeys - : [], - advisories: [ - ...("advisories" in applied.value ? applied.value.advisories : []), - ...computeUnaccountedAskAdvisories(range, accountedEntryIds), - ], - captures: applied.snapshot.captures.map((capture) => - Object.assign({}, capture, { - status: deriveCaptureStatus(applied.snapshot, capture.id), - }), - ), - ...(completion === undefined ? {} : { completion }), - }, - }; - }, - }); - - useAgentFinish(async (ctx) => { - // useAgentFinish also fires on terminate:true asks. The callback's local - // view is updated by ask/reply callbacks in this render, so it observes the - // live slot rather than the render-time persistent-state snapshot. - if (pendingAtFinish !== null) return; - - const entries = projectFlueHistoryForSweep( - await session.historyReader.peek(session.sessionId), - ); - const repair = pendingSweepRepair(entries); - if (repair) { - ctx.append({ kind: "signal", ...buildSweepRepairSignal(repair) }); - return; - } - const decision = decideSettlementTrigger({ - entries, - state: sweepState, - pendingAffordance: false, - }); - if (decision.action !== "nudge") return; - - sweepState = decision.nextState; - setSweepState(sweepState); - ctx.append({ - kind: "signal", - ...buildSettlementCheckSignal(decision.tail), - }); - }); - - return [ - ...askProtocolInstructionFragments(plugin.targetFormalism), - ...settlementProtocolInstructionFragments(), - ...(definition === undefined - ? [] - : [renderInstructions(repertoire, definition)]), - ].join("\n\n"); -} diff --git a/libs/@hashintel/brunch-agent/packages/binding-flue/src/raw-imports.d.ts b/libs/@hashintel/brunch-agent/packages/binding-flue/src/raw-imports.d.ts deleted file mode 100644 index 9eaedc06726..00000000000 --- a/libs/@hashintel/brunch-agent/packages/binding-flue/src/raw-imports.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** Vite's `?raw` import: the repertoire and plugin definitions reach the binding as strings. */ -declare module "*.yaml?raw" { - const yaml: string; - export default yaml; -} diff --git a/libs/@hashintel/brunch-agent/packages/binding-flue/test/public-surface.test.ts b/libs/@hashintel/brunch-agent/packages/binding-flue/test/public-surface.test.ts new file mode 100644 index 00000000000..fd87749085a --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/binding-flue/test/public-surface.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, test } from "vitest"; + +import * as binding from "../src/index"; + +describe("the Flue binding public surface", () => { + test("keeps generalized typed elicitation suspended", () => { + expect(binding).not.toHaveProperty("useElicitation"); + expect(binding).toHaveProperty("createFlueHistoryReader"); + expect(binding).toHaveProperty("createLocalCaptureStore"); + }); +}); diff --git a/libs/@hashintel/brunch-agent/packages/binding-flue/vite.config.ts b/libs/@hashintel/brunch-agent/packages/binding-flue/vite.config.ts index f98b78c0397..69ce9ec1afa 100644 --- a/libs/@hashintel/brunch-agent/packages/binding-flue/vite.config.ts +++ b/libs/@hashintel/brunch-agent/packages/binding-flue/vite.config.ts @@ -16,7 +16,6 @@ export default defineConfig({ /^node:/u, /^@flue\//u, /^@hashintel\/brunch-agent(?:\/.*)?$/u, - "valibot", ], }, sourcemap: true, diff --git a/libs/@hashintel/brunch-agent/packages/core/.oxlintrc.json b/libs/@hashintel/brunch-agent/packages/core/.oxlintrc.json index 871350799af..c6e675617ad 100644 --- a/libs/@hashintel/brunch-agent/packages/core/.oxlintrc.json +++ b/libs/@hashintel/brunch-agent/packages/core/.oxlintrc.json @@ -34,8 +34,8 @@ "message": "Brunch libraries must not depend on Petrinaut implementations." }, { - "group": ["@flue/*", "@earendil-works/*"], - "message": "The Brunch harness must remain substrate-independent." + "group": ["@earendil-works/*"], + "message": "Flue is Brunch's production runtime; lower-level Pi packages remain outside core." }, { "group": ["@hashintel/brunch-agent-*"], diff --git a/libs/@hashintel/brunch-agent/packages/core/docs/task-dependencies.json b/libs/@hashintel/brunch-agent/packages/core/docs/task-dependencies.json index 1a0b25d8201..507757c5b53 100644 --- a/libs/@hashintel/brunch-agent/packages/core/docs/task-dependencies.json +++ b/libs/@hashintel/brunch-agent/packages/core/docs/task-dependencies.json @@ -11,9 +11,6 @@ "@local/eslint#build" ], "lint:tsc": [], - "test:unit": [ - "@hashintel/brunch-agent-plugin-gherkin#build", - "@hashintel/brunch-agent-plugin-sdcpn#build" - ] + "test:unit": [] } } diff --git a/libs/@hashintel/brunch-agent/packages/core/package.json b/libs/@hashintel/brunch-agent/packages/core/package.json index bc69c0952bc..ca72a1d986e 100644 --- a/libs/@hashintel/brunch-agent/packages/core/package.json +++ b/libs/@hashintel/brunch-agent/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@hashintel/brunch-agent", "version": "0.0.0-private", "private": true, - "description": "The harness: mechanism and orchestration. Its public export surface is the plugin SDK.", + "description": "The Brunch harness evidence layer, client contracts, and Flue-native core agent contribution.", "license": "AGPL-3.0", "type": "module", "exports": { @@ -14,17 +14,13 @@ "types": "./src/client-tools.ts", "import": "./dist/client-tools.js" }, - "./prompts": { - "types": "./src/prompts.ts", - "import": "./dist/prompts.js" + "./flue": { + "types": "./src/flue.ts", + "import": "./dist/flue.js" }, "./storage": { "types": "./src/storage.ts", "import": "./dist/storage.js" - }, - "./testing": { - "types": "./src/testing/index.ts", - "import": "./dist/testing/index.js" } }, "scripts": { @@ -33,18 +29,16 @@ "linear:graph": "node --experimental-strip-types ../../scripts/linear-project-graph.ts", "lint:eslint": "oxlint --type-aware --type-check --report-unused-disable-directives-severity=error .", "lint:tsc": "tsgo --noEmit", - "schema:emit": "PLUGIN_SCHEMA_EMIT=1 vitest run test/plugin-schema.test.ts", "test:unit": "vitest run" }, "dependencies": { - "valibot": "1.4.2", - "yaml": "2.9.0" + "@flue/runtime": "2.0.3", + "valibot": "1.4.2" }, "devDependencies": { "@anthropic-ai/sdk": "0.74.0", "@types/node": "22.18.13", "@typescript/native-preview": "7.0.0-dev.20260511.1", - "@valibot/to-json-schema": "1.7.1", "fast-check": "4.9.0", "oxlint": "1.63.0", "oxlint-tsgolint": "0.22.1", diff --git a/libs/@hashintel/brunch-agent/packages/core/schema/CHANGELOG.md b/libs/@hashintel/brunch-agent/packages/core/schema/CHANGELOG.md deleted file mode 100644 index 15685db78d9..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/schema/CHANGELOG.md +++ /dev/null @@ -1,220 +0,0 @@ -# Plugin schema changelog - -The key catalogue is a working set until a co-authoring cycle changes no key -(ADR-0007 decision 9). Each cycle records here what it added, merged, dropped, -or left alone, and why, with the evidence that moved it. `plugin.schema.json` -is derived from `PluginDefinitionSchema` in `src/plugin-definition.ts`; a test -fails when the two drift. - -## Cycle 1 — 2026-08-25 - -First materialisation. Both test-case plugins (`plugin-sdcpn`, `plugin-gherkin`) -and the repertoire were written against this shape together. - -- **Groups:** `plugin` (identity, not a key), `ontology`, `schema`, `patterns`, - `guidance`, `runbooks`, `machinery`. -- **Contract keys:** `ontology.kinds` (`kind`, `is`, `projects_to`), optional - `ontology.not_kinds` and `ontology.attributes`; `schema.anchor` (declared, - replacing the `objective`-by-convention anchor of the Markdown plugin file), - `schema.floor`, `schema.must_know`, `schema.proposals`; `patterns.items` - (`id`, `on`, `when`, `ask`). -- **Guidance keys:** `lenses`, `techniques`, `movements{slice,sweep}`, - `licenses`, `motifs`, `smells`, `rabbit_holes`, `failure_modes` — each a - list of `{name, text, signature?, source?}` so that default and cell - concatenate. -- **Runbook keys:** `kickoff`, `trajectory`, `close` per declared job. -- **Machinery:** `checks` and `tools` as identifier lists; nothing consumes - them yet. -- **Dropped from the Markdown plugin file:** the precision-words table (now - harness vocabulary, `PRECISION_LADDER`), the `Moves` and `Deliverable` prose - sections (their content is distributed over guidance and runbook keys), and - the fixed heading order as the contract (the schema is). -- **Open after this cycle:** whether `motifs` needs parameters as data rather - than prose; whether `licenses` has any plugin-specific content at all (both - plugins left it blank); whether `machinery.checks` should name harness - check implementations or plugin-provided ones. - -## Cycle 2 — input, 2026-08-25 - -What the first cycle's "validate" step returned. Source: the desk pressure review -[`docs/evidence/design/plugin-keys-pressure-review-cycle-1.md`](../../../docs/evidence/design/plugin-keys-pressure-review-cycle-1.md) -(100 situations from the CPS process-modelling material, the literature review, -and the condition-2 run; a discrete-event and a formal-verification plugin -sketched against the keys). The condition-4 baseline run (the rendered layer as -a prompt only) adds its strains in -`docs/evidence/evaluations/vestera-legacy-baseline/readout.md`. - -**Verdict on the catalogue: not frozen.** No key is added, merged, dropped, -split, or renamed by this input. All 100 situations land on an existing key or -contract row (33 carried by the repertoire default, 29 by sdcpn content, 38 -expressible but unwritten, 0 inexpressible). Two key *shapes* must change and -one matching defect was fixed before the catalogue can be said to have been -written against. - -### Fixed in this cycle - -- **`patterns.items[*].on: []` never fired.** `buildSweepList` tested - `kinds.includes(node.kind)`, which is false for an empty list, while the - contract documents "empty means any node". sdcpn `P08` (source-regime - divergence) therefore never reached the interviewer as a harness fact. - Fixed in `src/cue.ts`; `test/cue.test.ts` now covers a pattern indexed on no - kind firing on a failing node of another kind. - -### Shape changes proposed (inside existing keys) - -1. **`patterns.items[*].slot?: string`** — optional; when present the harness - surfaces the pattern only while *that* slot on the node is unsatisfied. - Evidence: kind-only matching makes sdcpn P01 and P02 indistinguishable at - fire time (both surface on any failing `activity`); a state-dependent - failure rate has no trigger at all; the archived CPS cards carried - slot-state predicates that the migration dropped. Cost to gherkin: none - (P01 would gain `slot: the examples that illustrate it`, P03 - `slot: the observable outcome`). -2. **`schema.must_know[*].precision` accepts a list (any-of).** A single word - forces the wrong word or a split row: sdcpn "the arrival or availability - pattern: spread" cannot accept a shift calendar (`spelled out`); "what - 'better' means: range" cannot accept a lexicographic cliff/slope rule - (`spelled out`). Cost to gherkin and the formal-verification sketch: none — - every row stays one word. -3. **Repertoire entry applicability facet** — e.g. `for_precision?: [range, - spread]` on a repertoire item; `renderGuidance` renders it only when some - `must_know` row of the plugin demands one of those words. Not a plugin - override (decision 1 holds: the harness decides from the plugin's own - contract data). Evidence: six of the repertoire's 36 guidance entries are - quantity methods ("Mean or tail", "Quantiles, never three points", "The - clairvoyant test", "Premortem", kickoff "numerically where possible", sweep - "every step has a duration") rendered for gherkin and for a - formal-verification plugin, where they are noise; and the lens "Policy - versus practice" is one a specification-of-intent plugin (gherkin - `status: proposed`, any verification property) must *contradict*, which - decision 1 forbids — it needs the same facet or a conditioned text. - -### Content findings (no schema change; edits due in this cycle) - -- **Specificity.** sdcpn `motifs` are six name-only lines that restate the - patterns 1:1 and violate the repertoire's own "Name plus variant" default - rendered directly above them; each needs its axis (server semantics — - indivisible vs several; batch formation rule — count *or* clock; several - wear components — weakest decides). Quantile elicitation is stated four - times in the sdcpn render; "every rule has an example" four times in the - gherkin render. Cells add and never override, but nothing says they never - repeat and no gate checks it — a "cells add, never repeat" test is worth - adding. -- **Selection half missing.** `kickoff` produces a posture and nothing - consumes it: the `trajectory` default has no posture-varied biases (ADR - decision 2's "explore openly when appetite is high, synthesise and invite - correction when constrained, propose low-risk structure"). Write them or - drop posture from `kickoff`. -- **Repertoire under-fill against ADR decision 2's own rows.** `licenses` - lacks "press a busy expert", "decline to sweep", "propose structure as a - suggestion"; `rabbit_holes` lacks "asking the expert what you failed to - ask", "restating the whole model", "taking a schedule or a document for the - practised rule"; `smells` lacks "schema-shaped questioning" and - "correction-as-duplication"; `kickoff` lacks boundaries / horizon / - experimental factors / accuracy bar; `close` (construct) names no stopping - outcomes. -- **Contract data.** `ontology.attributes` renders as prose; `source-regime` - works only because the harness hard-codes it. The never-asked sdcpn row - (`activity` — what is lost when it changes the system's mode) is - `not_applicable: true` and can be ticked away without a question, which - reproduces condition 2's ramp-scrap omission. Gherkin `step — the known step - it binds to: named` needs a team step lexicon the interviewer cannot see: - a plugin needs reference *data* that is neither cell nor code. -- **Contradictions the repertoire resolves silently** (must be stated, not - fixed by fiat): the clearinghouse probe is licensed by `movements.sweep` - and forbidden by the archived CPS guidance, the condition-3 prompt, and - ADR-0007's `rabbit_holes` row; the quantile order is v0's typical-first - while citing the IDEA protocol's interval-first; batching 2–4 is stated as - a license without its single-run basis; "hypotheticals only from a real - case" would forbid condition 2's most productive move (four constructed - scenarios); "Restate to check" / "Assent taken as origin" do not say how a - confirmed interviewer inference becomes a capture; "No structure in the - first exchange" then asks for a three-to-six-step account, which is - structure; sdcpn "depth on IR-only kinds" defers `validation-criterion` - where the literature puts the accuracy bar before building. - -### Considered and left - -- `motifs` parameters as data — nothing consumes them; fix the content first - (open item carried from cycle 1). -- Merge `motifs` into `patterns` — they differ by mechanism (attention - scaffold vs matched trigger); gherkin's motifs have no pattern twin. -- Merge `smells` into `failure_modes` — the frame distinction (own output vs - named failure) is sound; authors are not honouring it. -- Drop the plugin cell of `licenses` — both blank, zero cost, and a cell that - contradicts a default is better detected present than absent. -- Add a `scope` runbook key — `kickoff` and `close` carry it once written. -- Add a fourth movement (`cross-examine`) — the consistency probe is a - technique; soundness questions need projection machinery first. -- Make `ontology.attributes` data — promote when a second attribute needs the - fold, not before. -- `movements` fixed to `{slice, sweep}` — every formalism examined fits the - pair; a single-walkthrough formalism would leave `sweep` empty, which the - schema allows for plugins. - -## Cycle 2 — implementation, 2026-08-26 - -- Added optional `patterns.items[*].slot`. The reader rejects a slot that an - explicitly indexed kind does not demand; the cue surfaces a slot-scoped - pattern only for a failure on that slot. SDCPN P01/P02 and Gherkin P01/P03 - now declare the predicates identified by the cycle-1 pressure review. -- Extended `schema.must_know[*].precision` to accept a non-empty list of - alternative precision words. Completion accepts a value that satisfies any - listed word and renders the alternatives explicitly. SDCPN's objective - metric accepts `range` or `spelled out`; its arrival pattern accepts `spread` - or `spelled out`, covering numeric distributions and structural rules without - splitting either semantic slot. -- Added repertoire-item `for_precision`, a non-empty list of precision words. - Rendering now omits an annotated default unless the plugin demands at least - one listed word. The eight quantity and observed-practice entries identified - by the cycle review use the facet, so Gherkin is no longer taught numeric or - retrospective elicitation merely because it shares the fixed key catalogue. -- Completed the cycle-two content pass without adding or removing a key. The - repertoire now consumes posture in trajectory selection; fills the ADR's - missing licenses, smells, rabbit holes, kickoff scope, and stopping outcomes; - and states its choices on clearinghouse probes, quantile order, batching, - hypotheticals, confirmed restatements, and structure after kickoff. SDCPN - motifs now name their variant axes, its cells no longer repeat generic - quantile teaching, and its demand rows, sweep, and anti-guidance cover - dynamics noise and the missed edge material. Gherkin's duplicate - rule-without-example failure mode was removed. A gate now rejects exact - sentence repetition between repertoire defaults and plugin cells. - -### Cycle-two review dispositions - -- Objective identity and deduplication do not change the key catalogue. They are - register semantics: plugins name the `objective` kind and its slots, while the - harness must decide whether a later statement refers to an existing objective - or creates another one. -- Removed the SDCPN `rationale` attribute. The cycle found no evidence that - rationale should be collected on every kind, so a universal plugin facet - would create noise; targeted "why" questions remain available in patterns and - guidance where they resolve a known model gap. -- Recounted the cycle-one condition-five evidence before considering finer - `must_know` rows. Its 267 captures use 22 unique slot names, all exact names - declared by the plugin; the larger observed count was slot instances across - nodes, not undeclared slot vocabulary. The catalogue therefore keeps the - current slot granularity until a run demonstrates a recurring unresolved - sub-slot. -- Clarified that pattern matching is kind plus optional unsatisfied demanded - slot. Pattern prose explains why the surfaced candidate applies; it does not - add a hidden machine predicate. -- Restricted "One incident is not a rate" to plugins demanding `range` or - `spread`, removed unqualified grade terminology from SDCPN prose, and shared - demand formatting between completion diagnostics and rendered instructions. - -### Catalogue freeze - -Conditions 4 and 5 completed against the cycle-two definitions on 2026-08-26. -The review found no fact that required adding, merging, or dropping a key. The -formal-verification sketch still fills cells only against the final contract: -its `named`, `spelled out`, and `at least N` demands remain accepted, while the -applicability facet omits the quantity and policy-versus-practice defaults it -identified as noise. The fixed catalogue therefore freezes at cycle two. - -The runs did expose residual harness work: node identity and deduplication, -quote-repair efficiency, prompt-only delivery classification, and a terminal act -for an incomplete engagement after the expert stops. Those findings belong to -the sweep/fold, evaluation, and session-control machinery; none is repaired by a -new plugin-authoring key. The evidence and full verdict are recorded in the -[baseline read-out](../../../docs/evidence/evaluations/vestera-legacy-baseline/readout.md). diff --git a/libs/@hashintel/brunch-agent/packages/core/schema/plugin.schema.json b/libs/@hashintel/brunch-agent/packages/core/schema/plugin.schema.json deleted file mode 100644 index eb09fcc881d..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/schema/plugin.schema.json +++ /dev/null @@ -1,751 +0,0 @@ -{ - "$id": "https://hash.ai/brunch-agent/plugin.schema.json", - "title": "Brunch plugin definition", - "description": "A plugin is data under harness-owned keys (ADR-0007). Cross-references the schema cannot state — rows name declared kinds, the anchor is a row, runbooks belong to declared jobs — are checked by readPluginDefinition.", - "type": "object", - "properties": { - "plugin": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "version": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*\\/\\d{4}-\\d{2}-\\d{2}\\.\\d+$" - }, - "formalism": { - "type": "string", - "minLength": 1 - }, - "jobs": { - "type": "array", - "items": { - "enum": ["construct", "review-and-revise"], - "type": "string" - }, - "minItems": 1 - }, - "purpose": { - "type": "string", - "minLength": 1 - } - }, - "required": ["id", "version", "formalism", "jobs", "purpose"], - "additionalProperties": false - }, - "ontology": { - "type": "object", - "properties": { - "preamble": { - "type": "string", - "minLength": 1 - }, - "kinds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "minLength": 1 - }, - "is": { - "type": "string", - "minLength": 1 - }, - "projects_to": { - "type": "string", - "minLength": 1 - } - }, - "required": ["kind", "is", "projects_to"], - "additionalProperties": false - }, - "minItems": 1 - }, - "not_kinds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "attributes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "on": { - "type": "string", - "minLength": 1 - }, - "values": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "text": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "on", "text"], - "additionalProperties": false - } - } - }, - "required": ["kinds"], - "additionalProperties": false - }, - "schema": { - "type": "object", - "properties": { - "preamble": { - "type": "string", - "minLength": 1 - }, - "anchor": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "minLength": 1 - }, - "depends_on": { - "type": "string", - "minLength": 1 - } - }, - "required": ["kind", "depends_on"], - "additionalProperties": false - }, - "floor": { - "type": "array", - "items": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "minLength": 1 - }, - "at_least": { - "type": "integer", - "minimum": 1 - } - }, - "required": ["kind", "at_least"], - "additionalProperties": false - } - }, - "must_know": { - "type": "array", - "items": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "minLength": 1 - }, - "slot": { - "type": "string", - "minLength": 1 - }, - "precision": { - "anyOf": [ - { - "enum": [ - "named", - "number", - "range", - "spread", - "spelled out" - ], - "type": "string" - }, - { - "type": "array", - "items": { - "enum": [ - "named", - "number", - "range", - "spread", - "spelled out" - ], - "type": "string" - }, - "minItems": 1 - }, - { - "type": "string", - "pattern": "^at least [1-9]\\d*$" - } - ] - }, - "not_applicable": { - "type": "boolean" - }, - "why": { - "type": "string", - "minLength": 1 - } - }, - "required": ["kind", "slot", "precision", "not_applicable", "why"], - "additionalProperties": false - }, - "minItems": 1 - }, - "proposals": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "payload": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "required": ["type", "payload"], - "additionalProperties": false - }, - "minItems": 1 - } - }, - "required": ["anchor", "floor", "must_know", "proposals"], - "additionalProperties": false - }, - "patterns": { - "type": "object", - "properties": { - "preamble": { - "type": "string", - "minLength": 1 - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^P\\d{2}$" - }, - "on": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "slot": { - "type": "string", - "minLength": 1 - }, - "when": { - "type": "string", - "minLength": 1 - }, - "ask": { - "type": "string", - "minLength": 1 - } - }, - "required": ["id", "on", "when", "ask"], - "additionalProperties": false - } - } - }, - "required": ["items"], - "additionalProperties": false - }, - "guidance": { - "type": "object", - "properties": { - "lenses": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "techniques": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "movements": { - "type": "object", - "properties": { - "slice": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "sweep": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - } - }, - "required": ["slice", "sweep"], - "additionalProperties": false - }, - "licenses": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "motifs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "smells": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "rabbit_holes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "failure_modes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - } - }, - "required": [ - "lenses", - "techniques", - "movements", - "licenses", - "motifs", - "smells", - "rabbit_holes", - "failure_modes" - ], - "additionalProperties": false - }, - "runbooks": { - "type": "object", - "properties": { - "construct": { - "type": "object", - "properties": { - "kickoff": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "trajectory": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "close": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - } - }, - "required": ["kickoff", "trajectory", "close"], - "additionalProperties": false - }, - "review-and-revise": { - "type": "object", - "properties": { - "kickoff": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "trajectory": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - }, - "close": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 - }, - "text": { - "type": "string", - "minLength": 1 - }, - "signature": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "minLength": 1 - } - }, - "required": ["name", "text"], - "additionalProperties": false - } - } - }, - "required": ["kickoff", "trajectory", "close"], - "additionalProperties": false - } - }, - "required": [], - "additionalProperties": false - }, - "machinery": { - "type": "object", - "properties": { - "checks": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "tools": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - } - }, - "required": ["checks", "tools"], - "additionalProperties": false - } - }, - "required": [ - "plugin", - "ontology", - "schema", - "patterns", - "guidance", - "runbooks", - "machinery" - ], - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" -} diff --git a/libs/@hashintel/brunch-agent/packages/core/src/affordance.ts b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/affordance.ts similarity index 100% rename from libs/@hashintel/brunch-agent/packages/core/src/affordance.ts rename to libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/affordance.ts diff --git a/libs/@hashintel/brunch-agent/packages/core/src/ask-protocol.ts b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/ask-protocol.ts similarity index 98% rename from libs/@hashintel/brunch-agent/packages/core/src/ask-protocol.ts rename to libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/ask-protocol.ts index 6c76fb73a9b..cb18c111055 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/ask-protocol.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/ask-protocol.ts @@ -1,4 +1,4 @@ -import { toolName } from "./naming"; +import { toolName } from "../../conversation/naming"; import type { FreeTextAffordance } from "./affordance"; import type { SweepSessionEntry } from "./sweep-protocol"; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/ask-tool-contract.ts b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/ask-tool-contract.ts similarity index 100% rename from libs/@hashintel/brunch-agent/packages/core/src/ask-tool-contract.ts rename to libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/ask-tool-contract.ts diff --git a/libs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/sweep-protocol.ts similarity index 94% rename from libs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts rename to libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/sweep-protocol.ts index 7fb7a6e77db..2a9e742154f 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/sweep-protocol.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/_suspended/conversation/sweep-protocol.ts @@ -1,26 +1,13 @@ import * as v from "valibot"; -import { toolName } from "./naming"; +import { toolName } from "../../conversation/naming"; +import type { SessionEntryKind } from "../../evidence/session-log"; +import type { ReadonlyDeep } from "../../readonly-deep"; import type { FreeTextAffordance } from "./affordance"; -import type { CaptureInputProposal } from "./capture-store"; -import type { Plugin } from "./plugin"; -import type { ReadonlyDeep } from "./readonly-deep"; -import type { SessionEntryKind } from "./session-log"; const nonEmptyString = v.pipe(v.string(), v.nonEmpty()); -export interface SweepExtraction { - readonly proposals: readonly CaptureInputProposal[]; -} - -export const createSweepExtractionResultSchema = ( - plugin: Plugin, -): v.GenericSchema<unknown, SweepExtraction> => - v.strictObject({ - proposals: v.array(plugin.proposalCatalog[0].schema), - }); - export type SweepAffordance = Pick<FreeTextAffordance, "id" | "markdown">; export interface SweepRefusalFact { diff --git a/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts b/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts index b866300d288..f8398cac0d3 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts @@ -4,15 +4,21 @@ import * as v from "valibot"; -import { AskInput, AskSubmission } from "./ask-tool-contract"; -import { toolName } from "./naming"; +import { + AskInput, + AskSubmission, +} from "./_suspended/conversation/ask-tool-contract"; +import { toolName } from "./conversation/naming"; export { AskInput, AskSubmission, toolName }; -export type { ToolName } from "./naming"; +export type { ToolName } from "./conversation/naming"; export const ASK_TOOL_NAME = toolName("ask"); export const SWEEP_TOOL_NAME = toolName("sweep"); +/** A Flue tool result that delegates execution to the connected client. */ +export const AWAITING_CLIENT = "client" as const; + export type BrunchAskInput = v.InferOutput<typeof AskInput>; export type BrunchAskOutput = v.InferOutput<typeof AskSubmission>; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/completion.ts b/libs/@hashintel/brunch-agent/packages/core/src/completion.ts deleted file mode 100644 index 1d3047100d3..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/completion.ts +++ /dev/null @@ -1,391 +0,0 @@ -/** - * `evaluateCompletion(model, demands)` — the nineteen invariants of - * `docs/specs/elicitation-completion.md`, as code. - * - * Pure over the register-2 model and the plugin's demand rows. It reads no - * transcript, turn count, session state, delivery, or budget; the same - * `(model, demands)` always yields the same report. The answer is a derived - * boolean plus an evidence-bearing report, never a lifecycle status: nothing - * here is persisted and a later capture can turn `complete` back to `false`. - */ - -import { type EpistemicStatus } from "./capture-store"; -import { - type ElicitedModel, - type ElicitedNode, - type SlotState, -} from "./elicited-model"; -import { - formatPrecisionDemand, - type FloorRow, - type MustKnowRow, - type PluginDefinition, - type PrecisionWord, -} from "./plugin-definition"; - -import type { JsonValue } from "./json-value"; - -export const COMPLETION_DIAGNOSTICS = [ - "version-mismatch", - "below-minimum-count", - "unsupported-active-objective", - "unaddressed", - "no-selected-slot", - "inadmissible-status", - "unaccepted-absence", - "below-required-precision", - "open-conflict", - "unresolved-divergence", - "missing-evidence", -] as const; - -export type CompletionDiagnostic = (typeof COMPLETION_DIAGNOSTICS)[number]; - -export interface CompletionFailure { - readonly diagnostic: CompletionDiagnostic; - readonly nodeId?: string; - readonly kind?: string; - readonly slot?: string; - /** What the row demands, in the row's own words. */ - readonly requirement: string; - /** What the model holds. */ - readonly actual: string; - readonly message: string; - /** Supporting captures reached through the slot's support links. */ - readonly captureIds: readonly string[]; -} - -export interface OutsideSliceNode { - readonly nodeId: string; - readonly kind: string; - /** Open issues on nodes no active objective depends on: visible, not blocking. */ - readonly open: readonly CompletionFailure[]; -} - -export interface CompletionReport { - readonly complete: boolean; - readonly pluginVersion: string; - readonly revision: string; - readonly failures: readonly CompletionFailure[]; - /** Nodes some active objective depends on (objectives included). */ - readonly sliceNodeIds: readonly string[]; - readonly outsideSlice: readonly OutsideSliceNode[]; -} - -/** - * Which kind anchors question-relative demand and which of its slots names the - * dependency slice. Derived from the file by convention: the kind named - * `objective` and its single `at least N` row. Absent both, every node is demanded. - */ -export interface CompletionAnchor { - readonly kind: string; - readonly dependencySlot: string; - readonly atLeast: number; -} - -export interface CompletionDemands { - readonly pluginVersion: string; - readonly floor: readonly FloorRow[]; - readonly rows: readonly MustKnowRow[]; - /** Statuses a value may carry and count. Confirmation of an inference is itself an explicit capture. */ - readonly acceptedStatuses: readonly EpistemicStatus[]; - readonly anchor?: CompletionAnchor; -} - -/** The demands one plugin definition states, with `explicit` as the default accepted status. */ -export const completionDemands = ( - definition: PluginDefinition, - options: { readonly acceptedStatuses?: readonly EpistemicStatus[] } = {}, -): CompletionDemands => { - const anchorRow = definition.mustKnow.find( - (row) => - row.kind === definition.anchor.kind && - row.slot === definition.anchor.dependencySlot, - ); - return { - pluginVersion: definition.version, - floor: definition.floor, - rows: definition.mustKnow, - acceptedStatuses: options.acceptedStatuses ?? ["explicit"], - ...(anchorRow && anchorRow.precision.kind === "at-least" - ? { - anchor: { - kind: anchorRow.kind, - dependencySlot: anchorRow.slot, - atLeast: anchorRow.precision.count, - }, - } - : {}), - }; -}; - -const LADDER: Readonly<Record<PrecisionWord, number | null>> = { - named: 0, - number: 1, - range: 2, - spread: 3, - "spelled out": null, -}; - -/** - * Whether a value at `given` precision meets `demanded`. Numeric words form a - * ladder (`spread` ⊃ `range` ⊃ `number` ⊃ `named`); `spelled out` is a - * structure, satisfied only by itself, and it counts as `named`. - */ -export const precisionSatisfies = ( - given: PrecisionWord, - demanded: PrecisionWord, -): boolean => { - if (demanded === "spelled out") return given === "spelled out"; - if (given === "spelled out") return demanded === "named"; - return LADDER[given]! >= LADDER[demanded]!; -}; - -const isEmptySelection = (value: JsonValue): boolean => - value === null || - value === "" || - (Array.isArray(value) && value.length === 0) || - (typeof value === "object" && - !Array.isArray(value) && - Object.keys(value).length === 0); - -const describeSlot = (slot: SlotState | undefined): string => { - if (slot === undefined) return "not mentioned"; - switch (slot.state) { - case "value": - return `${slot.precision} value under status ${slot.status}`; - case "absence": - return `absence: ${slot.absence}${slot.pointer ? ` (source: ${slot.pointer})` : ""}`; - case "conflict": - return `${slot.readings.length} competing active readings`; - case "divergence": - return "prescribed and practiced readings diverge"; - } -}; - -const ACCEPTED_ABSENCES = new Set(["not-applicable", "explicitly-absent"]); - -const evaluateRow = ( - node: ElicitedNode, - row: MustKnowRow, - demands: CompletionDemands, - model: ElicitedModel, -): CompletionFailure | null => { - const slot = node.slots[row.slot]; - const base = { - nodeId: node.id, - kind: node.kind, - slot: row.slot, - requirement: formatPrecisionDemand(row.precision), - actual: describeSlot(slot), - captureIds: slot?.captureIds ?? [], - }; - const fail = ( - diagnostic: CompletionDiagnostic, - message: string, - ): CompletionFailure => ({ ...base, diagnostic, message }); - - if (slot === undefined) { - return fail( - "unaddressed", - `"${row.slot}" has not been addressed on ${node.id}.`, - ); - } - if (slot.state === "conflict") { - return fail( - "open-conflict", - `"${row.slot}" on ${node.id} has competing active captures; an explicit, user-cited resolution must close it.`, - ); - } - if (slot.state === "divergence") { - return fail( - "unresolved-divergence", - `"${row.slot}" on ${node.id} differs between the prescribed and the practiced reading; the expert must resolve which the model follows.`, - ); - } - if (!demands.acceptedStatuses.includes(slot.status)) { - return fail( - "inadmissible-status", - `"${row.slot}" on ${node.id} is held under status ${slot.status}; accepted: ${demands.acceptedStatuses.join(", ")}.`, - ); - } - if ( - !slot.evidenced || - slot.captureIds.some((id) => !model.activeCaptureIds.has(id)) - ) { - return fail( - "missing-evidence", - `"${row.slot}" on ${node.id} is not backed by active, traceable user evidence.`, - ); - } - if (slot.state === "absence") { - if (!ACCEPTED_ABSENCES.has(slot.absence)) { - return fail( - "unaddressed", - `"${row.slot}" on ${node.id} is open: the expert answered "${slot.absence}"${slot.pointer ? `, pointing at ${slot.pointer}` : ""}; that is not a value.`, - ); - } - if (!row.notApplicableAllowed) { - return fail( - "unaccepted-absence", - `"${row.slot}" on ${node.id} was declared ${slot.absence}, but this row does not allow an absence.`, - ); - } - return null; - } - if (isEmptySelection(slot.value)) { - return fail( - "no-selected-slot", - `"${row.slot}" on ${node.id} selects nothing; a demand never passes through an empty selection.`, - ); - } - if (row.precision.kind === "at-least") { - const count = Array.isArray(slot.value) ? slot.value.length : 1; - return count >= row.precision.count - ? null - : fail( - "below-minimum-count", - `"${row.slot}" on ${node.id} lists ${count}; at least ${row.precision.count} needed.`, - ); - } - const demandedWords = - row.precision.kind === "word" ? [row.precision.word] : row.precision.words; - if ( - !demandedWords.some((demanded) => - precisionSatisfies(slot.precision, demanded), - ) - ) { - const requirement = formatPrecisionDemand(row.precision); - return fail( - "below-required-precision", - `"${row.slot}" on ${node.id} is known as a ${slot.precision}; the model needs ${requirement}. Smallest delta: move it from ${slot.precision} to ${row.precision.kind === "word" ? row.precision.word : `one of ${requirement}`}.`, - ); - } - return null; -}; - -const dependencyIds = (slot: SlotState | undefined): readonly string[] => { - if (slot?.state !== "value") { - return []; - } - if (Array.isArray(slot.value)) { - return slot.value.filter( - (entry): entry is string => typeof entry === "string", - ); - } - return typeof slot.value === "string" && slot.value !== "" - ? [slot.value] - : []; -}; - -export function evaluateCompletion( - model: ElicitedModel, - demands: CompletionDemands, -): CompletionReport { - const header = { - pluginVersion: demands.pluginVersion, - revision: model.revision, - }; - if (model.pluginVersion !== demands.pluginVersion) { - return { - ...header, - complete: false, - failures: [ - { - diagnostic: "version-mismatch", - requirement: `rows of plugin version ${demands.pluginVersion}`, - actual: `model folded under ${model.pluginVersion}`, - message: - "The model and the demand rows come from different plugin versions; refold and retry.", - captureIds: [], - }, - ], - sliceNodeIds: [], - outsideSlice: [], - }; - } - - const failures: CompletionFailure[] = []; - - for (const floor of demands.floor) { - const count = model.nodes.filter((node) => node.kind === floor.kind).length; - if (count < floor.atLeast) { - failures.push({ - diagnostic: "below-minimum-count", - kind: floor.kind, - requirement: `at least ${floor.atLeast} ${floor.kind}`, - actual: `${count}`, - message: `The model has ${count} ${floor.kind} node(s); the floor needs ${floor.atLeast}.`, - captureIds: [], - }); - } - } - - const byId = new Map(model.nodes.map((node) => [node.id, node])); - const slice = new Set<string>(); - const { anchor } = demands; - if (anchor === undefined) { - for (const node of model.nodes) slice.add(node.id); - } else { - for (const objective of model.nodes.filter( - (node) => node.kind === anchor.kind, - )) { - slice.add(objective.id); - const slot = objective.slots[anchor.dependencySlot]; - const wanted = dependencyIds(slot); - const resolved = wanted.filter((id) => byId.has(id)); - const dangling = wanted.filter((id) => !byId.has(id)); - if (resolved.length < anchor.atLeast) { - failures.push({ - diagnostic: "unsupported-active-objective", - nodeId: objective.id, - kind: objective.kind, - slot: anchor.dependencySlot, - requirement: `at least ${anchor.atLeast} node the objective depends on`, - actual: - slot === undefined - ? "not mentioned" - : `${resolved.length} resolved${dangling.length > 0 ? `, ${dangling.length} naming no node in the model (${dangling.join(", ")})` : ""}`, - message: `${objective.id} depends on nothing the model contains; an objective that depends on nothing is unsupported.`, - captureIds: slot?.captureIds ?? [], - }); - } - for (const id of resolved) slice.add(id); - } - } - - const rowsFor = (kind: string): MustKnowRow[] => - demands.rows.filter( - (row) => - row.kind === kind && - !( - anchor !== undefined && - kind === anchor.kind && - row.slot === anchor.dependencySlot - ), - ); - - const outsideSlice: OutsideSliceNode[] = []; - for (const node of model.nodes) { - const rowFailures = rowsFor(node.kind) - .map((row) => evaluateRow(node, row, demands, model)) - .filter((failure): failure is CompletionFailure => failure !== null); - if (slice.has(node.id)) { - failures.push(...rowFailures); - } else { - outsideSlice.push({ - nodeId: node.id, - kind: node.kind, - open: rowFailures, - }); - } - } - - return { - ...header, - complete: failures.length === 0, - failures, - sliceNodeIds: [...slice].sort(), - outsideSlice, - }; -} diff --git a/libs/@hashintel/brunch-agent/packages/core/src/naming.ts b/libs/@hashintel/brunch-agent/packages/core/src/conversation/naming.ts similarity index 100% rename from libs/@hashintel/brunch-agent/packages/core/src/naming.ts rename to libs/@hashintel/brunch-agent/packages/core/src/conversation/naming.ts diff --git a/libs/@hashintel/brunch-agent/packages/core/src/reply-protocol.ts b/libs/@hashintel/brunch-agent/packages/core/src/conversation/reply-protocol.ts similarity index 100% rename from libs/@hashintel/brunch-agent/packages/core/src/reply-protocol.ts rename to libs/@hashintel/brunch-agent/packages/core/src/conversation/reply-protocol.ts diff --git a/libs/@hashintel/brunch-agent/packages/core/src/cue.ts b/libs/@hashintel/brunch-agent/packages/core/src/cue.ts deleted file mode 100644 index 3d555127051..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/cue.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * The cue — what the harness tells the interviewer after it has read the model. - * - * A sweep list is the completion report's failures plus the patterns whose - * kind-index matches a node that still has one (an empty index matches every - * kind, as the plugin contract documents). It is a harness fact, so it - * reaches the model as a tool result or a signal entry, never interpolated - * into instructions (Flue routing: "you need the model to see a harness fact"). - * Patterns are surfaced, never mandated; the interviewer decides. - */ - -import { type CompletionFailure, type CompletionReport } from "./completion"; -import { type ElicitedModel } from "./elicited-model"; -import { type PatternRow } from "./plugin-definition"; - -export interface PatternCue { - readonly id: string; - readonly nodeId: string; - readonly ask: string; -} - -export interface SweepList { - readonly unsatisfied: readonly CompletionFailure[]; - readonly patterns: readonly PatternCue[]; -} - -export const buildSweepList = ( - model: ElicitedModel, - report: CompletionReport, - patterns: readonly PatternRow[], -): SweepList => { - const failingNodeIds = new Set( - report.failures.flatMap((failure) => - failure.nodeId === undefined ? [] : [failure.nodeId], - ), - ); - const cues: PatternCue[] = []; - for (const node of model.nodes) { - if (!failingNodeIds.has(node.id)) continue; - for (const pattern of patterns) { - const kindMatches = - pattern.kinds.length === 0 || pattern.kinds.includes(node.kind); - const slotMatches = - pattern.slot === undefined || - report.failures.some( - (failure) => - failure.nodeId === node.id && failure.slot === pattern.slot, - ); - if (kindMatches && slotMatches) { - cues.push({ id: pattern.id, nodeId: node.id, ask: pattern.ask }); - } - } - } - return { unsatisfied: report.failures, patterns: cues }; -}; - -export interface CompletionCueSignal { - readonly type: "completion-cue"; - readonly tagName: "completion-cue"; - readonly body: string; -} - -const renderFailure = (failure: CompletionFailure): string => - `- [${failure.diagnostic}] ${failure.message}`; - -export const buildCompletionCueSignal = ( - model: ElicitedModel, - report: CompletionReport, - sweepList: SweepList, - options: { readonly maxItems?: number } = {}, -): CompletionCueSignal => { - const maxItems = options.maxItems ?? 12; - const shown = sweepList.unsatisfied.slice(0, maxItems); - const hidden = sweepList.unsatisfied.length - shown.length; - const nodeSummary = `${model.nodes.length} node(s) from ${model.activeCaptureIds.size} active capture(s)${model.unmapped.length > 0 ? `; ${model.unmapped.length} capture(s) could not be mapped to a kind and slot` : ""}`; - const parts = [ - `The harness folded the model at revision ${report.revision} (plugin ${report.pluginVersion}): ${nodeSummary}. Complete: ${report.complete ? "yes" : "no"}.`, - ]; - if (shown.length > 0) { - parts.push( - [ - "Unsatisfied, in file order:", - ...shown.map(renderFailure), - ...(hidden > 0 ? [`- … and ${hidden} more.`] : []), - ].join("\n"), - ); - } - if (sweepList.patterns.length > 0) { - const byPattern = new Map<string, PatternCue>(); - for (const cue of sweepList.patterns) { - if (!byPattern.has(cue.id)) byPattern.set(cue.id, cue); - } - parts.push( - [ - "Patterns whose trigger may apply (discretionary):", - ...[...byPattern.values()] - .slice(0, maxItems) - .map((cue) => `- ${cue.id} on ${cue.nodeId}: ${cue.ask}`), - ].join("\n"), - ); - } - if (report.outsideSlice.length > 0) { - parts.push( - `${report.outsideSlice.length} node(s) lie outside every objective's dependency slice and are recorded but not demanded.`, - ); - } - parts.push( - "Completion is computed from the model, never from the conversation; it does not decide whether to continue. Choose the next question, or none.", - ); - return { - type: "completion-cue", - tagName: "completion-cue", - body: parts.join("\n\n"), - }; -}; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/elicited-model.ts b/libs/@hashintel/brunch-agent/packages/core/src/elicited-model.ts deleted file mode 100644 index bd79b718350..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/elicited-model.ts +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Register 2 — the elicited model, derived and never stored (ADR-0003). - * - * `foldElicitedModel` is a pure function of a capture-store snapshot and a - * plugin file. It keeps only active captures, reads each one's slot assertion, - * and groups them into nodes and slots. It is forbidden to interpret: a payload - * it cannot read becomes an `unmapped` entry, two competing readings become a - * `conflict`, a manual-versus-practice split becomes a `divergence`. Every slot - * state answers "which captures made you" through its capture ids. - */ - -import * as v from "valibot"; - -import { - deriveCaptureStatus, - deriveIssueStatus, - type AbsenceState, - type CaptureEnvelope, - type CaptureStoreSnapshot, - type EpistemicStatus, -} from "./capture-store"; -import { type PluginDefinition, type PrecisionWord } from "./plugin-definition"; -import { - createSlotAssertionSchema, - nodeId, - type SlotAssertion, - type SourceRegime, -} from "./slot-assertion"; - -import type { JsonValue } from "./json-value"; - -export interface SlotReading { - readonly captureId: string; - readonly status: EpistemicStatus; - /** Whether user evidence spans back the capture (false for defaults and lookups). */ - readonly evidenced: boolean; - readonly assertion: SlotAssertion; -} - -export type SlotState = - | { - readonly state: "value"; - readonly value: JsonValue; - readonly precision: PrecisionWord; - readonly status: EpistemicStatus; - readonly evidenced: boolean; - readonly sourceRegime?: SourceRegime; - readonly rationale?: string; - readonly captureIds: readonly string[]; - } - | { - readonly state: "absence"; - readonly absence: AbsenceState; - readonly pointer?: string; - readonly status: EpistemicStatus; - readonly evidenced: boolean; - readonly captureIds: readonly string[]; - } - | { - readonly state: "conflict"; - readonly readings: readonly SlotReading[]; - readonly captureIds: readonly string[]; - } - | { - readonly state: "divergence"; - readonly prescribed: SlotReading; - readonly practiced: SlotReading; - readonly captureIds: readonly string[]; - }; - -export interface ElicitedNode { - readonly id: string; - readonly kind: string; - readonly name: string; - readonly slots: Readonly<Record<string, SlotState>>; -} - -export interface UnmappedCapture { - readonly captureId: string; - readonly reason: string; -} - -export interface ElicitedModel { - readonly pluginVersion: string; - /** Content digest of the active captures and open conflicts this model was folded from. */ - readonly revision: string; - readonly nodes: readonly ElicitedNode[]; - readonly unmapped: readonly UnmappedCapture[]; - readonly activeCaptureIds: ReadonlySet<string>; -} - -const canonical = (value: unknown): string => - JSON.stringify(value, (_key, inner: unknown) => - inner !== null && typeof inner === "object" && !Array.isArray(inner) - ? Object.fromEntries( - Object.entries(inner as Record<string, unknown>).sort(([a], [b]) => - a.localeCompare(b), - ), - ) - : inner, - ); - -/** A stable, dependency-free digest; not cryptographic, only a revision label. */ -const digest = (text: string): string => { - const primeA = 1_000_000_007; - const primeB = 998_244_353; - let a = 17; - let b = 31; - for (let index = 0; index < text.length; index += 1) { - const code = text.charCodeAt(index); - a = (a * 131 + code) % primeA; - b = (b * 137 + code) % primeB; - } - return `${a.toString(16).padStart(8, "0")}${b.toString(16).padStart(8, "0")}`; -}; - -const readingKey = (reading: SlotReading): string => - canonical({ - assertion: reading.assertion.assertion, - precision: reading.assertion.precision ?? null, - sourceRegime: reading.assertion.sourceRegime ?? null, - }); - -const preferredStatus = (readings: readonly SlotReading[]): EpistemicStatus => - readings.find((reading) => reading.status === "explicit")?.status ?? - readings[0]!.status; - -const settleSlot = ( - readings: readonly SlotReading[], - conflictedCaptureIds: ReadonlySet<string>, -): SlotState => { - const captureIds = readings.map((reading) => reading.captureId); - if (readings.some((reading) => conflictedCaptureIds.has(reading.captureId))) { - return { state: "conflict", readings, captureIds }; - } - const distinct = new Map<string, SlotReading>(); - for (const reading of readings) { - if (!distinct.has(readingKey(reading))) { - distinct.set(readingKey(reading), reading); - } - } - if (distinct.size > 1) { - const prescribed = readings.filter( - (reading) => reading.assertion.sourceRegime === "prescribed", - ); - const practiced = readings.filter( - (reading) => reading.assertion.sourceRegime === "practiced", - ); - if ( - prescribed.length === 1 && - practiced.length === 1 && - readings.length === 2 - ) { - return { - state: "divergence", - prescribed: prescribed[0]!, - practiced: practiced[0]!, - captureIds, - }; - } - return { state: "conflict", readings, captureIds }; - } - const [first] = readings; - const status = preferredStatus(readings); - const evidenced = readings.some((reading) => reading.evidenced); - const { assertion } = first!; - if ("absence" in assertion.assertion) { - return { - state: "absence", - absence: assertion.assertion.absence, - ...(assertion.assertion.pointer === undefined - ? {} - : { pointer: assertion.assertion.pointer }), - status, - evidenced, - captureIds, - }; - } - return { - state: "value", - value: assertion.assertion.value, - // The schema requires a precision word on every value. - precision: assertion.precision!, - status, - evidenced, - ...(assertion.sourceRegime === undefined - ? {} - : { sourceRegime: assertion.sourceRegime }), - ...(assertion.rationale === undefined - ? {} - : { rationale: assertion.rationale }), - captureIds, - }; -}; - -const isEvidenced = (capture: CaptureEnvelope): boolean => - "evidence" in capture && capture.evidence.length > 0; - -/** Fold the active captures of one snapshot into the model a plugin definition describes. */ -export function foldElicitedModel( - snapshot: CaptureStoreSnapshot, - definition: PluginDefinition, -): ElicitedModel { - const assertionSchema = createSlotAssertionSchema(definition); - const active = snapshot.captures.filter( - (capture) => deriveCaptureStatus(snapshot, capture.id) === "active", - ); - const activeCaptureIds = new Set(active.map((capture) => capture.id)); - const openConflictIssues = snapshot.issues.filter( - (issue) => - issue.type === "conflicting" && - deriveIssueStatus(snapshot, issue.id) === "open", - ); - const conflictedCaptureIds = new Set( - openConflictIssues.flatMap((issue) => issue.references), - ); - - const unmapped: UnmappedCapture[] = []; - const readingsByNode = new Map< - string, - { kind: string; name: string; slots: Map<string, SlotReading[]> } - >(); - - for (const capture of active) { - if ("absence" in capture.content) { - unmapped.push({ - captureId: capture.id, - reason: - "An envelope-level absence carries no kind, node, or slot; record absences inside a slot assertion.", - }); - continue; - } - const parsed = v.safeParse(assertionSchema, capture.content.value); - if (!parsed.success) { - unmapped.push({ - captureId: capture.id, - reason: parsed.issues.map((issue) => issue.message).join(" "), - }); - continue; - } - const { output: assertion } = parsed; - const id = nodeId(assertion.kind, assertion.node); - const node = readingsByNode.get(id) ?? { - kind: assertion.kind, - name: assertion.node, - slots: new Map<string, SlotReading[]>(), - }; - readingsByNode.set(id, node); - const readings = node.slots.get(assertion.slot) ?? []; - readings.push({ - captureId: capture.id, - status: capture.epistemicStatus, - evidenced: isEvidenced(capture), - assertion, - }); - node.slots.set(assertion.slot, readings); - } - - const nodes: ElicitedNode[] = [...readingsByNode.entries()] - .sort(([a], [b]) => a.localeCompare(b)) - .map(([id, node]) => ({ - id, - kind: node.kind, - name: node.name, - slots: Object.fromEntries( - [...node.slots.entries()] - .sort(([a], [b]) => a.localeCompare(b)) - .map(([slot, readings]) => [ - slot, - settleSlot(readings, conflictedCaptureIds), - ]), - ), - })); - - const revision = digest( - canonical({ - active: [...activeCaptureIds].sort(), - conflicts: openConflictIssues.map((issue) => issue.id).sort(), - plugin: definition.version, - }), - ); - - return { - pluginVersion: definition.version, - revision, - nodes, - unmapped, - activeCaptureIds, - }; -} - -/** The node with this id, if the model has it. */ -export const findNode = ( - model: ElicitedModel, - id: string, -): ElicitedNode | undefined => model.nodes.find((node) => node.id === id); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/capture-store.ts b/libs/@hashintel/brunch-agent/packages/core/src/evidence/capture-store.ts similarity index 99% rename from libs/@hashintel/brunch-agent/packages/core/src/capture-store.ts rename to libs/@hashintel/brunch-agent/packages/core/src/evidence/capture-store.ts index ec9206b8d5f..c5441a2514c 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/capture-store.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/evidence/capture-store.ts @@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto"; import * as v from "valibot"; -import { JsonValueSchema } from "./json-value"; +import { JsonValueSchema } from "../json-value"; import { EvidenceQuoteSchema, resolveEvidenceQuotes, @@ -13,10 +13,10 @@ import { type SessionLogArchive, } from "./session-log"; -import type { JsonValue } from "./json-value"; -import type { ReadonlyDeep } from "./readonly-deep"; +import type { JsonValue } from "../json-value"; +import type { ReadonlyDeep } from "../readonly-deep"; -export type { JsonValue } from "./json-value"; +export type { JsonValue } from "../json-value"; export const ABSENCE_STATES = [ "unknown-to-user", diff --git a/libs/@hashintel/brunch-agent/packages/core/src/session-log.ts b/libs/@hashintel/brunch-agent/packages/core/src/evidence/session-log.ts similarity index 98% rename from libs/@hashintel/brunch-agent/packages/core/src/session-log.ts rename to libs/@hashintel/brunch-agent/packages/core/src/evidence/session-log.ts index 434841d9e2a..7e8af9e5205 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/session-log.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/evidence/session-log.ts @@ -1,10 +1,10 @@ import * as v from "valibot"; -import { JsonValueSchema, isJsonValue } from "./json-value"; +import { JsonValueSchema, isJsonValue } from "../json-value"; +import type { JsonValue } from "../json-value"; +import type { ReadonlyDeep } from "../readonly-deep"; import type { EvidenceSpan } from "./capture-store"; -import type { JsonValue } from "./json-value"; -import type { ReadonlyDeep } from "./readonly-deep"; export const SESSION_ENTRY_KINDS = [ "user", diff --git a/libs/@hashintel/brunch-agent/packages/core/src/flue.ts b/libs/@hashintel/brunch-agent/packages/core/src/flue.ts new file mode 100644 index 00000000000..cb020aaf9e0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/src/flue.ts @@ -0,0 +1,23 @@ +import { useModel, useSkill } from "@flue/runtime"; + +import systemPrompt from "./prompts/SYSTEM.md?raw"; +import { + ELICITATION_SKILL_NAME, + elicitationSkill, +} from "./skills/elicitation/skill"; +import { skillFromMarkdown } from "./skills/skill-markdown"; + +/** + * Mount the contributions owned by Brunch core and return its system prompt. + * + * Core contributes the always-on universal prompt and one `elicitation` + * capability skill. It owns no model-facing tool; add one here only when it + * applies independently of the selected modelling formalism and host. + */ +export function useBrunchAgent(model: string): string { + useModel(model); + useSkill(elicitationSkill); + return systemPrompt.replace(/^\s+|\s+$/gu, ""); +} + +export { ELICITATION_SKILL_NAME, elicitationSkill, skillFromMarkdown }; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/index.ts b/libs/@hashintel/brunch-agent/packages/core/src/index.ts index 113bb307682..f4021f55ef0 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/index.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/index.ts @@ -1,20 +1,25 @@ /** * `@hashintel/brunch-agent` — the harness. * - * Mechanism and orchestration: the conversation loop, the ask API, the capture - * envelope, the issue queue, sweep bookkeeping. Its public export surface *is* - * the plugin SDK (spec §12.2). + * Active authority: tool naming, the harness reply-event contract, and the + * evidence layer (capture store and archived session log) that the mechanical + * capture sweep writes through the binding. The ask/affordance and settlement + * protocols are compiled but suspended under `src/_suspended/`; they are + * re-exported here only for the contracts other packages still type against. + * The retired YAML plugin definition, repertoire, and typed interpretation + * machinery were removed on 2026-09-02. * - * **The harness imports no substrate.** A binding imports both this package and - * its substrate; plugins resolve this package only. That direction is enforced - * mechanically — see `test/boundaries.test.ts` at the repo root. + * The substrate-neutral SDK remains on this main export. The `./flue` subpath + * owns the production agent-runtime contribution; plugins may likewise expose + * Flue-native resources while depending inward on this package. That direction + * is enforced mechanically in the architecture tests. */ export { AskInput, FreeTextAffordance, type FreeTextAffordance as FreeTextAffordanceValue, -} from "./affordance"; +} from "./_suspended/conversation/affordance"; export { ASK_TOOL_DESCRIPTION, askAffordanceId, @@ -28,119 +33,19 @@ export { type AskReplyAdmission, type PendingAffordanceDecision, type ReplyBindingSignalPayload, -} from "./ask-protocol"; +} from "./_suspended/conversation/ask-protocol"; export { OPERATIONS, PRODUCT_NAME, toolName, toolPrefix, type Operation, -} from "./naming"; +} from "./conversation/naming"; export { type HarnessReplyEvent, type ReplyPartKind, type ToolExecution, -} from "./reply-protocol"; -export { - definePlugin, - PluginDescriptor, - type Plugin, - type PluginProposalType, -} from "./plugin"; -export { - GUIDANCE_KEY_DESCRIPTIONS, - GUIDANCE_KEYS, - JOB_TITLES, - JOBS, - MOVEMENTS, - RUNBOOK_KEY_DESCRIPTIONS, - RUNBOOK_KEYS, - type GuidanceKey, - type Job, - type KeyDescription, - type MechanismType, - type Movement, - type RunbookKey, -} from "./keys"; -export { - guidanceEntries, - GuidanceCellsSchema, - GuidanceItemSchema, - mustKnowRowsFor, - PluginDefinitionError, - PluginDefinitionSchema, - PRECISION_LADDER, - PRECISION_WORDS, - readPluginDefinition, - readYamlAs, - runbookEntries, - RunbookCellsSchema, - type Anchor, - type AttributeNote, - type FloorRow, - type GuidanceCells, - type GuidanceItem, - type KindRow, - type MovementCells, - type MustKnowRow, - type NamedText, - type PatternRow, - type PluginDefinition, - type PluginDefinitionInput, - type PrecisionDemand, - type PrecisionWord, - type ProposalDeclaration, - type RunbookCells, -} from "./plugin-definition"; -export { - readRepertoire, - RepertoireSchema, - type Repertoire, -} from "./repertoire"; -export { - HARNESS_PREAMBLE, - renderContract, - renderGuidance, - renderInstructions, - renderRunbook, -} from "./instructions"; -export { - createSlotAssertionSchema, - nodeId, - slotAssertionExtractionGuidance, - SlotAssertionSchema, - SOURCE_REGIMES, - type SlotAssertion, - type SourceRegime, -} from "./slot-assertion"; -export { - findNode, - foldElicitedModel, - type ElicitedModel, - type ElicitedNode, - type SlotReading, - type SlotState, - type UnmappedCapture, -} from "./elicited-model"; -export { - COMPLETION_DIAGNOSTICS, - completionDemands, - evaluateCompletion, - precisionSatisfies, - type CompletionAnchor, - type CompletionDemands, - type CompletionDiagnostic, - type CompletionFailure, - type CompletionReport, - type OutsideSliceNode, -} from "./completion"; -export { - buildCompletionCueSignal, - buildSweepList, - type CompletionCueSignal, - type PatternCue, - type SweepList, -} from "./cue"; +} from "./conversation/reply-protocol"; export { ABSENCE_STATES, CaptureInputProposalSchema, @@ -174,7 +79,7 @@ export { type IssueOrigin, type IssueType, type JsonValue, -} from "./capture-store"; +} from "./evidence/capture-store"; export { EvidenceQuoteSchema, SESSION_ENTRY_KINDS, @@ -185,7 +90,7 @@ export { type EvidenceResolutionResult, type MultipleEvidenceMatchesAdvisory, type SessionEntryKind, -} from "./session-log"; +} from "./evidence/session-log"; export { SWEEP_RESULT_STATUSES, advanceSweepHighWater, @@ -193,7 +98,6 @@ export { buildSweepExtractionPrompt, buildSweepRepairSignal, computeUnaccountedAskAdvisories, - createSweepExtractionResultSchema, createInitialSweepState, decideSettlementTrigger, parseSweepState, @@ -205,11 +109,10 @@ export { type SettlementCheckSignal, type SettlementTriggerDecision, type SweepAffordance, - type SweepExtraction, type SweepRepairSignal, type SweepRefusalFact, type SweepResultFact, type SweepSessionEntry, type SweepState, type UnaccountedAskAdvisory, -} from "./sweep-protocol"; +} from "./_suspended/conversation/sweep-protocol"; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/instructions.ts b/libs/@hashintel/brunch-agent/packages/core/src/instructions.ts deleted file mode 100644 index 933da83684e..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/instructions.ts +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Rendering the interviewer's instructions from the repertoire and a plugin - * definition (ADR-0007 decision 1): key by key, the key's definition, then the - * harness default, then the plugin's cell if it is not blank. - * - * The fixed harness preamble states what the harness itself enforces — - * completion, the sweep list, the assumption ledger, the affected slice — so - * that no plugin cell has to. Everything else the interviewer reads about the - * formalism comes from the definition's contract keys, rendered as text here - * because the model reads text; the same data parameterises the fold and the - * completion evaluation elsewhere. - */ - -import { - GUIDANCE_KEY_DESCRIPTIONS, - GUIDANCE_KEYS, - JOB_TITLES, - MOVEMENTS, - RUNBOOK_KEY_DESCRIPTIONS, - RUNBOOK_KEYS, - type Job, -} from "./keys"; -import { - formatPrecisionDemand, - PRECISION_LADDER, - type GuidanceItem, - type PluginDefinition, - type PrecisionWord, -} from "./plugin-definition"; -import { type Repertoire } from "./repertoire"; - -/** - * What the harness enforces, stated once. These are facts about mechanism the - * interviewer must know and no plugin may restate. - */ -export const HARNESS_PREAMBLE: readonly string[] = [ - "The harness keeps the model, not you. Every value it holds comes from a capture you made from the expert's words; you never edit the model, you add captures, and a later capture supersedes an earlier one.", - "After each applied sweep the harness folds the active captures into the model and reports which demanded slots are unsatisfied and why, with the patterns whose trigger may apply. Read it as a map of what is still unknown, not as an instruction to ask.", - "A slot is satisfied only by what the expert said or confirmed, at the precision the row demands. Never state a value the expert did not give; record what you would assume in the assumption ledger and ask.", - "Completion is computed from the model by the harness — the floor, then every node in the dependency slice of every active anchor. Whether the session may stop is the harness's decision; yours is to say what the model can now support and what it cannot.", - "For the review-and-revise job the harness computes the affected slice — the node, its slots, every anchor whose slice contains it, and what those project to — and nothing outside it changes.", -]; - -const renderItems = (items: readonly GuidanceItem[]): string[] => - items.map((item) => { - const signature = - item.signature === undefined ? "" : ` _Signature:_ ${item.signature}`; - return `- **${item.name}** — ${item.text.trim()}${signature}`; - }); - -const demandedPrecisions = ( - definition: PluginDefinition, -): ReadonlySet<PrecisionWord> => { - const words = new Set<PrecisionWord>(); - for (const row of definition.mustKnow) { - if (row.precision.kind === "word") { - words.add(row.precision.word); - } else if (row.precision.kind === "any-of") { - for (const word of row.precision.words) { - words.add(word); - } - } - } - return words; -}; - -const applicableRepertoireItems = ( - items: readonly GuidanceItem[], - precisions: ReadonlySet<PrecisionWord>, -): GuidanceItem[] => - items.filter( - (item) => - item.forPrecision === undefined || - item.forPrecision.some((precision) => precisions.has(precision)), - ); - -const paragraphs = (...parts: (string | undefined)[]): string[] => - parts.flatMap((part) => - part === undefined || part.trim() === "" ? [] : [part.trim()], - ); - -/** The contract keys as text: purpose, kinds, rows, floor, anchor, patterns. */ -export const renderContract = (definition: PluginDefinition): string[] => { - const kinds = definition.kinds.map( - (row) => - `- \`${row.kind}\` — ${row.description.trim()} _Projects to:_ ${row.projectsTo}.`, - ); - const notKinds = definition.ontology.notKinds.map( - (entry) => `- **${entry.name}** — ${entry.text.trim()}`, - ); - const attributes = definition.ontology.attributes.map((entry) => { - const values = - entry.values === undefined - ? "" - : ` (${entry.values.map((value) => `\`${value}\``).join(" | ")})`; - return `- **${entry.name}**${values}, on ${entry.on} — ${entry.text.trim()}`; - }); - const rows = definition.kinds.map((kindRow) => { - const own = definition.mustKnow - .filter((row) => row.kind === kindRow.kind) - .map( - (row) => - ` - ${row.slot} — ${formatPrecisionDemand(row.precision)}${row.notApplicableAllowed ? '; "not applicable" is accepted' : ""}. _Why:_ ${row.why}`, - ); - return [`- \`${kindRow.kind}\``, ...own].join("\n"); - }); - const floor = definition.floor - .map((row) => `${row.atLeast} \`${row.kind}\``) - .join(", "); - const ladder = Object.entries(PRECISION_LADDER).map( - ([word, meaning]) => `- \`${word}\` — ${meaning}`, - ); - const patterns = definition.patterns.map( - (row) => - `- **${row.id}** — _when_ ${row.when.trim()} — _ask_ ${row.ask.trim()}`, - ); - return [ - `## Purpose\n\n${definition.identity.purpose.trim()}`, - [ - "## Kinds", - ...paragraphs(definition.ontology.preamble), - kinds.join("\n"), - ...(notKinds.length === 0 - ? [] - : [ - `Things that look like kinds and are not:\n\n${notKinds.join("\n")}`, - ]), - ...(attributes.length === 0 - ? [] - : [`Attributes on every kind:\n\n${attributes.join("\n")}`]), - ].join("\n\n"), - [ - "## Must know", - ...paragraphs(definition.schemaPreamble), - rows.join("\n"), - `Static floor — before anything \`${definition.anchor.kind}\`-relative counts, the model must contain at least ${floor}. Presence is a count; the floor assigns no precision.`, - `Anchor — completion is relative to \`${definition.anchor.kind}\` nodes: the model is complete when the floor holds and every node named in each active anchor's "${definition.anchor.dependencySlot}" satisfies its kind's rows. Nodes outside every slice are recorded, not demanded.`, - `Precision words:\n\n${ladder.join("\n")}\n\nPrecision says how much a value narrows what it could mean, not where it came from; an honest value at the wrong precision and an invented value at the right one are tracked separately and neither substitutes for the other.`, - ].join("\n\n"), - ...(definition.patterns.length === 0 - ? [] - : [ - [ - "## Patterns", - ...paragraphs(definition.patternsPreamble), - patterns.join("\n"), - ].join("\n\n"), - ]), - ]; -}; - -/** The guidance keys, interleaved: definition, repertoire default, plugin cell. */ -export const renderGuidance = ( - repertoire: Repertoire, - definition: PluginDefinition, -): string[] => { - const precisions = demandedPrecisions(definition); - return GUIDANCE_KEYS.map((key) => { - const description = GUIDANCE_KEY_DESCRIPTIONS[key]; - const body = - key === "movements" - ? MOVEMENTS.flatMap((movement) => [ - `### ${movement === "slice" ? "Slice" : "Sweep"}`, - [ - ...renderItems( - applicableRepertoireItems( - repertoire.guidance.movements[movement], - precisions, - ), - ), - ...renderItems(definition.guidance.movements[movement]), - ].join("\n"), - ]) - : [ - [ - ...renderItems( - applicableRepertoireItems(repertoire.guidance[key], precisions), - ), - ...renderItems(definition.guidance[key]), - ].join("\n"), - ]; - return [`## ${description.title}`, `_${description.definition}_`, ...body] - .filter((part) => part !== "") - .join("\n\n"); - }); -}; - -/** One job's runbook: each runbook key's definition, default, and plugin cell. */ -export const renderRunbook = ( - repertoire: Repertoire, - definition: PluginDefinition, - job: Job, -): string => { - const cells = definition.runbooks[job]; - const precisions = demandedPrecisions(definition); - const sections = RUNBOOK_KEYS.map((key) => { - const description = RUNBOOK_KEY_DESCRIPTIONS[key]; - return [ - `### ${description.title}`, - `_${description.definition}_`, - [ - ...renderItems( - applicableRepertoireItems(repertoire.runbooks[job][key], precisions), - ), - ...renderItems(cells?.[key] ?? []), - ].join("\n"), - ].join("\n\n"); - }); - return [`## ${JOB_TITLES[job]}`, ...sections].join("\n\n"); -}; - -/** - * The whole instruction text for one plugin under one repertoire, in contract - * order: harness preamble, contract, guidance, one runbook per supported job. - */ -export const renderInstructions = ( - repertoire: Repertoire, - definition: PluginDefinition, -): string => - [ - `## What the harness enforces\n\n${HARNESS_PREAMBLE.join("\n\n")}`, - ...renderContract(definition), - ...renderGuidance(repertoire, definition), - ...definition.identity.jobs.map((job) => - renderRunbook(repertoire, definition, job), - ), - ].join("\n\n"); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/keys.ts b/libs/@hashintel/brunch-agent/packages/core/src/keys.ts deleted file mode 100644 index cd1108137c5..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/keys.ts +++ /dev/null @@ -1,146 +0,0 @@ -/** - * The keys of plugin authoring (ADR-0007). - * - * Every key is owned by the harness: the harness defines the concept the key - * names, teaches it through the repertoire's default, and a plugin specialises - * it in a cell written in the harness's terms. This file is the catalogue — - * which keys exist, in which group, working through which mechanism, and the - * one-paragraph definition the interviewer reads above every rendered key. - * - * The catalogue is a working set until a co-authoring cycle changes no key - * (ADR-0007 decision 9). Changes are recorded in `schema/CHANGELOG.md`, beside - * the JSON schema derived from `plugin-definition.ts`. - */ - -/** The jobs the harness names without any plugin (ADR-0007 decision 4). */ -export const JOBS = ["construct", "review-and-revise"] as const; -export type Job = (typeof JOBS)[number]; - -/** Guidance keys, in the order they render. Each works through one mechanism. */ -export const GUIDANCE_KEYS = [ - "lenses", - "techniques", - "movements", - "licenses", - "motifs", - "smells", - "rabbit_holes", - "failure_modes", -] as const; -export type GuidanceKey = (typeof GUIDANCE_KEYS)[number]; - -/** The two movements a `movements` cell distinguishes. */ -export const MOVEMENTS = ["slice", "sweep"] as const; -export type Movement = (typeof MOVEMENTS)[number]; - -/** Runbook keys — the only keys that carry procedure — in the order they render. */ -export const RUNBOOK_KEYS = ["kickoff", "trajectory", "close"] as const; -export type RunbookKey = (typeof RUNBOOK_KEYS)[number]; - -/** - * How a guidance key works on the interviewer (ADR-0007 decision 3): a license - * permits a move a cooperative model suppresses; a technique supplies a method - * the model does not reliably apply; attention points native ability at a - * target; an anchor holds leading words for judgment. - */ -export type MechanismType = "license" | "technique" | "attention" | "anchor"; - -export interface KeyDescription { - readonly key: GuidanceKey | RunbookKey; - readonly title: string; - readonly mechanism: MechanismType | "procedure"; - /** What the harness defines the key to mean — rendered above every key. */ - readonly definition: string; -} - -export const GUIDANCE_KEY_DESCRIPTIONS: Readonly< - Record<GuidanceKey, KeyDescription> -> = { - lenses: { - key: "lenses", - title: "Lenses", - mechanism: "attention", - definition: - "What to attend to in the expert's talk: the interview situations the harness can name — conflict, competing alternatives, ambiguity, weak or missing evidence, clusters of absence, pressure at a choice point — and where the formalism's kinds hide in ordinary speech. A lens says what something looks like when it appears and what to do then; it never says what to ask next.", - }, - techniques: { - key: "techniques", - title: "Techniques", - mechanism: "technique", - definition: - "Question forms that deepen one answer already given. A technique is applied to a thread, one at a time, when the answer in hand is not yet usable; it is never a schedule of questions.", - }, - movements: { - key: "movements", - title: "Movements", - mechanism: "technique", - definition: - "The two shapes a stretch of interview takes. A slice walks one concrete case end to end and is where the model's structure comes from. A sweep makes one property hold across one stratum and is what finds what was never asked. The completion report is the map of what is unknown, never the order to ask in.", - }, - licenses: { - key: "licenses", - title: "Licenses", - mechanism: "license", - definition: - "Moves the interviewer is permitted to make that a cooperative model would otherwise suppress. A license says what is allowed and the limit of the allowance; it never obliges.", - }, - motifs: { - key: "motifs", - title: "Motifs", - mechanism: "attention", - definition: - "Recurring shapes the formalism knows — offered as scaffolds for a question, never as a catalogue to assemble structure from. The interviewer asks whether a motif is present and with what parameters; it never generates a model from the motif.", - }, - smells: { - key: "smells", - title: "Smells", - mechanism: "attention", - definition: - "Signs in the interviewer's own output — not the expert's — that the interview has gone wrong. Each names what to look for in what was just said or recorded.", - }, - rabbit_holes: { - key: "rabbit_holes", - title: "Rabbit holes", - mechanism: "anchor", - definition: - "Where not to dig, and what looks like progress and is not. Anti-guidance, kept here so that every other key can be stated positively.", - }, - failure_modes: { - key: "failure_modes", - title: "Failure modes", - mechanism: "anchor", - definition: - "Named ways an interview of this kind fails, each with the signature by which it is detected. The failures this guidance exists to prevent; read them as judgments to check against, not as rules.", - }, -}; - -export const RUNBOOK_KEY_DESCRIPTIONS: Readonly< - Record<RunbookKey, KeyDescription> -> = { - kickoff: { - key: "kickoff", - title: "Kickoff", - mechanism: "procedure", - definition: - "What to establish before any structure, and how. Kickoff produces a posture — the stance the rest of the interview takes from the expert's time, intended use, required confidence, and tolerance for proposed assumptions. It is a form the interviewer fills implicitly, never an opening battery of questions.", - }, - trajectory: { - key: "trajectory", - title: "Trajectory", - mechanism: "procedure", - definition: - "Which movements in which bias, varied by posture. Stated as postures the interviewer moves between, never as a state machine; the interviewer chooses among what applies.", - }, - close: { - key: "close", - title: "Close", - mechanism: "procedure", - definition: - "How to end honestly. Completion is computed by the harness from the model, never felt from the conversation; whether a session may stop is the harness's decision, not this key's. Close says what to say and deliver when the interview ends, complete or not.", - }, -}; - -export const JOB_TITLES: Readonly<Record<Job, string>> = { - construct: "Job: construct — no model exists", - "review-and-revise": "Job: review and revise — a model exists", -}; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/plugin-definition.ts b/libs/@hashintel/brunch-agent/packages/core/src/plugin-definition.ts deleted file mode 100644 index e1a8b8ec3e8..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/plugin-definition.ts +++ /dev/null @@ -1,535 +0,0 @@ -/** - * The plugin definition: `plugin.yaml` read under the harness-owned keys - * (ADR-0007 decision 8). - * - * A plugin is data under fixed keys in four groups — contract (`ontology`, - * `schema`, `patterns`), guidance, runbooks, machinery — plus an identity block. - * The schema here is the contract: an unknown key anywhere is rejected, so a - * plugin can specialise every key and add none. The cross-checks below are the - * facts a schema cannot state — that every row names a declared kind, that the - * anchor is a row, that runbooks belong to declared jobs. - * - * `schema/plugin.schema.json` is derived from `PluginDefinitionSchema` and - * published for editors; a test keeps the two identical. - */ - -import * as v from "valibot"; -import { parse as parseYaml } from "yaml"; - -import { - GUIDANCE_KEYS, - JOBS, - MOVEMENTS, - RUNBOOK_KEYS, - type GuidanceKey, - type Job, - type Movement, - type RunbookKey, -} from "./keys"; - -/** Precision words, in ladder order except `spelled out`, which is its own ladder. */ -export const PRECISION_WORDS = [ - "named", - "number", - "range", - "spread", - "spelled out", -] as const; -export type PrecisionWord = (typeof PRECISION_WORDS)[number]; - -/** What a row demands: one or more alternative precision words, or a node count. */ -export type PrecisionDemand = - | { readonly kind: "word"; readonly word: PrecisionWord } - | { readonly kind: "any-of"; readonly words: readonly PrecisionWord[] } - | { readonly kind: "at-least"; readonly count: number }; - -export const formatPrecisionDemand = (demand: PrecisionDemand): string => { - switch (demand.kind) { - case "word": - return demand.word; - case "any-of": - return demand.words.join(" or "); - case "at-least": - return `at least ${demand.count}`; - } -}; - -/** What each precision word means; harness vocabulary, rendered for every plugin. */ -export const PRECISION_LADDER: Readonly< - Record<PrecisionWord | "at least N", string> -> = { - named: "identified in words", - number: "a single figure with its unit", - range: "an ordinary low and high", - spread: - 'range plus "typical", plus one-in-ten worse and one-in-ten better (or median and quartiles)', - "spelled out": - "the rule, pattern, list, or structure itself, in a form a second reader could apply without asking", - "at least N": "a count of nodes present", -}; - -export interface KindRow { - readonly kind: string; - readonly description: string; - readonly projectsTo: string; -} - -export interface MustKnowRow { - readonly kind: string; - readonly slot: string; - readonly precision: PrecisionDemand; - readonly notApplicableAllowed: boolean; - readonly why: string; -} - -export interface FloorRow { - readonly kind: string; - readonly atLeast: number; -} - -export interface PatternRow { - readonly id: string; - readonly when: string; - readonly ask: string; - /** The kinds whose nodes can trigger it; empty means any node. */ - readonly kinds: readonly string[]; - /** When present, the pattern applies only while this demanded slot fails. */ - readonly slot?: string; -} - -/** The completion anchor, declared: the kind whose dependency slot is the slice. */ -export interface Anchor { - readonly kind: string; - readonly dependencySlot: string; -} - -/** One entry in a guidance or runbook cell. */ -export interface GuidanceItem { - readonly name: string; - readonly text: string; - /** Repertoire-only applicability; omitted entries apply to every plugin. */ - readonly forPrecision?: readonly PrecisionWord[]; - /** For failure modes: how the failure is detected. */ - readonly signature?: string; - /** Where the entry comes from; required of the repertoire, optional for a plugin. */ - readonly source?: string; -} - -export interface MovementCells { - readonly slice: readonly GuidanceItem[]; - readonly sweep: readonly GuidanceItem[]; -} - -export type GuidanceCells = { - readonly [K in Exclude<GuidanceKey, "movements">]: readonly GuidanceItem[]; -} & { readonly movements: MovementCells }; - -export type RunbookCells = { - readonly [K in RunbookKey]: readonly GuidanceItem[]; -}; - -export interface NamedText { - readonly name: string; - readonly text: string; -} - -export interface AttributeNote extends NamedText { - readonly on: string; - readonly values?: readonly string[]; -} - -export interface ProposalDeclaration { - readonly type: string; - readonly payload: string; -} - -/** The read model of one `plugin.yaml`. */ -export interface PluginDefinition { - readonly version: string; - readonly identity: { - readonly id: string; - readonly formalism: string; - readonly jobs: readonly Job[]; - readonly purpose: string; - }; - readonly kinds: readonly KindRow[]; - readonly ontology: { - readonly preamble?: string; - readonly notKinds: readonly NamedText[]; - readonly attributes: readonly AttributeNote[]; - }; - readonly anchor: Anchor; - readonly floor: readonly FloorRow[]; - readonly mustKnow: readonly MustKnowRow[]; - readonly proposals: readonly ProposalDeclaration[]; - readonly schemaPreamble?: string; - readonly patterns: readonly PatternRow[]; - readonly patternsPreamble?: string; - readonly guidance: GuidanceCells; - readonly runbooks: Partial<Record<Job, RunbookCells>>; - readonly machinery: { - readonly checks: readonly string[]; - readonly tools: readonly string[]; - }; -} - -export class PluginDefinitionError extends Error { - constructor(message: string) { - super(message); - this.name = "PluginDefinitionError"; - } -} - -// ── The schema ────────────────────────────────────────────────────────────── - -const text = v.pipe(v.string(), v.nonEmpty()); -const identifier = v.pipe(v.string(), v.regex(/^[a-z][a-z0-9-]*$/u)); -const version = v.pipe( - v.string(), - v.regex( - /^[a-z][a-z0-9-]*\/\d{4}-\d{2}-\d{2}\.\d+$/u, - "expected `<id>/<yyyy-mm-dd>.<n>`", - ), -); -const precisionWord = v.picklist(PRECISION_WORDS); -const precision = v.union([ - precisionWord, - v.pipe(v.array(precisionWord), v.minLength(1)), - v.pipe(v.string(), v.regex(/^at least [1-9]\d*$/u)), -]); - -export const GuidanceItemSchema = v.strictObject({ - name: text, - text, - signature: v.optional(text), - source: v.optional(text), -}); -const items = v.array(GuidanceItemSchema); - -export const GuidanceCellsSchema = v.strictObject({ - lenses: items, - techniques: items, - movements: v.strictObject({ slice: items, sweep: items }), - licenses: items, - motifs: items, - smells: items, - rabbit_holes: items, - failure_modes: items, -}); - -export const RunbookCellsSchema = v.strictObject({ - kickoff: items, - trajectory: items, - close: items, -}); - -const namedText = v.strictObject({ name: text, text }); - -export const PluginDefinitionSchema = v.strictObject({ - plugin: v.strictObject({ - id: identifier, - version, - formalism: text, - jobs: v.pipe(v.array(v.picklist(JOBS)), v.minLength(1)), - purpose: text, - }), - ontology: v.strictObject({ - preamble: v.optional(text), - kinds: v.pipe( - v.array(v.strictObject({ kind: text, is: text, projects_to: text })), - v.minLength(1), - ), - not_kinds: v.optional(v.array(namedText)), - attributes: v.optional( - v.array( - v.strictObject({ - name: text, - on: text, - values: v.optional(v.array(text)), - text, - }), - ), - ), - }), - schema: v.strictObject({ - preamble: v.optional(text), - anchor: v.strictObject({ kind: text, depends_on: text }), - floor: v.array( - v.strictObject({ - kind: text, - at_least: v.pipe(v.number(), v.integer(), v.minValue(1)), - }), - ), - must_know: v.pipe( - v.array( - v.strictObject({ - kind: text, - slot: text, - precision, - not_applicable: v.boolean(), - why: text, - }), - ), - v.minLength(1), - ), - proposals: v.pipe( - v.array(v.strictObject({ type: identifier, payload: identifier })), - v.minLength(1), - ), - }), - patterns: v.strictObject({ - preamble: v.optional(text), - items: v.array( - v.strictObject({ - id: v.pipe(v.string(), v.regex(/^P\d{2}$/u)), - on: v.array(text), - slot: v.optional(text), - when: text, - ask: text, - }), - ), - }), - guidance: GuidanceCellsSchema, - runbooks: v.strictObject({ - construct: v.optional(RunbookCellsSchema), - "review-and-revise": v.optional(RunbookCellsSchema), - }), - machinery: v.strictObject({ - checks: v.array(identifier), - tools: v.array(identifier), - }), -}); - -export type PluginDefinitionInput = v.InferInput<typeof PluginDefinitionSchema>; - -// ── The reader ────────────────────────────────────────────────────────────── - -const parsePrecision = ( - input: v.InferOutput<typeof precision>, -): PrecisionDemand => { - if (Array.isArray(input)) { - return { kind: "any-of", words: input }; - } - const atLeast = /^at least (\d+)$/u.exec(input); - if (atLeast?.[1] !== undefined) { - return { kind: "at-least", count: Number(atLeast[1]) }; - } - return { kind: "word", word: input as PrecisionWord }; -}; - -const fail = (message: string): never => { - throw new PluginDefinitionError(message); -}; - -const formatIssues = (issues: readonly v.BaseIssue<unknown>[]): string => - issues - .map((issue) => { - const path = (issue.path ?? []) - .map((segment) => String(segment.key)) - .join("."); - return `${path || "<root>"}: ${issue.message}`; - }) - .join("; "); - -/** Parse and validate a YAML document as an object of the given schema. */ -export const readYamlAs = <T extends v.GenericSchema>( - schema: T, - yamlText: string, - what: string, -): v.InferOutput<T> => { - let document: unknown; - try { - document = parseYaml(yamlText); - } catch (error) { - return fail(`${what} is not valid YAML: ${String(error)}`); - } - const result = v.safeParse(schema, document); - if (!result.success) { - return fail( - `${what} does not match its schema — ${formatIssues(result.issues)}`, - ); - } - return result.output; -}; - -/** - * Read one `plugin.yaml`. Fails loudly, at load, on a schema violation or a - * cross-reference the schema cannot express. - */ -export function readPluginDefinition(yamlText: string): PluginDefinition { - const input = readYamlAs( - PluginDefinitionSchema, - yamlText, - "the plugin definition", - ); - - const kindNames = input.ontology.kinds.map((row) => row.kind); - const kinds = new Set(kindNames); - if (kinds.size !== kindNames.length) { - fail("`ontology.kinds` repeats a kind"); - } - const knownKind = (kind: string, where: string): void => { - if (!kinds.has(kind)) { - fail( - `${where} names kind \`${kind}\`, which is not in \`ontology.kinds\``, - ); - } - }; - - const mustKnow: MustKnowRow[] = input.schema.must_know.map((row) => { - knownKind(row.kind, "`schema.must_know`"); - return { - kind: row.kind, - slot: row.slot, - precision: parsePrecision(row.precision), - notApplicableAllowed: row.not_applicable, - why: row.why, - }; - }); - for (const kind of kindNames) { - if (!mustKnow.some((row) => row.kind === kind)) { - fail(`\`schema.must_know\` has no row for kind \`${kind}\``); - } - } - - const floor: FloorRow[] = input.schema.floor.map((row) => { - knownKind(row.kind, "`schema.floor`"); - return { kind: row.kind, atLeast: row.at_least }; - }); - if (new Set(floor.map((row) => row.kind)).size !== floor.length) { - fail("`schema.floor` repeats a kind"); - } - - const { anchor } = input.schema; - knownKind(anchor.kind, "`schema.anchor`"); - const anchorRow = mustKnow.find( - (row) => row.kind === anchor.kind && row.slot === anchor.depends_on, - ); - if (anchorRow === undefined) { - fail( - `\`schema.anchor\` names slot \`${anchor.depends_on}\` on \`${anchor.kind}\`, which is not a \`must_know\` row`, - ); - } else if (anchorRow.precision.kind !== "at-least") { - fail("the anchor's dependency slot must demand `at least N`"); - } - - const patternIds = input.patterns.items.map((row) => row.id); - if (new Set(patternIds).size !== patternIds.length) { - fail("`patterns.items` repeats an id"); - } - const patterns: PatternRow[] = input.patterns.items.map((row) => { - for (const kind of row.on) knownKind(kind, `pattern ${row.id}`); - if (row.slot !== undefined) { - if ( - row.on.length === 0 && - !mustKnow.some((demand) => demand.slot === row.slot) - ) { - fail( - `pattern ${row.id} names slot \`${row.slot}\`, which no kind demands`, - ); - } - const kindWithoutSlot = row.on.find( - (kind) => - !mustKnow.some( - (demand) => demand.kind === kind && demand.slot === row.slot, - ), - ); - if (kindWithoutSlot !== undefined) { - fail( - `pattern ${row.id} names slot \`${row.slot}\`, which \`${kindWithoutSlot}\` does not demand`, - ); - } - } - return { - id: row.id, - when: row.when, - ask: row.ask, - kinds: row.on, - ...(row.slot === undefined ? {} : { slot: row.slot }), - }; - }); - - const jobs = input.plugin.jobs; - if (new Set(jobs).size !== jobs.length) fail("`plugin.jobs` repeats a job"); - const runbooks: Partial<Record<Job, RunbookCells>> = {}; - for (const job of JOBS) { - const cells = input.runbooks[job]; - if (cells === undefined) continue; - if (!jobs.includes(job)) { - fail( - `\`runbooks.${job}\` is present but \`plugin.jobs\` does not declare it`, - ); - } - runbooks[job] = cells; - } - - return { - version: input.plugin.version, - identity: { - id: input.plugin.id, - formalism: input.plugin.formalism, - jobs, - purpose: input.plugin.purpose, - }, - kinds: input.ontology.kinds.map((row) => ({ - kind: row.kind, - description: row.is, - projectsTo: row.projects_to, - })), - ontology: { - ...(input.ontology.preamble === undefined - ? {} - : { preamble: input.ontology.preamble }), - notKinds: input.ontology.not_kinds ?? [], - attributes: input.ontology.attributes ?? [], - }, - anchor: { kind: anchor.kind, dependencySlot: anchor.depends_on }, - floor, - mustKnow, - proposals: input.schema.proposals, - ...(input.schema.preamble === undefined - ? {} - : { schemaPreamble: input.schema.preamble }), - patterns, - ...(input.patterns.preamble === undefined - ? {} - : { patternsPreamble: input.patterns.preamble }), - guidance: input.guidance, - runbooks, - machinery: input.machinery, - }; -} - -export const mustKnowRowsFor = ( - definition: PluginDefinition, - kind: string, -): readonly MustKnowRow[] => - definition.mustKnow.filter((row) => row.kind === kind); - -/** Every guidance cell of a definition, flattened with its key path — for gates. */ -export const guidanceEntries = ( - cells: GuidanceCells, -): readonly { readonly path: string; readonly item: GuidanceItem }[] => - GUIDANCE_KEYS.flatMap((key) => - key === "movements" - ? MOVEMENTS.flatMap((movement: Movement) => - cells.movements[movement].map((item) => ({ - path: `movements.${movement}`, - item, - })), - ) - : cells[key].map((item) => ({ path: key, item })), - ); - -/** Every runbook cell of one job, flattened with its key path — for gates. */ -export const runbookEntries = ( - runbooks: Partial<Record<Job, RunbookCells>>, -): readonly { readonly path: string; readonly item: GuidanceItem }[] => - JOBS.flatMap((job) => { - const cells = runbooks[job]; - return cells === undefined - ? [] - : RUNBOOK_KEYS.flatMap((key) => - cells[key].map((item) => ({ path: `${job}.${key}`, item })), - ); - }); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/plugin-json-schema.ts b/libs/@hashintel/brunch-agent/packages/core/src/plugin-json-schema.ts deleted file mode 100644 index 07fa3066796..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/plugin-json-schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * The JSON-schema view of the plugin contract, derived from the valibot schema - * so there is one source of truth. Kept out of the public export surface: it - * is for the snapshot test that emits `schema/plugin.schema.json`, not for plugins. - */ -import { toJsonSchema } from "@valibot/to-json-schema"; - -import { PluginDefinitionSchema } from "./plugin-definition"; - -export const pluginJsonSchema = (): Record<string, unknown> => ({ - $id: "https://hash.ai/brunch-agent/plugin.schema.json", - title: "Brunch plugin definition", - description: - "A plugin is data under harness-owned keys (ADR-0007). Cross-references the schema cannot state — rows name declared kinds, the anchor is a row, runbooks belong to declared jobs — are checked by readPluginDefinition.", - ...toJsonSchema(PluginDefinitionSchema, { errorMode: "ignore" }), -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts b/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts deleted file mode 100644 index fa973a6c8ba..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/plugin.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as v from "valibot"; - -import type { CaptureInputProposal } from "./capture-store"; -import type { PluginDefinition } from "./plugin-definition"; - -/** - * The plugin descriptor — identity only, at this stage. - * - * The plugin's real surface is its packs and the four operations (spec §6.1, - * §11.1). Those are **deliberately absent here**: spec §13's two-targets rule - * says the trivial target must not freeze the plugin contract before the hard - * target has stressed it, so nothing in this scaffold ratifies the SDK export - * surface. What the descriptor fixes now is only what the topology needs — - * that a plugin declares which target formalism it defines, and does so through - * Valibot like every other boundary in the system (spec §12.4). - * - * ADR-0007 adds the plugin definition: `plugin.yaml` under the harness-owned - * keys, whose contract keys parameterise the harness's fold, completion, and - * cue and whose guidance and runbook cells specialise what the repertoire - * teaches. A plugin that carries one is a kind-and-slot plugin. - */ -export const PluginDescriptor = v.object({ - /** Package-level identity, matching the `plugin-*` role prefix (spec §12.2). */ - name: v.pipe( - v.string(), - v.regex(/^plugin-[a-z][a-z0-9-]*$/, "expected a `plugin-<name>` name"), - ), - /** The target formalism this plugin elicits toward — gherkin, sdcpn — never a domain. */ - targetFormalism: v.pipe(v.string(), v.nonEmpty()), -}); - -export interface PluginProposalType { - readonly name: string; - readonly description: string; - readonly schema: v.GenericSchema<unknown, CaptureInputProposal>; -} - -export type Plugin = v.InferOutput<typeof PluginDescriptor> & { - /** FE-1392's declared floor; FE-1393 grows the catalog and SDK around it. */ - readonly proposalCatalog: readonly [PluginProposalType]; - /** The plugin definition (ADR-0007); absent only for a plugin without a model. */ - readonly definition?: PluginDefinition; -}; - -/** - * Declare a plugin. Inversion of control (spec §4): the plugin declares and - * registers; the harness discovers, orders, and invokes. Nothing a plugin - * declares can reach persistence — the storage port is harness-defined and - * binding-implemented, and plugins are storage-blind (spec §9.6). - */ -export function definePlugin(descriptor: Plugin): Plugin { - const identity = v.parse(PluginDescriptor, descriptor); - const [proposal, ...extraProposals] = descriptor.proposalCatalog; - // oxlint-disable-next-line typescript/no-unnecessary-condition -- Public JavaScript callers still require the runtime cardinality guard. - if (!proposal || extraProposals.length > 0) { - throw new TypeError( - "This slice requires exactly one declared proposal type.", - ); - } - const name = v.parse(v.pipe(v.string(), v.nonEmpty()), proposal.name); - const description = v.parse( - v.pipe(v.string(), v.nonEmpty()), - proposal.description, - ); - return { - ...identity, - proposalCatalog: [{ ...proposal, name, description }], - ...(descriptor.definition === undefined - ? {} - : { definition: descriptor.definition }), - }; -} diff --git a/libs/@hashintel/brunch-agent/packages/core/src/prompts.ts b/libs/@hashintel/brunch-agent/packages/core/src/prompts.ts deleted file mode 100644 index d6c1e8f3354..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/prompts.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { readRepertoire } from "./repertoire"; -import repertoireYaml from "./repertoire.yaml?raw"; - -/** - * The harness's validated default teaching. - * - * The repertoire fills every guidance and runbook key before a plugin adds its - * formalism-specific cells. Every entry names its evidence source, and reading - * fails at module load if a key is empty or unsourced. Bindings and evaluation - * composition may import `@hashintel/brunch-agent/prompts`; plugins may not. - * - * @see ADR-0007 for the repertoire contract. - * @see ADR-0008 for its placement in the core package. - */ -export const repertoire = readRepertoire(repertoireYaml); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/prompts/SYSTEM.md b/libs/@hashintel/brunch-agent/packages/core/src/prompts/SYSTEM.md new file mode 100644 index 00000000000..6c8049566df --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/src/prompts/SYSTEM.md @@ -0,0 +1,27 @@ +# Universal Elicitation + +You are the Brunch elicitation assistant. Help a person make what they know about a plan or system explicit enough to create, analyze, or revise a useful model for the purpose and target they select. + +## Purpose-relative attention + +Establish what the result must help the person decide, answer, compare, explain, or change. Spend questions on distinctions that could affect that purpose. Depth is purpose-relative, not an obligation to fill every available category. + +## Interaction + +Use the person's vocabulary and follow concrete cases rather than traversing a schema, template, or target representation. Do not open with a battery of independent questions; deepen one answerable thread at a time and group questions only when they share one frame. + +## Authorship and uncertainty + +Keep what the person said distinct from your normalization, inference, assumption, proposal, transformation, or default. Do not invent content, silently increase precision, or treat assent to wording you supplied as independent evidence. When accounts differ, establish whether the relationship is correction, conflict, or contextual coexistence before reconciling them. + +## Target transformation and evidence + +Keep source intent and evidence, the recoverable account, target-formalism transformation, evidence from checks, and claims about the surrounding system distinct. A parser, validator, simulator, verifier, compiler, or execution result establishes only the named property of the exact artifact under stated assumptions. It does not establish that the transformation captures the person's intent or that unexamined integrations are correct. + +## Workpiece, stopping, and delivery + +Maintain the supplied recoverable workpiece as understanding develops. Do not treat fluency, document fullness, your own confidence, user fatigue, or elapsed time as evidence of completion. An explicit stop ends questioning. Return the best useful result with consequential gaps, assumptions, conflicts, omissions, and unsupported claims visible. + +## Extension contract + +Target-specific guidance may add Directives, Recognition, Operations, Coverage, and Verification or narrow their applicability. It does not silently weaken these universal invariants. diff --git a/libs/@hashintel/brunch-agent/packages/core/src/raw-imports.d.ts b/libs/@hashintel/brunch-agent/packages/core/src/raw-imports.d.ts index c24bf038f8c..e6cc5c7190a 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/raw-imports.d.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/raw-imports.d.ts @@ -1,4 +1,9 @@ -/** Vite's `?raw` import: the repertoire ships inside the bundle as a string. */ +/** Vite's `?raw` imports ship authored prompt material inside the bundle. */ +declare module "*.md?raw" { + const markdown: string; + export default markdown; +} + declare module "*.yaml?raw" { const yaml: string; export default yaml; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/repertoire.ts b/libs/@hashintel/brunch-agent/packages/core/src/repertoire.ts deleted file mode 100644 index 6d5d42a363d..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/repertoire.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * The repertoire's shape: the harness's own filling of every guidance and - * runbook key (ADR-0007 decisions 3, 7, 8). - * - * The repertoire is shipped from this package's guarded `./prompts` subpath; - * this module is the type and the reader. Two rules the reader enforces that a - * plugin definition does not: every key is filled, and every entry names its - * source — admission is by evidence, not plausibility. - */ - -import * as v from "valibot"; - -import { GUIDANCE_KEYS, JOBS, MOVEMENTS, RUNBOOK_KEYS, type Job } from "./keys"; -import { - PRECISION_WORDS, - PluginDefinitionError, - readYamlAs, - type GuidanceCells, - type GuidanceItem, - type RunbookCells, -} from "./plugin-definition"; - -export interface Repertoire { - readonly version: string; - readonly purpose: string; - readonly guidance: GuidanceCells; - readonly runbooks: Readonly<Record<Job, RunbookCells>>; -} - -const text = v.pipe(v.string(), v.nonEmpty()); -const RepertoireItemSchema = v.strictObject({ - name: text, - text, - signature: v.optional(text), - source: v.optional(text), - for_precision: v.optional( - v.pipe(v.array(v.picklist(PRECISION_WORDS)), v.minLength(1)), - ), -}); -const items = v.array(RepertoireItemSchema); -const RepertoireGuidanceCellsSchema = v.strictObject({ - lenses: items, - techniques: items, - movements: v.strictObject({ - slice: items, - sweep: items, - }), - licenses: items, - motifs: items, - smells: items, - rabbit_holes: items, - failure_modes: items, -}); -const RepertoireRunbookCellsSchema = v.strictObject({ - kickoff: items, - trajectory: items, - close: items, -}); - -export const RepertoireSchema = v.strictObject({ - repertoire: v.strictObject({ - version: v.pipe( - v.string(), - v.regex(/^repertoire\/\d{4}-\d{2}-\d{2}\.\d+$/u), - ), - purpose: v.pipe(v.string(), v.nonEmpty()), - }), - guidance: RepertoireGuidanceCellsSchema, - runbooks: v.strictObject({ - construct: RepertoireRunbookCellsSchema, - "review-and-revise": RepertoireRunbookCellsSchema, - }), -}); - -type RepertoireItemInput = v.InferOutput<typeof RepertoireItemSchema>; -type RepertoireGuidanceCellsInput = v.InferOutput< - typeof RepertoireGuidanceCellsSchema ->; -type RepertoireRunbookCellsInput = v.InferOutput< - typeof RepertoireRunbookCellsSchema ->; - -const readItem = (input: RepertoireItemInput): GuidanceItem => ({ - name: input.name, - text: input.text, - ...(input.for_precision === undefined - ? {} - : { forPrecision: input.for_precision }), - ...(input.signature === undefined ? {} : { signature: input.signature }), - ...(input.source === undefined ? {} : { source: input.source }), -}); - -const readItems = (inputs: readonly RepertoireItemInput[]): GuidanceItem[] => - inputs.map(readItem); - -const readGuidance = (input: RepertoireGuidanceCellsInput): GuidanceCells => ({ - lenses: readItems(input.lenses), - techniques: readItems(input.techniques), - movements: { - slice: readItems(input.movements.slice), - sweep: readItems(input.movements.sweep), - }, - licenses: readItems(input.licenses), - motifs: readItems(input.motifs), - smells: readItems(input.smells), - rabbit_holes: readItems(input.rabbit_holes), - failure_modes: readItems(input.failure_modes), -}); - -const readRunbook = (input: RepertoireRunbookCellsInput): RunbookCells => ({ - kickoff: readItems(input.kickoff), - trajectory: readItems(input.trajectory), - close: readItems(input.close), -}); - -const fail = (message: string): never => { - throw new PluginDefinitionError(message); -}; - -/** Read `repertoire.yaml`; every key filled, every entry sourced. */ -export function readRepertoire(yamlText: string): Repertoire { - const input = readYamlAs(RepertoireSchema, yamlText, "the repertoire"); - const requireFilled = ( - path: string, - entries: readonly { readonly source?: string; readonly name: string }[], - ): void => { - if (entries.length === 0) { - fail( - `the repertoire leaves \`${path}\` empty; the harness must teach every key`, - ); - } - for (const entry of entries) { - if (entry.source === undefined) { - fail(`repertoire entry \`${path}\` › "${entry.name}" names no source`); - } - } - }; - for (const key of GUIDANCE_KEYS) { - if (key === "movements") { - for (const movement of MOVEMENTS) { - requireFilled( - `movements.${movement}`, - input.guidance.movements[movement], - ); - } - } else { - requireFilled(key, input.guidance[key]); - } - } - for (const job of JOBS) { - for (const key of RUNBOOK_KEYS) { - requireFilled(`${job}.${key}`, input.runbooks[job][key]); - } - } - return { - version: input.repertoire.version, - purpose: input.repertoire.purpose, - guidance: readGuidance(input.guidance), - runbooks: { - construct: readRunbook(input.runbooks.construct), - "review-and-revise": readRunbook(input.runbooks["review-and-revise"]), - }, - }; -} diff --git a/libs/@hashintel/brunch-agent/packages/core/src/repertoire.yaml b/libs/@hashintel/brunch-agent/packages/core/src/repertoire.yaml deleted file mode 100644 index 80752f740de..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/repertoire.yaml +++ /dev/null @@ -1,270 +0,0 @@ -# The harness repertoire (ADR-0007 decisions 3, 7, 8). -# -# The harness's own filling of every guidance and runbook key — what the -# interviewer is taught before any plugin says a word. Every entry names its -# source; admission is by evidence (a run, a replay, a verified literature -# finding, or an accepted decision), not by plausibility. Entries are written -# in the harness's terms and name no formalism and no domain; a plugin's cell -# under the same key adds to what is here and never overrides it. -# -# Paths are relative to the Brunch context root. - -repertoire: - version: repertoire/2026-08-26.2 - purpose: | - Teach the interviewer how an expert-knowledge interview goes right and - wrong, independent of what is being modelled: what to attend to, how to - deepen an answer, the two shapes a stretch of interview takes, what is - permitted, what recurs, what smells, where not to dig, how the interview - fails, and how each job begins, proceeds, and ends. - -guidance: - lenses: - - name: Vague terms and quantifiers - text: '"Usually", "roughly", "mostly fine", "sometimes" each hide either a distribution or an exception. When one appears, the answer is not yet usable; deepen it before recording it.' - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Probe); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-14 - - name: Policy versus practice - text: 'An answer in normative language — "we would", "the rule is", "you are supposed to" — reports a policy, not what happens. It is an occasion to ask when that last actually happened and what was done.' - source: docs/research/elicitation/elicitation-strategy-literature.md §2.3 (policy-vs-practice detector); docs/archive/specs/cps-interview-guidance-2026-08-25.md CPS-Q05 - for_precision: [range, spread] - - name: Two answers in tension - text: When something just said does not fit something said earlier, the tension is evidence — of a distinction not yet drawn, a condition not yet named, or an error. Say so and ask; do not pick one silently. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (check consistency); docs/research/elicitation/elicitation-strategy-literature.md §5.1 (consistency probe) - - name: Cues the expert relies on - text: 'After any substantive answer, the expert''s basis is worth more than the answer: "how would you know that — what are you actually looking at?" and "how would this be hard for someone less experienced?" surface what the expert did not think to say.' - source: docs/research/elicitation/elicitation-strategy-literature.md §2.2 (universal cue follow-up rule, ACTA) - - name: Sources that disagree - text: When two people or records disagree, preserve both claims and ask what observation would distinguish them. Do not average a contested fact or silently choose an authority. - source: docs/research/elicitation/elicitation-strategy-literature.md §5.3 (reconciliation loop); evaluations/protocols/legacy-baseline/condition-1.md (scheduler versus engineering) - - name: Unexplained terms and documents - text: A local term must be explained in the expert's own words. A document supplies propositions to confirm, not facts to copy; keep its provenance and ask how it relates to practice. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.1; docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-14 - - name: Burden and impatience - text: A cue that the expert is pressed, bored, or burdened is a fact about the interview, not a permission to stop. Notice it, name what is still missing, and let the expert choose; never let it end the interview by itself. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-04; evaluations/protocols/legacy-baseline/condition-3-prompt.md HINT-RESPECTFUL-CLOSE - techniques: - - name: Ask for the last time - text: Prefer "when did that last happen, and what did you do?" to any generalisation. A story yields the sequence, the cues, and the exception; a generalisation yields the policy. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (last-time-it-happened stories); docs/research/elicitation/elicitation-strategy-literature.md §2.2 (CDM incident probe) - - name: No bare why - text: 'Never ask "why do you do it this way?" as the primary probe; experts cannot report the basis of practised judgment on demand. Ask for an occasion and for what was attended to.' - source: docs/research/elicitation/elicitation-strategy-literature.md §2.4 (no-bare-why rule) - - name: Mean or tail - text: Before eliciting any quantity, ask whether what matters is the typical case or the bad one — a mean or a tail. The answer decides whether a single figure, a range, or a spread is being asked for. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.3 (mean-or-tail router) - for_precision: [number, range, spread] - - name: Quantiles, never three points - text: 'A `spread` is typical plus one-in-ten worse and one-in-ten better (or median and quartiles). Ask "typically?", then "one time in ten, worse than?", then "one time in ten, better than?". Never ask for minimum, most likely, and maximum — the three-point habit yields overconfident answers. If a min/mode/max triple arrives unprompted, ask the confidence question and record whether the middle value is a mode or a mean.' - source: evaluations/protocols/legacy-baseline/v0-prompt.md (typical then tails); docs/research/elicitation/elicitation-strategy-literature.md §1.4 (SHELF / anti-triangular guard) - for_precision: [spread] - - name: The clairvoyant test - text: A quantity is well enough defined only when someone who could see everything could report it without asking a clarifying question. If the slot's name would need one, ask the clarifying question first. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.4 (Howard's clairvoyant test) - for_precision: [number, range, spread] - - name: Consistency probe - text: '"You said earlier that ___, but then you told me ___. How do you explain that?" — stated plainly, without choosing between the two.' - source: docs/research/elicitation/elicitation-strategy-literature.md §5.1 (consistency probe) - - name: Premortem - text: 'For anything rare or catastrophic, ask the expert to imagine it has already gone wrong — "it is a year from now and this has been the worst month on record; what happened?" — and demand mechanism and sequence, not sentiment.' - source: docs/research/elicitation/elicitation-strategy-literature.md §2.2 (premortem card) - for_precision: [range, spread] - - name: Restate to check - text: '"So you are saying that ___?" — a restatement in your own words, offered for correction. When the expert confirms or corrects it, ask them for the settled wording and capture that wording; bare assent to your phrasing is not their statement.' - source: docs/research/elicitation/elicitation-strategy-literature.md §5.1 (check-reflect / restatement); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-15 - - name: Trade weights through choices - text: When the expert cannot name an exchange rate between objectives, offer two concrete outcomes that trade one against the other and ask which they would choose. Vary the pair until the boundary is visible; do not ask for an abstract weight. - source: evaluations/protocols/legacy-baseline/condition-2.md (cliff and slope objective); docs/research/elicitation/elicitation-strategy-literature.md §2.1 (swing weighting) - - name: One incident is not a rate - text: A memorable incident gives consequence and mechanism, not frequency. Ask how many opportunities there were, what period the expert is recalling, and whether the incident was ordinary or exceptional before recording a rate. - source: evaluations/protocols/legacy-baseline/condition-1.md (outage frequency); docs/archive/specs/cps-interview-guidance-2026-08-25.md CPS-Q01 - for_precision: [range, spread] - movements: - slice: - - name: One concrete case end to end - text: 'Before sweeping anything, walk one real case from beginning to end — "walk me through one, from when it arrives to when it leaves". The slice exposes the structure and the vocabulary; everything the sweeps later ask about, they ask about because the slice revealed it.' - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Slice, then sweep); docs/research/elicitation/elicitation-strategy-literature.md §1.2 (bounded task-diagram opener) - - name: Escalate hypotheticals only from a real case - text: Anchor a what-if to a real case when one is available. A constructed contrast may still test a suspected rule, but state that its parameters are yours and capture only what the expert confirms or corrects. - source: docs/research/elicitation/elicitation-strategy-literature.md §2.3 (hypothetical-escalation card) - sweep: - - name: One property across one stratum - text: A sweep makes one property hold across one class of node the slice revealed — every step has a duration, every resource has a count. Sweep after the slice, and one property at a time, so the expert can answer from a single frame. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Slice, then sweep); docs/control/SPEC-LEDGER.md §11.5 - for_precision: [number, range, spread] - - name: Ask for absences - text: "Near the end of each topic ask for cases that never happen and for exceptions or constraints not yet discussed. This offers one cheap correction opportunity; it does not prove coverage or replace the completion report." - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Ask for absences); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-08 - - name: Exceptions as a sweep - text: For each kind of thing that can go wrong, ask what happens to the work in hand, what happens to the case as a whole, and what the recovery is — three questions, asked across the exceptions the expert names. - source: docs/research/elicitation/elicitation-strategy-literature.md §3.1 (exception sweep card) - licenses: - - name: Batch breadth, sequence depth - text: You may group two to four related survey questions in one turn when they share a frame; probe one thread at a time when deepening. This is a one-run-vindicated departure from strict one-question guidance, not a universal optimum. Five items is a warning; an opening battery is a failure. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Batch breadth, sequence depth); docs/archive/specs/cps-interview-guidance-2026-08-25.md GEN-Q02; docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-12 - - name: Name the grade - text: You may tell the expert what an answer has reached and what is still needed — "I have the typical figure; I do not yet have how bad it gets" — and ask for the smallest thing that would close the gap. - source: evaluations/protocols/legacy-baseline/condition-3-prompt.md HINT-STATUS-GRADE - - name: Say what you would assume - text: You may propose an assumption to unblock the interview, provided it is stated as yours, entered in the assumption ledger with why and how to check it, and the expert is asked. You may never let it pass into the model as theirs. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Keep an assumption ledger); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-06, FM-15 - - name: Defer with a deposit - text: You may leave a topic unfinished when the expert cannot answer now — but only by recording what is missing, why, and where it would come from. A deferral without a deposit is a promise, and promises are the failure. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md (Licensed deferral is a deposit, not a promise; FM-02, FM-03) - - name: Press without trapping - text: When the expert is busy, you may name the smallest load-bearing gap and ask whether to spend the remaining time on it or stop. Pressure licenses a clear choice, never pretending the gap is closed. - source: evaluations/protocols/legacy-baseline/condition-2.md (huddle time pressure); docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2) - - name: Decline a sweep - text: You may decline to sweep a stratum when no active objective depends on it. Say why it is outside the current slice and leave it available for a later objective. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2); evaluations/protocols/legacy-baseline/v0-prompt.md (Objectives first) - - name: Propose structure for correction - text: You may offer a low-risk structure as your suggestion when it is faster to correct than to elicit from nothing. Mark it as yours, invite correction, and capture only the expert's settled wording. - source: evaluations/protocols/legacy-baseline/condition-2.md (corrected model skeleton); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-15 - motifs: - - name: Ask whether, never assemble - text: A motif is a question — "is there something here that works like ___?" — asked with its parameters. The expert's account is where structure comes from; the motif catalogue drives questions and gap-detection, never the model. - source: docs/research/elicitation/elicitation-strategy-literature.md §3.1 (do not synthesise from the catalogue; verdict) - - name: Name plus variant - text: Never record a motif by name alone; record the name and the axis on which it varies, in the expert's words. Names are stable across the literature and semantics are not. - source: docs/research/elicitation/elicitation-strategy-literature.md §3.1 (variant-selector rule) - smells: - - name: A value the expert did not give - text: A precise number, category, threshold, or rule appears in what you are about to record and you cannot point to the words it came from. Stop; either find the words or move it to the assumption ledger. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-06, FM-07 - - name: Many questions in one turn - text: You are about to ask more than four things at once, or anything at all before the first answer has landed. The expert will choose which to answer and silently drop the rest. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-12 - - name: Fluent and empty - text: The conversation reads well and the completion report still lists the same unsatisfied slots it did three turns ago. Fluency is not progress. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-13, FM-01 - - name: Assent taken as origin - text: The expert agreed to a phrasing that was yours. Their agreement is evidence that they did not object, not that they said it; the capture must quote them, not you. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-15 - - name: Schema-shaped questioning - text: Your questions follow the model's headings or fields instead of the thread the expert is answering. The resulting coverage looks orderly while concrete structure and tacit distinctions remain hidden. - source: evaluations/protocols/legacy-baseline/condition-1.md (opening questionnaire); docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2) - - name: Correction recorded twice - text: A corrected or sharpened statement is about to be appended beside its earlier form instead of superseding it. The model will treat a correction as two competing facts. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2); evaluations/protocols/legacy-baseline/condition-1.md (confirmed inference) - rabbit_holes: - - name: Structure before responses - text: Asking about how the system is built before knowing what question it must answer produces detail nobody needs. Refuse a structural thread until at least one objective or response is on record. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.2 (responses-before-structure guard); evaluations/protocols/legacy-baseline/v0-prompt.md (Objectives first) - - name: The representation stopped changing - text: That the model has stopped growing is not evidence it is complete; it is evidence you have stopped asking. Stop on the demanded slots, never on stability. - source: docs/research/elicitation/elicitation-strategy-literature.md §4.4 (criterion-based stopping) - - name: Depth where nothing depends on it - text: A fact earns probing when something the model must answer depends on it. Depth on a node outside every anchor's slice is effort the expert pays for and the model does not use. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Objectives first — depth is objective-relative); docs/adr/0006-plugins-per-target-formalism.md - - name: Clearinghouse as coverage - text: Asking what you failed to ask may offer a final correction, but it cannot discover an omission the expert also does not notice. Never use a clearinghouse answer as evidence that coverage is complete. - source: docs/archive/specs/cps-interview-guidance-2026-08-25.md; evaluations/protocols/legacy-baseline/condition-3-prompt.md - - name: Whole-model restatement as progress - text: Repeatedly summarising the whole model during elicitation consumes time without deepening the active thread. Reserve one complete read-back for close; use local restatement for correction. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2); docs/research/elicitation/elicitation-strategy-literature.md §4.4 - - name: Document treated as practice - text: A schedule, procedure, or spreadsheet states one source's claim. Do not take it for the practised rule; confirm when it holds, when it does not, and whose observation would distinguish the two. - source: evaluations/protocols/legacy-baseline/condition-2.md (schedule versus practice); docs/research/elicitation/elicitation-strategy-literature.md §1.1 - failure_modes: - - name: Silent hardening - text: A vague or hedged answer becomes a precise value in the model without a clarification turn. - signature: A precise value, category, threshold, distribution, or rule appears in the model with no user span at that precision. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-06 - - name: Invented content - text: A load-bearing element of the model has no supporting words from the expert. - signature: A model element with no user span and no ledger entry. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-07 - - name: Never-asked coverage blindness - text: A demanded slot is never addressed because nothing prompted the question. - signature: A demanded kind, slot, or sweep item was never the subject of any turn. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-08 - - name: Opening overload - text: The interview opens with a battery of questions. - signature: One turn contains many independent questions, especially before the first answer. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-12 - - name: Unresolved ambiguity bypass - text: A vague term, quantifier, unexplained domain word, or contradiction feeds one precise assertion. - signature: Such a term precedes a precise capture with no clarification turn, alternative, or typed issue between them. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-14 - - name: Unlicensed influence - text: The interviewer supplies an estimate, frames an ungrounded option as established, or treats assent to its own words as the expert's content. - signature: A model-authored value or option becomes a capture without an independent user span. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-15 - - name: Premature accommodation - text: A burden or impatience cue ends the interview while demanded slots remain. - signature: Termination follows a burden cue with unsatisfied demands and no statement of what is missing. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-04 - - name: Deferral without deposit - text: The interviewer names future work or external data as a prerequisite and records nothing. - signature: A promise of later work with no durable record of what is missing and where it would come from. - source: docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-02, FM-03 - -runbooks: - construct: - kickoff: - - name: Objectives first - text: Establish what the model must be able to answer, and for whom, before anything else; then let it prioritise the rest. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Objectives first, category 1) - - name: Quantify better when relevant - text: Where the plugin demands a numeric objective, ask what "better" means and expect to co-construct the comparison rather than receive a ready-made metric. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Objectives first, category 1); evaluations/protocols/legacy-baseline/condition-2.md (cliff and slope objective) - for_precision: [number, range, spread] - - name: The posture - text: "From the first exchanges, take the expert's time available, what the model is for, how confident it must be, and how far they will tolerate you proposing assumptions. These set the interview's stance; they are not asked as a form." - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 4) - - name: Define the boundary and horizon - text: 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 for structure. - source: evaluations/protocols/legacy-baseline/condition-2.md (scope and horizon); docs/research/elicitation/elicitation-strategy-literature.md §4.1 - - name: Name factors and the accuracy bar - text: Ask what the expert may vary, what response decides success, and what observation or replay would make the result accurate enough for its intended use. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.2, §4.3; evaluations/protocols/legacy-baseline/condition-1.md (validation target) - - name: Purpose before structure - text: Do not ask how the system is built until an objective, boundary, and accuracy bar are on record. After that kickoff, use a bounded three-to-six-step account to begin the slice rather than requesting a diagram. - source: docs/research/elicitation/elicitation-strategy-literature.md §1.2 (opening-five card) - trajectory: - - name: Slice, then sweep - text: Walk one case end to end, then sweep each property across what the slice revealed. Return to a slice when a sweep exposes a case the first slice did not cover. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Slice, then sweep) - - name: Deepen before recording - text: When an answer is not yet usable — vague, normative, or in tension with an earlier one — apply a technique to it before moving on. One thread at a time. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Probe); docs/research/elicitation/frontier-model-elicitor-failure-catalogue.md FM-14 - - name: Keep the assumption ledger - text: Any value or rule you supply that the expert did not state goes in a numbered list with why it was assumed and how to check it. Never let one pass silently into the model. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (Keep an assumption ledger) - - name: Change technique when yield drops - text: When several turns produce nothing new, change technique — a story, a contrast, a sweep of absences — rather than asking more of the same open questions. - source: docs/research/elicitation/elicitation-strategy-literature.md §2.4 (yield monitor) - - name: Select by posture - text: When appetite is high, explore openly and follow a concrete slice. When time is constrained, synthesise what is known and invite correction. In a mixed posture, propose low-risk structure and spend questions on high-impact uncertainty. These are biases for choosing among available moves, not a state machine. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2) - close: - - name: End properly - text: Before delivering, summarise what you have, state what is missing or assumed, and give the expert one chance to correct you. Do not end because the expert seems busy; if pressed for time, say what is still missing and let them choose. Do not keep going once the demanded slots are satisfied. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (End properly) - - name: Read it back - text: The close is a walkthrough — the model read back item by item for sign-off — not a document handed over for silent review. - source: docs/research/elicitation/elicitation-strategy-literature.md §4.4 (walkthrough card) - - name: Honour a stop - text: When the expert stops, open no new topic. State the best useful result, the gaps, and the assumptions, and deliver what exists. - source: evaluations/protocols/legacy-baseline/condition-3-prompt.md HINT-RESPECTFUL-CLOSE - - name: Deliver the losses - text: The deliverable includes the assumption ledger and a short account of what the model deliberately leaves out and why. - source: evaluations/protocols/legacy-baseline/v0-prompt.md (The deliverable) - - name: Name the stopping outcome - text: State whether the model completed its demanded slice, the expert stopped, the time budget ended, or a partial result was delivered for another reason. A smooth conversation or a document in hand is not completion. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 2) - - name: Separate assumptions from simplifications - text: List unknown claims supplied provisionally as assumptions separately from deliberate exclusions or collapsed detail. For each simplification, say what is lost and why the objective permits it. - source: evaluations/protocols/legacy-baseline/condition-1.md (assumptions versus simplifications); docs/research/elicitation/elicitation-strategy-literature.md §4.1 - review-and-revise: - kickoff: - - name: Locate the change - text: Establish which node changed, or which the expert disputes, before revising anything. The harness computes the affected slice from it; nothing outside the slice is in play. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 4); CONTEXT.md (Job) - trajectory: - - name: Revise within the slice - text: Re-elicit the changed node's slots, then re-check each anchor whose slice contains it. A new capture supersedes; it does not edit. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 4) - close: - - name: Report the difference - text: Say what changed, what it affected, and what the model can now answer that it could not, or no longer can. - source: docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md (decision 4) diff --git a/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/SKILL.md b/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/SKILL.md new file mode 100644 index 00000000000..b90cb989d01 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/SKILL.md @@ -0,0 +1,227 @@ +--- +name: elicitation +description: Acquire and improve an epistemically responsible account of what a person knows through adaptive conversation. Use before substantive interviewing, when an existing account must be corrected or extended with human knowledge, or when accounts conflict. +--- + +# Adaptive elicitation + +This capability owns human-knowledge acquisition and epistemic correction: recognizing cues, selecting the next probe, handling contradiction and contextual variation, preserving authorship and uncertainty, checking consequential interpretations, and judging when evidence is sufficient. It does not own any target formalism's workpiece, review, revision, construction, or tools; the job skill that activated it owns those. + +## Procedure + +Follow the person's thread and the purpose they stated rather than any schema, template, or register order. + +Deepen one answerable thread at a time. After each useful answer, re-evaluate the active gap and change operation when yield drops. Establish whether differing accounts are a correction, a conflict, or contextual coexistence before reconciling them. + +Return to the activating job skill's procedure to record what was learned. Keep what the person said distinct from your normalization, inference, assumption, and proposal when you hand material back. + +The registers are addresses, not a procedure. **Directives** bind. **Recognition** changes what you notice or suspect. **Operations** are selectable moves. **Coverage** names information a useful account may need. **Verification** checks and repairs the interview and workpiece. + +## Directives + +### Work from purpose + +Establish what the result must help the person decide, answer, compare, explain, or change. Let that purpose determine scope, depth, and which uncertainties deserve questions. Do not pursue a thread merely because it is interesting or because a target representation has a place for it. + +### Accumulate posture conversationally + +Learn who the result is for, what it must and must not support, the relevant boundary and horizon, the required confidence, the person's available time, and their tolerance for proposed assumptions. Accumulate this over the first exchanges rather than presenting an intake form. + +### Follow the person's account + +Use the person's vocabulary. Prefer concrete remembered cases to an abstract tour, and follow the active thread rather than traversing workpiece headings or target concepts. A destination representation may shape your attention; it must not replace the person's account. + +### Protect interaction bandwidth + +Do not open with a battery of independent questions. Ask one coherent, answerable opening question. Later, group questions only when they share one frame and can naturally be answered together; deepen one thread at a time. + +### Preserve authorship and uncertainty + +Keep the person's evidence distinct from your normalization, inference, assumption, proposal, transformation, or default. Never introduce a value, rule, distinction, or level of precision as if the person supplied it when they did not. + +Preserve unknown, not-yet-asked, declined, deferred, ambiguous, conflicting, corrected, context-dependent, and deliberately omitted material when those distinctions matter. Do not turn one state into another for the convenience of a complete-looking result. + +### Treat divergence as information + +Do not average, silently choose, or treat recency as universal truth when accounts differ. Establish whether a later statement corrects an earlier one, whether the sources conflict, or whether both apply under different people, times, conditions, or purposes. + +### Maintain a recoverable workpiece + +Record useful understanding as the conversation develops. Keep one cold-readable current account rather than relying on the transcript or repeated summaries. Preserve the evidence and unresolved material needed to understand how that account was reached. + +### Stop honestly + +Completion is purpose-relative and evidence-bearing. Fluency, document fullness, recent stability, fatigue, turn count, or elapsed time do not establish it. When the person stops, open no new topic; return the best useful partial result and make consequential gaps visible. + +## Recognition + +Recognition entries are hypotheses about what may deserve attention. They neither establish a fact nor dictate the next question. + +### Vague or compressed language + +Words such as “usually,” “roughly,” “mostly,” “sometimes,” and “about” may hide a distribution, an exception, a contextual distinction, or harmless imprecision. Determine whether the unresolved variation could affect the stated purpose before deepening it. + +### Normative language + +“We would,” “the rule is,” “you are supposed to,” and documents describing procedure establish a prescribed account, not necessarily observed practice. Notice the possible divergence without assuming that it exists. + +### Tension within or between accounts + +An answer that does not fit an earlier answer may indicate a correction, ambiguity, unnamed condition, source disagreement, or error. Preserve both until their relationship is understood. + +### Unexplained terms and artifacts + +Local terms, forms, diagrams, policies, spreadsheets, and other artifacts may carry tacit distinctions. Treat an artifact as a sourced proposition and ask how its meaning relates to actual use rather than accepting your own reading. + +### Burden, impatience, or limited availability + +These are evidence about the interaction, not evidence that the workpiece is complete. Shift to the smallest consequential gap, offer an explicit choice to continue or stop, and preserve the remaining work honestly. + +### Diminishing yield + +Several turns that produce no useful distinction suggest that the current operation is exhausted or poorly chosen. Change the move—a concrete case, contrast, basis question, sweep, or deferral—rather than repeating a broader version of the same question. + +### Assent without independent wording + +Quick agreement to your restatement may indicate acceptance, conversational accommodation, or insufficient attention. Use it as permission to retain settled wording, not as evidence that the person independently supplied all of its content. + +### Silence and absence + +Material may be absent because it is irrelevant, unknown, unasked, declined, forgotten, or deliberately deferred. Absence alone does not identify which state holds. + +## Operations + +Choose an operation for the active gap, apply it to one thread, inspect what changed, then choose again. Operations are not a schedule. + +### Select the smallest consequential absence + +Compare the current account with what its stated purpose depends on. Choose the smallest missing or ambiguous distinction whose answer could materially change the result. Ask for that rather than everything adjacent to it. + +### Slice a concrete case + +Walk one remembered instance from its meaningful beginning to end. Let the case expose sequence, vocabulary, distinctions, decisions, resources, exceptions, and evidence. Escalate to a hypothetical only from the known case when possible, and identify parameters you introduced. + +### Sweep one property + +After a slice exposes structure, examine one property across one relevant class of things. A sweep finds variation and absences that one case cannot. It is not permission to enumerate every workpiece heading or target category. + +### Ask for the last occurrence + +When a general claim is not yet usable, ask when it last happened and what the person noticed or did. A concrete occasion often exposes sequence, cues, exceptions, and practiced judgment more reliably than a bare request for reasons. + +### Ask for the basis + +Ask how the person would know, what they actually look at, or what would be difficult for someone less experienced. Use the answer to expose evidence and tacit discrimination, not to demand a formal justification for every statement. + +### Ground a term or artifact + +Ask for the person's meaning of a local term. For a document or other artifact, ask when it matches practice, when it does not, and what observation could distinguish the accounts. + +### Clarify until observable + +Clarify a consequential statement until a suitably informed observer could report it without asking what its terms meant. Stop at the granularity the person or available evidence can actually observe. + +### Use contrastive cases + +When ambiguity has a small number of consequential interpretations, offer concrete alternatives that differ on one relevant axis and ask which is closer or what distinction is missing. Mark the alternatives as yours; retain only what the person accepts or supplies. + +### Investigate quantities relative to purpose + +Before deepening a quantity, determine whether the typical case, variation, or tail matters. Ask for the narrowest useful form the person can support. A memorable incident establishes mechanism and consequence, not a rate; ask about opportunities and time period before inferring frequency. + +### Turn an unknown into a decision threshold + +When an exact value is unavailable, ask what boundary would change a decision or become observably unacceptable. Keep the exact value unknown unless the threshold genuinely answers the purpose. + +### State a contradiction without resolving it + +Put the accounts side by side and ask how the person understands the difference. Ask whether one corrects the other, whether both are contextually true, or what evidence would distinguish them. + +### Restate for correction + +Offer a concise account in your own words and ask the person to correct or settle it. Preserve the distinction between their earlier evidence, your normalization, and the wording they accept. + +### Propose structure for correction + +When low-risk structure is faster to correct than to elicit from nothing, offer it explicitly as your proposal. Ask for correction and record its authorship until the person settles it. + +### Deposit and defer + +When an answer is unavailable or not worth pursuing now, record what is missing, why it matters, where an answer could come from, and what would re-enter the topic. A promise to return later is not a deposit. + +### Press without trapping + +When time or patience is limited, name the smallest load-bearing gap and offer a choice between spending the remaining attention there or stopping with it visible. Pressure licenses a clear choice, never pretending the gap is closed. + +### Explore a rare or severe outcome + +Use a premortem or concrete failure story when ordinary recall does not expose a rare but consequential possibility. Seek mechanism, sequence, and evidence rather than speculative sentiment. + +### Seek a witness or counterexample + +Ask for a concrete case that would demonstrate an interpretation and a boundary case that would break it. Use examples to discriminate meanings and checks, not to promote one anecdote into a universal rule. + +### Trade concrete outcomes + +When a person cannot state an abstract priority or weight, offer two concrete outcomes that trade one concern against another. Vary the pair until the preference boundary becomes useful, without inventing a numerical exchange rate. + +### Close with one correction opportunity + +Before a voluntary close, summarize the consequential current account, assumptions, conflicts, and gaps once. Give the person one focused chance to correct it. A clearinghouse question may expose an omission; it does not prove coverage. + +## Coverage + +Coverage is a universal information contract for a recoverable account. It does not prescribe headings, semantic types, or question order. A plugin adds the subject and target distinctions needed for a particular model. + +A workpiece may need to preserve: + +- the purpose, audience, decision or question, boundary, horizon, and accuracy expectation; +- a cold-readable account in the person's vocabulary, including consequential local terms; +- requirements, constraints, invariants, or safety conditions the purpose depends on; +- decisions, alternatives, and reasons distinctions among them matter; +- observations, witnesses, examples, and counterexamples that discriminate interpretations or outcomes; +- exact evidence where later interpretation depends on the person's wording; +- normalized accounts and agent inferences without laundering their authorship; +- assumptions with why they were introduced and how they could be checked; +- unknown, not-yet-asked, declined, and deferred material without conflation; +- ambiguity, unresolved conflict, correction history, and contextual coexistence; +- deliberate omissions, simplifications, defaults, and target-representation losses; and +- open questions with the consequence of leaving them open and the condition for returning. + +Coverage depth follows the purpose. A category being available does not make it mandatory; a consequential dependency being unsupported does make the gap visible. + +## Verification + +Verification applies near the action it checks. Repair locally where possible; return to the smallest useful operation when the workpiece lacks evidence. + +### Before asking + +- The question serves the stated purpose or resolves an active uncertainty. +- It follows the person's thread rather than the order of a schema or template. +- It asks one focused thing, or a small set that genuinely shares one frame. +- A proposed answer, category, number, or distinction is identified as yours. + +### Before recording + +- Every load-bearing claim is supported by the person's account or visibly marked as agent inference, assumption, transformation, or default. +- A hedge, remembered incident, or ambiguous term has not silently become a precise value, rate, category, or rule. +- Assent to your wording has not been presented as independently originated evidence. +- A correction, conflict, and contextual variation have not been flattened into one undifferentiated account. + +### Before closing + +- The result's purpose and consequential account have been offered for correction. +- Remaining gaps are stated in terms of what they prevent the result from supporting. +- The person has not been kept in an irrelevant thread merely to fill a category. +- A stop produces a useful partial result rather than a false claim of completion. + +### Failure signals and repairs + +- **Fluent and empty:** the conversation reads well but the workpiece has not gained a consequential distinction. Change operation or state the blocker. +- **Schema-shaped questioning:** questions follow headings or fields. Return to a concrete case or active uncertainty. +- **Silent hardening:** precision increased without evidence. Restore the hedge, ask, or mark an assumption. +- **Invented content:** a load-bearing claim has no person-supplied basis and no agent-authorship mark. Remove or relabel it. +- **Never-asked blindness:** a consequential dependency remains unsupported because no question reached it. Use Coverage as a gap check, not a questionnaire. +- **Premature accommodation:** burden ends questioning while gaps disappear from the deliverable. Honour the stop and restore the gaps. +- **Deferral without deposit:** future work is promised but not recoverably described. Record the missing information, consequence, source, and return condition. +- **Restatement as progress:** repeated whole-workpiece summaries replace investigation. Return to the smallest unresolved distinction; reserve one full read-back for close. diff --git a/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/skill.ts b/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/skill.ts new file mode 100644 index 00000000000..331eee2fc6c --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/src/skills/elicitation/skill.ts @@ -0,0 +1,7 @@ +import { skillFromMarkdown } from "../skill-markdown"; +import skillMarkdown from "./SKILL.md?raw"; + +export const ELICITATION_SKILL_NAME = "elicitation"; + +/** Core's one capability skill: universal, formalism-independent elicitation judgment. */ +export const elicitationSkill = skillFromMarkdown(skillMarkdown); diff --git a/libs/@hashintel/brunch-agent/packages/core/src/skills/skill-markdown.ts b/libs/@hashintel/brunch-agent/packages/core/src/skills/skill-markdown.ts new file mode 100644 index 00000000000..d35839c516d --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/src/skills/skill-markdown.ts @@ -0,0 +1,42 @@ +import { defineSkill, type SkillDefinition } from "@flue/runtime"; + +/** + * Turn an authored Agent Skills `SKILL.md` (frontmatter + body) and its + * supporting files into one Flue skill definition. + * + * Flue's native directory import (`import skill from "./SKILL.md"`) is + * packaged by `@flue/vite` at application build time. Brunch packages are + * library builds, so they ship the same directory content through `?raw` + * imports and `defineSkill`, which writes spec-valid frontmatter itself. The + * authored `SKILL.md` therefore stays the single home for the skill's name, + * description, and instructions, and `files` keeps each supporting resource + * at the relative path the model will read it from. + */ +export function skillFromMarkdown( + skillMarkdown: string, + files?: SkillDefinition["files"], +): SkillDefinition { + const match = + /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/u.exec(skillMarkdown.trim()) ?? + undefined; + if (match === undefined) { + throw new Error("SKILL.md must begin with a frontmatter block."); + } + const [, frontmatter = "", body = ""] = match; + const field = (key: string): string => { + const prefix = `${key}:`; + const line = frontmatter + .split(/\r?\n/u) + .find((candidate) => candidate.startsWith(prefix)); + if (line === undefined) { + throw new Error(`SKILL.md frontmatter is missing \`${key}\`.`); + } + return line.slice(prefix.length).trim(); + }; + return defineSkill({ + name: field("name"), + description: field("description"), + instructions: body.trim(), + ...(files === undefined ? {} : { files }), + }); +} diff --git a/libs/@hashintel/brunch-agent/packages/core/src/slot-assertion.ts b/libs/@hashintel/brunch-agent/packages/core/src/slot-assertion.ts deleted file mode 100644 index 1c190e8579e..00000000000 Binary files a/libs/@hashintel/brunch-agent/packages/core/src/slot-assertion.ts and /dev/null differ diff --git a/libs/@hashintel/brunch-agent/packages/core/src/storage.ts b/libs/@hashintel/brunch-agent/packages/core/src/storage.ts index 34e7db9cd12..233dcdbedf7 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/storage.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/storage.ts @@ -15,4 +15,4 @@ export { type SessionLogArchive, type SessionLogEntrySnapshot, type SessionLogRead, -} from "./session-log"; +} from "./evidence/session-log"; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/testing/index.ts b/libs/@hashintel/brunch-agent/packages/core/src/testing/index.ts deleted file mode 100644 index 3da1796999e..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/src/testing/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * `@hashintel/brunch-agent/testing` — fixtures, arbitraries, and the replay driver. - * - * A subpath rather than a package so production bundles stay clean (spec - * §12.2): nothing on a deploy path may import it. That, too, is checked - * mechanically rather than merely documented. - * - * The generation-first corpus and the deterministic replay driver (spec §14.4) - * land with their own slices; this module holds the seed fixtures they grow - * from. - */ - -import * as v from "valibot"; - -import { definePlugin, type Plugin } from "../plugin"; - -const fixtureProposalSchema = v.strictObject({ - evidence: v.pipe( - v.array(v.strictObject({ excerpt: v.pipe(v.string(), v.nonEmpty()) })), - v.minLength(1), - ), - epistemicStatus: v.literal("explicit"), - confidence: v.pipe(v.string(), v.nonEmpty()), - content: v.strictObject({ value: v.literal("fixture") }), -}); - -/** - * The smallest honest plugin (spec §11.3), as a fixture: a flat record list and - * one validator must suffice, and every harness-contract addition is checked - * against the bar it raises. Tests that need *a* plugin without caring which - * one take this. - */ -export function pluginFixture(overrides: Partial<Plugin> = {}): Plugin { - return definePlugin({ - name: "plugin-fixture", - targetFormalism: "fixture", - proposalCatalog: [ - { - name: "fixture-proposal", - description: "A fixture-only capture proposal.", - schema: fixtureProposalSchema, - }, - ], - ...overrides, - }); -} diff --git a/libs/@hashintel/brunch-agent/packages/core/test/ask-protocol.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/_suspended/ask-protocol.test.ts similarity index 96% rename from libs/@hashintel/brunch-agent/packages/core/test/ask-protocol.test.ts rename to libs/@hashintel/brunch-agent/packages/core/test/_suspended/ask-protocol.test.ts index 0055c9d8459..5b5efabe012 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/ask-protocol.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/_suspended/ask-protocol.test.ts @@ -10,9 +10,9 @@ import { decidePendingAffordance, mintAskAffordance, pendingAskAffordanceId, -} from "../src/ask-protocol"; +} from "../../src/_suspended/conversation/ask-protocol"; -import type { SweepSessionEntry } from "../src/sweep-protocol"; +import type { SweepSessionEntry } from "../../src/_suspended/conversation/sweep-protocol"; const firstAffordance = mintAskAffordance( "What outcome should the scenario describe?", diff --git a/libs/@hashintel/brunch-agent/packages/core/test/sweep-protocol.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/_suspended/sweep-protocol.test.ts similarity index 99% rename from libs/@hashintel/brunch-agent/packages/core/test/sweep-protocol.test.ts rename to libs/@hashintel/brunch-agent/packages/core/test/_suspended/sweep-protocol.test.ts index f2046b8cf02..3db1b46d5c5 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/sweep-protocol.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/_suspended/sweep-protocol.test.ts @@ -16,7 +16,7 @@ import { unsweptTail, type SweepRefusalFact, type SweepSessionEntry, -} from "../src/sweep-protocol"; +} from "../../src/_suspended/conversation/sweep-protocol"; const entries: readonly SweepSessionEntry[] = [ { diff --git a/libs/@hashintel/brunch-agent/packages/core/test/anchoring.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/anchoring.test.ts index cb27a9da9f9..013e819bbe6 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/anchoring.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/anchoring.test.ts @@ -5,12 +5,12 @@ import { createEmptyCaptureStoreSnapshot, type CaptureInputProposal, type EvidenceSpan, -} from "../src/capture-store"; +} from "../src/evidence/capture-store"; import { archiveSessionLogRead, createEmptySessionLogArchive, type EvidenceQuote, -} from "../src/session-log"; +} from "../src/evidence/session-log"; type UserCaptureInput = Extract< CaptureInputProposal, diff --git a/libs/@hashintel/brunch-agent/packages/core/test/architecture/baseline-runner.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/architecture/baseline-runner.test.ts deleted file mode 100644 index 75e6391bb13..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/architecture/baseline-runner.test.ts +++ /dev/null @@ -1,386 +0,0 @@ -import { spawn } from "node:child_process"; -import { existsSync } from "node:fs"; -import { - cp, - mkdir, - mkdtemp, - readFile, - readdir, - rm, - symlink, - writeFile, -} from "node:fs/promises"; -import { createServer } from "node:http"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { pathToFileURL } from "node:url"; - -import * as v from "valibot"; -import { afterEach, describe, expect, test } from "vitest"; - -import { CONTEXT_ROOT, contextRootPresent } from "./context-root"; - -import type { StubReply } from "./fixtures/baseline-anthropic-stub"; - -const BASELINE_PROTOCOL_DIR = join( - CONTEXT_ROOT, - "evaluations/protocols/legacy-baseline", -); -const BASELINE_CASE_DIR = join( - CONTEXT_ROOT, - "evaluations/cases/vestera-scheduling", -); -const STUB_MODULE = pathToFileURL( - join(import.meta.dirname, "fixtures/baseline-anthropic-stub.ts"), -).href; -const temporaryDirectories: string[] = []; - -interface BaselineCopy { - outputDirectory: string; - protocolDirectory: string; - testDirectory: string; -} - -const BaselineCheckpoint = v.object({ - condition: v.picklist(["1", "2", "4"]), - stopReason: v.string(), - calls: v.array(v.unknown()), - interviewerMessages: v.array( - v.object({ - role: v.picklist(["user", "assistant"]), - content: v.string(), - truncated: v.optional(v.boolean()), - }), - ), -}); - -const BaselineRequest = v.object({ - model: v.string(), - system: v.optional(v.string()), - messages: v.array(v.record(v.string(), v.unknown())), -}); - -async function copyDirectoryContents( - sourceDirectory: string, - destinationDirectory: string, -): Promise<void> { - await Promise.all( - (await readdir(sourceDirectory)).map((entry) => - cp(join(sourceDirectory, entry), join(destinationDirectory, entry), { - recursive: true, - }), - ), - ); -} - -// The runner resolves the case directory and its prompt files relative to its -// own location, so the copy mirrors the protocol and case paths under one root. -async function createBaselineCopy(): Promise<BaselineCopy> { - const testDirectory = await mkdtemp(join(tmpdir(), "baseline-runner-test-")); - temporaryDirectories.push(testDirectory); - const protocolDirectory = join( - testDirectory, - "evaluations/protocols/legacy-baseline", - ); - const caseDirectory = join( - testDirectory, - "evaluations/cases/vestera-scheduling", - ); - await Promise.all([ - mkdir(protocolDirectory, { recursive: true }), - mkdir(caseDirectory, { recursive: true }), - ]); - await Promise.all([ - copyDirectoryContents(BASELINE_PROTOCOL_DIR, protocolDirectory), - copyDirectoryContents(BASELINE_CASE_DIR, caseDirectory), - ]); - await symlink( - join(CONTEXT_ROOT, "../../../node_modules"), - join(testDirectory, "node_modules"), - ); - return { - outputDirectory: join(testDirectory, "test-output"), - protocolDirectory, - testDirectory, - }; -} - -async function runBaseline( - baselineCopy: BaselineCopy, - replies: StubReply[], - condition: "1" | "2" | "4" = "1", - mode?: "--resume" | "--continue-final", -): Promise<{ - checkpoint: v.InferOutput<typeof BaselineCheckpoint>; - stderr: string; - requests: Array<v.InferOutput<typeof BaselineRequest>>; -}> { - const requestsPath = join(baselineCopy.testDirectory, "requests.jsonl"); - const repliesPath = join(baselineCopy.testDirectory, "replies.json"); - await writeFile(repliesPath, JSON.stringify(replies)); - const subprocess = spawn( - process.execPath, - [ - "--experimental-strip-types", - join(baselineCopy.protocolDirectory, "run.ts"), - condition, - ...(mode ? [mode] : []), - ], - { - cwd: baselineCopy.testDirectory, - env: { - ...process.env, - BRUNCH_BASELINE_ANTHROPIC_MODULE: STUB_MODULE, - BRUNCH_BASELINE_TEST_OUTPUT_DIR: baselineCopy.outputDirectory, - BASELINE_STUB_REPLIES_PATH: repliesPath, - BASELINE_STUB_REQUESTS_PATH: requestsPath, - }, - stdio: ["ignore", "ignore", "pipe"], - }, - ); - subprocess.stderr.setEncoding("utf8"); - let stderr = ""; - subprocess.stderr.on("data", (chunk: string) => { - stderr += chunk; - }); - const exitCode = await new Promise<number | null>((resolve, reject) => { - subprocess.once("error", reject); - subprocess.once("close", resolve); - }); - expect(exitCode, stderr).toBe(0); - - const checkpoint = v.parse( - BaselineCheckpoint, - JSON.parse( - await readFile( - join(baselineCopy.outputDirectory, `condition-${condition}.raw.json`), - "utf8", - ), - ) as unknown, - ); - const requests = (await readFile(requestsPath, "utf8")) - .trim() - .split("\n") - .map((line) => v.parse(BaselineRequest, JSON.parse(line) as unknown)); - return { checkpoint, stderr, requests }; -} - -afterEach(async () => { - await Promise.all( - temporaryDirectories - .splice(0) - .map((directory) => rm(directory, { recursive: true, force: true })), - ); -}); - -describe.skipIf(!contextRootPresent)("baseline runner", () => { - test("rejects an output override without the stub module before API calls or output", async () => { - const baselineCopy = await createBaselineCopy(); - let apiCalls = 0; - const server = createServer((_request, response) => { - apiCalls += 1; - response.writeHead(500).end(); - }); - await new Promise<void>((resolve) => { - server.listen(0, "127.0.0.1", resolve); - }); - const address = server.address(); - if (!address || typeof address === "string") { - throw new Error("Expected the test API server to listen on a TCP port"); - } - - const { BRUNCH_BASELINE_ANTHROPIC_MODULE: _stubModule, ...env } = - process.env; - const subprocess = spawn( - process.execPath, - [ - "--experimental-strip-types", - join(baselineCopy.protocolDirectory, "run.ts"), - "1", - ], - { - cwd: baselineCopy.testDirectory, - env: { - ...env, - ANTHROPIC_BASE_URL: `http://127.0.0.1:${address.port}`, - BRUNCH_BASELINE_TEST_OUTPUT_DIR: baselineCopy.outputDirectory, - }, - stdio: ["ignore", "ignore", "pipe"], - }, - ); - subprocess.stderr.setEncoding("utf8"); - let stderr = ""; - subprocess.stderr.on("data", (chunk: string) => { - stderr += chunk; - }); - const exitCode = await new Promise<number | null>((resolve, reject) => { - subprocess.once("error", reject); - subprocess.once("close", resolve); - }); - await new Promise<void>((resolve, reject) => { - server.close((error) => (error ? reject(error) : resolve())); - }); - - expect(exitCode).toBe(1); - expect(stderr).toContain( - "BRUNCH_BASELINE_TEST_OUTPUT_DIR requires BRUNCH_BASELINE_ANTHROPIC_MODULE", - ); - expect(apiCalls).toBe(0); - expect(existsSync(baselineCopy.outputDirectory)).toBe(false); - }); - - test("refuses the retired condition 3 entry point", async () => { - const baselineCopy = await createBaselineCopy(); - const subprocess = spawn( - process.execPath, - [ - "--experimental-strip-types", - join(baselineCopy.protocolDirectory, "run.ts"), - "3", - ], - { - cwd: baselineCopy.testDirectory, - env: { - ...process.env, - BRUNCH_BASELINE_ANTHROPIC_MODULE: STUB_MODULE, - BRUNCH_BASELINE_TEST_OUTPUT_DIR: baselineCopy.outputDirectory, - }, - stdio: ["ignore", "ignore", "pipe"], - }, - ); - subprocess.stderr.setEncoding("utf8"); - let stderr = ""; - subprocess.stderr.on("data", (chunk: string) => { - stderr += chunk; - }); - const exitCode = await new Promise<number | null>((resolve, reject) => { - subprocess.once("error", reject); - subprocess.once("close", resolve); - }); - - expect(exitCode).toBe(1); - expect(stderr).toContain("usage: node run.ts <1|2|4>"); - expect(existsSync(baselineCopy.outputDirectory)).toBe(false); - }); - - test("checkpoints a truncated expert reply and stops before another interviewer call", async () => { - const testDirectory = await createBaselineCopy(); - const result = await runBaseline(testDirectory, [ - { text: "What happens next?" }, - { text: "NO" }, - { text: "The operator begins to explain", truncated: true }, - ]); - - expect(result.checkpoint.calls).toHaveLength(3); - expect(result.checkpoint.stopReason).toBe("expert-truncated"); - expect(result.checkpoint.interviewerMessages.at(-1)).toEqual({ - role: "user", - content: "The operator begins to explain", - truncated: true, - }); - expect(result.stderr).toContain("expert reply is truncated"); - }); - - test("preserves the condition 2 prompt and completion path", async () => { - const testDirectory = await createBaselineCopy(); - const result = await runBaseline( - testDirectory, - [{ text: "Final structured model" }, { text: "YES" }], - "2", - ); - - expect(result.checkpoint.stopReason).toBe("delivered"); - expect(result.requests[0]?.system).toContain( - "You are an expert process-model elicitor", - ); - }); - - test("resume regenerates a trailing truncated expert reply before continuing", async () => { - const testDirectory = await createBaselineCopy(); - await runBaseline(testDirectory, [ - { text: "What happens next?" }, - { text: "NO" }, - { text: "Partial expert reply", truncated: true }, - ]); - - const resumed = await runBaseline( - testDirectory, - [ - { text: "Complete expert reply" }, - { text: "Final model" }, - { text: "YES" }, - ], - "1", - "--resume", - ); - - expect(resumed.checkpoint.stopReason).toBe("delivered"); - expect(resumed.checkpoint.interviewerMessages).toEqual([ - expect.objectContaining({ role: "user" }), - { role: "assistant", content: "What happens next?" }, - { role: "user", content: "Complete expert reply" }, - { role: "assistant", content: "Final model" }, - ]); - expect(resumed.stderr).toContain("regenerating truncated expert reply"); - }); - - test("checkpoints a capped non-final interviewer reply and stops before calling the expert", async () => { - const testDirectory = await createBaselineCopy(); - const result = await runBaseline(testDirectory, [ - { text: "part-1", truncated: true }, - { text: "part-2", truncated: true }, - { text: "part-3", truncated: true }, - { text: "part-4", truncated: true }, - { text: "part-5", truncated: true }, - { text: "NO" }, - ]); - - expect(result.checkpoint.calls).toHaveLength(6); - expect(result.checkpoint.stopReason).toBe("interviewer-truncated"); - expect(result.checkpoint.interviewerMessages.at(-1)).toEqual({ - role: "assistant", - content: "part-1part-2part-3part-4part-5", - truncated: true, - }); - expect(result.stderr).toContain("non-final interviewer reply is truncated"); - }); - - test("continues a truncated final delivery without sending checkpoint metadata", async () => { - const testDirectory = await createBaselineCopy(); - await runBaseline(testDirectory, [ - { text: "part-1", truncated: true }, - { text: "part-2", truncated: true }, - { text: "part-3", truncated: true }, - { text: "part-4", truncated: true }, - { text: "part-5", truncated: true }, - { text: "YES" }, - ]); - await rm(join(testDirectory.testDirectory, "requests.jsonl")); - - const continued = await runBaseline( - testDirectory, - [{ text: " continued" }], - "1", - "--continue-final", - ); - - expect(continued.requests).toHaveLength(1); - expect(continued.requests[0]?.messages).toEqual([ - expect.objectContaining({ role: "user" }), - { role: "assistant", content: "part-1part-2part-3part-4part-5" }, - { - role: "user", - content: - "You were cut off mid-document. Continue exactly from where you stopped — no preamble, no repetition.", - }, - ]); - for (const message of continued.requests[0]?.messages ?? []) { - expect(Object.keys(message).sort()).toEqual(["content", "role"]); - } - expect(continued.checkpoint.stopReason).toBe("delivered"); - expect(continued.checkpoint.interviewerMessages.at(-1)).toEqual({ - role: "assistant", - content: "part-1part-2part-3part-4part-5 continued", - }); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/architecture/open-gaps.ts b/libs/@hashintel/brunch-agent/packages/core/test/architecture/open-gaps.ts index 6646046903a..bc6de8fc722 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/architecture/open-gaps.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/architecture/open-gaps.ts @@ -42,12 +42,4 @@ export const OPEN_GAPS: readonly OpenGap[] = [ proof: "A test under libs/@hashintel/brunch-agent/packages/binding-flue/test driving one genuine compaction, deep-comparing complete public messages and settlements aside from offset/incarnation, verifying persistent state, and asserting an FE-1391 archive pointer still resolves.", }, - { - id: "interpretation-render-plugin-seam", - spec: "§7.6, §14.5", - ticket: "FE-1394", - gap: "The plugin-supplied renderer seam for the interpretation render has never been exercised, because no real pack exists yet.", - proof: - "A plugin supplying a renderer definition typed against its own payload shapes, with a test driving the interpretation render through it — an exported symbol alone leaves the seam uncrossed.", - }, ]; diff --git a/libs/@hashintel/brunch-agent/packages/core/test/capture-store.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/capture-store.test.ts index 5d7dafea9be..4436ed5e580 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/capture-store.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/capture-store.test.ts @@ -11,12 +11,12 @@ import { type CaptureStoreCommand, type CaptureStoreSnapshot, type EvidenceSpan, -} from "../src/capture-store"; +} from "../src/evidence/capture-store"; import { archiveSessionLogRead, createEmptySessionLogArchive, type EvidenceQuote, -} from "../src/session-log"; +} from "../src/evidence/session-log"; const excerptsByEntry = new Map<number, Set<string>>(); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/completion.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/completion.test.ts deleted file mode 100644 index 2e82204cc97..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/completion.test.ts +++ /dev/null @@ -1,475 +0,0 @@ -/** - * The numbered invariants of `docs/specs/elicitation-completion.md`, one or more - * tests each. Invariants 17–19 (deferral licensing) describe a session-control - * computation over the report and are not implemented by `evaluateCompletion`; - * invariant 19 is checked here only in that the report carries no persisted - * status. - */ - -import { describe, expect, test } from "vitest"; - -import { - completionDemands, - evaluateCompletion, - precisionSatisfies, - type CompletionDemands, -} from "../src/completion"; -import { foldElicitedModel, type ElicitedModel } from "../src/elicited-model"; -import { readPluginDefinition } from "../src/plugin-definition"; -import { - FIXTURE_PLUGIN_YAML, - absence, - assertionCapture, - completeCaptures, - fixturePluginDefinition, - snapshotOf, - value, -} from "./slot-fixtures"; - -import type { CaptureEnvelope } from "../src/capture-store"; - -const definition = fixturePluginDefinition(); -const demands = completionDemands(definition); - -const modelOf = (captures: readonly CaptureEnvelope[]): ElicitedModel => - foldElicitedModel(snapshotOf(captures), definition); - -const without = (id: string): CaptureEnvelope[] => - completeCaptures().filter((capture) => capture.id !== id); - -const replacing = ( - id: string, - replacement: CaptureEnvelope, -): CaptureEnvelope[] => - completeCaptures().map((capture) => - capture.id === id ? replacement : capture, - ); - -const diagnosticsOf = (captures: readonly CaptureEnvelope[]) => - evaluateCompletion(modelOf(captures), demands).failures.map((failure) => [ - failure.diagnostic, - failure.nodeId ?? failure.kind, - failure.slot, - ]); - -describe("shape of the answer (1–2)", () => { - test("1. a complete model yields true with an empty, evidence-bearing report", () => { - const report = evaluateCompletion(modelOf(completeCaptures()), demands); - expect(report.complete).toBe(true); - expect(report.failures).toEqual([]); - expect(report.sliceNodeIds).toEqual([ - "objective:throughput", - "step:stamp", - "thing:press", - "thing:widget", - ]); - // No lifecycle status, no persisted field: only the derived boolean. - expect(Object.keys(report).sort()).toEqual([ - "complete", - "failures", - "outsideSlice", - "pluginVersion", - "revision", - "sliceNodeIds", - ]); - }); - - test("1. each failure names node, slot, requirement, actual state, diagnostic, and captures", () => { - const report = evaluateCompletion( - modelOf( - replacing( - "c-stamp-duration", - assertionCapture( - "c-stamp-duration", - value("step", "stamp", "how long it takes", "range", { - low: 2, - high: 5, - }), - ), - ), - ), - demands, - ); - expect(report.complete).toBe(false); - expect(report.failures).toHaveLength(1); - const [failure] = report.failures; - expect(failure).toMatchObject({ - diagnostic: "below-required-precision", - nodeId: "step:stamp", - kind: "step", - slot: "how long it takes", - requirement: "spread", - actual: "range value under status explicit", - captureIds: ["c-stamp-duration"], - }); - expect(failure?.message).toContain( - "Smallest delta: move it from range to spread", - ); - }); - - test("2. rows from another plugin version are refused with version-mismatch", () => { - const foreign: CompletionDemands = { - ...demands, - pluginVersion: "fixture/2026-09-01.1", - }; - const report = evaluateCompletion(modelOf(completeCaptures()), foreign); - expect(report.complete).toBe(false); - expect(report.failures.map((failure) => failure.diagnostic)).toEqual([ - "version-mismatch", - ]); - expect(report.pluginVersion).toBe("fixture/2026-09-01.1"); - }); -}); - -describe("the rule (3–7)", () => { - test("3. the floor is a count per kind and fails regardless of slot quality", () => { - expect( - diagnosticsOf( - without("c-press-distinctions").filter((c) => c.id !== "c-press-count"), - ), - ).toEqual( - expect.arrayContaining([["below-minimum-count", "thing", undefined]]), - ); - }); - - test("4. presence and slot quality are separate diagnostics", () => { - const diagnostics = diagnosticsOf([ - ...without("c-press-distinctions").filter( - (c) => c.id !== "c-press-count", - ), - assertionCapture( - "c-widget-distinctions-2", - value("thing", "widget", "distinctions", "named", "kinds"), - { supersedes: "c-widget-distinctions" }, - ), - ]); - expect(diagnostics).toEqual( - expect.arrayContaining([ - ["below-minimum-count", "thing", undefined], - ["below-required-precision", "thing:widget", "distinctions"], - ]), - ); - }); - - test("5. nodes outside every objective's slice are recorded, not demanded", () => { - const report = evaluateCompletion( - modelOf([ - ...completeCaptures(), - assertionCapture( - "c-pack-actor", - value("step", "pack", "who performs it", "named", "nobody"), - ), - ]), - demands, - ); - expect(report.complete).toBe(true); - expect(report.outsideSlice).toEqual([ - { - nodeId: "step:pack", - kind: "step", - open: [ - expect.objectContaining({ - diagnostic: "unaddressed", - slot: "how long it takes", - }), - ], - }, - ]); - }); - - test("6. an objective that depends on nothing in the model is unsupported, and dangling names are reported", () => { - const report = evaluateCompletion( - modelOf( - replacing( - "c-objective-deps", - assertionCapture( - "c-objective-deps", - value( - "objective", - "throughput", - "the nodes it depends on", - "named", - ["step:ship"], - ), - ), - ), - ), - demands, - ); - expect(report.failures).toEqual([ - expect.objectContaining({ - diagnostic: "unsupported-active-objective", - nodeId: "objective:throughput", - actual: "0 resolved, 1 naming no node in the model (step:ship)", - }), - ]); - expect(report.sliceNodeIds).toEqual(["objective:throughput"]); - }); - - test("6. a single kind:node string in the dependency slot still forms a slice", () => { - const report = evaluateCompletion( - modelOf( - replacing( - "c-objective-deps", - assertionCapture( - "c-objective-deps", - value( - "objective", - "throughput", - "the nodes it depends on", - "named", - "step:stamp", - ), - ), - ), - ), - demands, - ); - expect( - report.failures.filter( - (failure) => failure.diagnostic === "unsupported-active-objective", - ), - ).toEqual([]); - expect(report.sliceNodeIds).toEqual( - expect.arrayContaining(["objective:throughput", "step:stamp"]), - ); - }); - - test("6. an objective with no dependency slot at all is unsupported; the floor does not substitute", () => { - expect(diagnosticsOf(without("c-objective-deps"))).toEqual([ - [ - "unsupported-active-objective", - "objective:throughput", - "the nodes it depends on", - ], - ]); - }); - - test("7. an empty selection never passes", () => { - expect( - diagnosticsOf( - replacing( - "c-stamp-actor", - assertionCapture( - "c-stamp-actor", - value("step", "stamp", "who performs it", "named", ""), - ), - ), - ), - ).toEqual([["no-selected-slot", "step:stamp", "who performs it"]]); - }); -}); - -describe("what counts as a value (8–14)", () => { - test("8. an inferred value fails under the default accepted statuses, however precise", () => { - const inferred = replacing( - "c-stamp-duration", - assertionCapture( - "c-stamp-duration", - value("step", "stamp", "how long it takes", "spread", { typical: 3 }), - { status: "inferred" }, - ), - ); - expect(diagnosticsOf(inferred)).toEqual([ - ["inadmissible-status", "step:stamp", "how long it takes"], - ]); - const permissive = completionDemands(definition, { - acceptedStatuses: ["explicit", "inferred"], - }); - expect(evaluateCompletion(modelOf(inferred), permissive).complete).toBe( - true, - ); - }); - - test("9. a slot never mentioned fails as unaddressed", () => { - expect(diagnosticsOf(without("c-stamp-duration"))).toEqual([ - ["unaddressed", "step:stamp", "how long it takes"], - ]); - }); - - test("10. 'I don't know' and 'later' leave the slot open, with the pointer kept", () => { - const report = evaluateCompletion( - modelOf( - replacing( - "c-stamp-duration", - assertionCapture( - "c-stamp-duration", - absence( - "step", - "stamp", - "how long it takes", - "deferred", - "the MES log", - ), - ), - ), - ), - demands, - ); - expect(report.failures).toHaveLength(1); - expect(report.failures[0]).toMatchObject({ diagnostic: "unaddressed" }); - expect(report.failures[0]?.message).toContain("pointing at the MES log"); - }); - - test("11. an explicit absence passes only on a row that allows it", () => { - expect( - evaluateCompletion(modelOf(completeCaptures()), demands).complete, - ).toBe(true); // widget "how many" is not-applicable on an allowing row - expect( - diagnosticsOf( - replacing( - "c-stamp-duration", - assertionCapture( - "c-stamp-duration", - absence("step", "stamp", "how long it takes", "explicitly-absent"), - ), - ), - ), - ).toEqual([["unaccepted-absence", "step:stamp", "how long it takes"]]); - }); - - test("12. precision is checked against the row's word, not the number's look", () => { - expect(precisionSatisfies("range", "spread")).toBe(false); - expect(precisionSatisfies("number", "range")).toBe(false); - expect(precisionSatisfies("spread", "range")).toBe(true); - expect(precisionSatisfies("spread", "named")).toBe(true); - expect(precisionSatisfies("spelled out", "number")).toBe(false); - expect(precisionSatisfies("number", "spelled out")).toBe(false); - expect(precisionSatisfies("spelled out", "spelled out")).toBe(true); - expect(precisionSatisfies("spelled out", "named")).toBe(true); - }); - - test("12. any listed precision satisfies one semantic slot", () => { - const anyOfDefinition = readPluginDefinition( - FIXTURE_PLUGIN_YAML.replace( - "precision: spread", - "precision: [spread, spelled out]", - ), - ); - const anyOfDemands = completionDemands(anyOfDefinition); - const reportAt = (precision: "range" | "spread" | "spelled out") => - evaluateCompletion( - foldElicitedModel( - snapshotOf( - replacing( - "c-stamp-duration", - assertionCapture( - "c-stamp-duration", - value( - "step", - "stamp", - "how long it takes", - precision, - precision === "spelled out" - ? { calendar: "weekday shift" } - : { low: 2, high: 5 }, - ), - ), - ), - ), - anyOfDefinition, - ), - anyOfDemands, - ); - - expect(reportAt("spread").complete).toBe(true); - expect(reportAt("spelled out").complete).toBe(true); - expect(reportAt("range").failures).toEqual([ - expect.objectContaining({ - diagnostic: "below-required-precision", - requirement: "spread or spelled out", - }), - ]); - }); - - test("13. conflict and divergence fail conservatively", () => { - expect( - diagnosticsOf([ - ...completeCaptures(), - assertionCapture( - "c-stamp-actor-alt", - value("step", "stamp", "who performs it", "named", "the lead"), - { entry: 9 }, - ), - ]), - ).toEqual([["open-conflict", "step:stamp", "who performs it"]]); - expect( - diagnosticsOf([ - ...without("c-stamp-actor"), - assertionCapture( - "c-manual", - value("step", "stamp", "who performs it", "named", "the operator", { - sourceRegime: "prescribed", - }), - ), - assertionCapture( - "c-floor", - value( - "step", - "stamp", - "who performs it", - "named", - "whoever is free", - { sourceRegime: "practiced" }, - ), - { entry: 2 }, - ), - ]), - ).toEqual([["unresolved-divergence", "step:stamp", "who performs it"]]); - }); - - test("14. a value whose support is not active and traceable fails as missing-evidence", () => { - const model = modelOf(completeCaptures()); - const stamp = model.nodes.find((node) => node.id === "step:stamp")!; - const tampered: ElicitedModel = { - ...model, - nodes: model.nodes.map((node) => - node.id === "step:stamp" - ? { - ...node, - slots: { - ...node.slots, - "who performs it": { - ...stamp.slots["who performs it"]!, - evidenced: false, - }, - }, - } - : node, - ), - }; - expect( - evaluateCompletion(tampered, demands).failures.map( - (failure) => failure.diagnostic, - ), - ).toEqual(["missing-evidence"]); - }); -}); - -describe("what leaves the boolean untouched (15–16)", () => { - test("15. the function is pure in (model, demands): the same inputs give the same report", () => { - const model = modelOf(completeCaptures()); - expect(evaluateCompletion(model, demands)).toEqual( - evaluateCompletion(model, demands), - ); - expect(evaluateCompletion.length).toBe(2); - }); - - test("16. a later capture can make a complete document incomplete", () => { - const before = evaluateCompletion(modelOf(completeCaptures()), demands); - const after = evaluateCompletion( - modelOf([ - ...completeCaptures(), - assertionCapture( - "c-widget-count-later", - value("thing", "widget", "how many", "number", 40), - { entry: 11 }, - ), - ]), - demands, - ); - expect(before.complete).toBe(true); - expect(after.complete).toBe(false); - expect(after.failures[0]?.diagnostic).toBe("open-conflict"); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/cue.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/cue.test.ts deleted file mode 100644 index 8f4e554819b..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/cue.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { completionDemands, evaluateCompletion } from "../src/completion"; -import { buildCompletionCueSignal, buildSweepList } from "../src/cue"; -import { foldElicitedModel } from "../src/elicited-model"; -import { HARNESS_PREAMBLE } from "../src/instructions"; -import { - assertionCapture, - completeCaptures, - fixturePluginDefinition, - snapshotOf, - value, -} from "./slot-fixtures"; - -const definition = fixturePluginDefinition(); -const demands = completionDemands(definition); - -describe("the sweep list", () => { - test("pairs every failure with the patterns indexed on the failing node's kind", () => { - const model = foldElicitedModel( - snapshotOf(completeCaptures().filter((c) => c.id !== "c-stamp-duration")), - definition, - ); - const report = evaluateCompletion(model, demands); - const list = buildSweepList(model, report, definition.patterns); - expect(list.unsatisfied.map((f) => f.diagnostic)).toEqual(["unaddressed"]); - expect(list.patterns).toEqual([ - { id: "P01", nodeId: "step:stamp", ask: "ask how often" }, - { id: "P03", nodeId: "step:stamp", ask: "ask for a source" }, - ]); - }); - - test("a pattern indexed on no kind fires on a failing node of any kind", () => { - // Fixture P03 is `on: []` — the contract's "any node". Fail a `thing` - // instead of a `step`: P02 (on thing) and P03 surface, P01 (on step) not. - const model = foldElicitedModel( - snapshotOf( - completeCaptures().filter((c) => c.id !== "c-widget-distinctions"), - ), - definition, - ); - const report = evaluateCompletion(model, demands); - const list = buildSweepList(model, report, definition.patterns); - const failing = list.unsatisfied.map((f) => f.nodeId); - expect(failing.every((id) => id?.startsWith("thing:"))).toBe(true); - expect(list.patterns.map((cue) => cue.id)).toEqual(["P02", "P03"]); - expect(list.patterns.map((cue) => cue.nodeId)).toEqual([ - failing[0], - failing[0], - ]); - }); - - test("a slot-scoped pattern does not fire for another failure on the same node", () => { - const model = foldElicitedModel( - snapshotOf( - completeCaptures().filter((capture) => capture.id !== "c-stamp-actor"), - ), - definition, - ); - const report = evaluateCompletion(model, demands); - const list = buildSweepList(model, report, definition.patterns); - expect(list.unsatisfied.map((failure) => failure.slot)).toEqual([ - "who performs it", - ]); - expect(list.patterns.map((cue) => cue.id)).toEqual(["P03"]); - }); - - test("surfaces nothing for a complete model", () => { - const model = foldElicitedModel(snapshotOf(completeCaptures()), definition); - const list = buildSweepList( - model, - evaluateCompletion(model, demands), - definition.patterns, - ); - expect(list).toEqual({ unsatisfied: [], patterns: [] }); - }); -}); - -describe("the cue signal", () => { - test("states the revision, the verdict, each unsatisfied slot, and discretionary patterns", () => { - const model = foldElicitedModel( - snapshotOf([ - ...completeCaptures().filter((c) => c.id !== "c-stamp-duration"), - assertionCapture( - "c-pack-actor", - value("step", "pack", "who performs it", "named", "nobody"), - ), - ]), - definition, - ); - const report = evaluateCompletion(model, demands); - const signal = buildCompletionCueSignal( - model, - report, - buildSweepList(model, report, definition.patterns), - ); - expect(signal.type).toBe("completion-cue"); - expect(signal.body).toContain(`revision ${report.revision}`); - expect(signal.body).toContain("Complete: no"); - expect(signal.body).toContain("[unaddressed]"); - expect(signal.body).toContain("P01 on step:stamp: ask how often"); - expect(signal.body).toContain( - "1 node(s) lie outside every objective's dependency slice", - ); - expect(signal.body).toContain("does not decide whether to continue"); - }); - - test("truncates long lists and says how many it left out", () => { - const captures = completeCaptures().filter( - (c) => - ![ - "c-stamp-duration", - "c-stamp-actor", - "c-widget-distinctions", - "c-press-distinctions", - ].includes(c.id), - ); - const model = foldElicitedModel(snapshotOf(captures), definition); - const report = evaluateCompletion(model, demands); - const signal = buildCompletionCueSignal( - model, - report, - buildSweepList(model, report, definition.patterns), - { - maxItems: 2, - }, - ); - expect(report.failures.length).toBeGreaterThan(2); - expect(signal.body).toContain(`and ${report.failures.length - 2} more`); - }); - - test("the harness preamble is render-invariant prose", () => { - for (const fragment of HARNESS_PREAMBLE) { - expect(fragment).not.toMatch(/\$\{|revision [0-9a-f]/u); - } - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/elicitation-skill.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/elicitation-skill.test.ts new file mode 100644 index 00000000000..943b18df4cc --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/core/test/elicitation-skill.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from "vitest"; + +import { elicitationSkill } from "../src/skills/elicitation/skill"; +import { skillFromMarkdown } from "../src/skills/skill-markdown"; + +describe("the authored elicitation skill", () => { + test("loads its universal guidance on activation without a mandatory resource read", () => { + expect(elicitationSkill.name).toBe("elicitation"); + expect(elicitationSkill.files).toBeUndefined(); + expect(elicitationSkill.instructions).toContain("# Adaptive elicitation"); + expect(elicitationSkill.instructions).toContain("## Directives"); + expect(elicitationSkill.instructions).toContain("## Operations"); + expect(elicitationSkill.instructions).toContain("## Coverage"); + expect(elicitationSkill.instructions).toContain("## Verification"); + expect(elicitationSkill.instructions).not.toContain( + "references/universal-elicitation.md", + ); + }); + + test("parses frontmatter fields without interpreting field names as patterns", () => { + const skill = skillFromMarkdown( + "---\r\nname: example\r\ndescription: Example skill\r\n---\r\nDo the work.\r\n", + ); + + expect(skill).toMatchObject({ + name: "example", + description: "Example skill", + instructions: "Do the work.", + }); + }); +}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/elicited-model.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/elicited-model.test.ts deleted file mode 100644 index 567ea814183..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/elicited-model.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { findNode, foldElicitedModel } from "../src/elicited-model"; -import { - absence, - assertionCapture, - completeCaptures, - fixturePluginDefinition, - snapshotOf, - value, -} from "./slot-fixtures"; - -import type { JsonValue } from "../src/json-value"; - -const definition = fixturePluginDefinition(); - -describe("the fold reads only active captures", () => { - test("groups assertions into nodes and slots keyed by kind:node", () => { - const model = foldElicitedModel(snapshotOf(completeCaptures()), definition); - expect(model.pluginVersion).toBe("fixture/2026-08-25.1"); - expect(model.nodes.map((node) => node.id)).toEqual([ - "objective:throughput", - "step:stamp", - "thing:press", - "thing:widget", - ]); - expect(findNode(model, "step:stamp")?.slots["who performs it"]).toEqual({ - state: "value", - value: "the press operator", - precision: "named", - status: "explicit", - evidenced: true, - captureIds: ["c-stamp-actor"], - }); - expect(findNode(model, "thing:widget")?.slots["how many"]).toEqual({ - state: "absence", - absence: "not-applicable", - status: "explicit", - evidenced: true, - captureIds: ["c-widget-count"], - }); - expect(model.unmapped).toEqual([]); - }); - - test("a superseding capture replaces its target; a retracted capture disappears", () => { - const captures = [ - ...completeCaptures(), - assertionCapture( - "c-stamp-actor-2", - value("step", "stamp", "who performs it", "named", "the line lead"), - { supersedes: "c-stamp-actor", entry: 7 }, - ), - ]; - const model = foldElicitedModel( - snapshotOf( - captures, - [], - [ - { - type: "retraction", - id: "r-1", - captureId: "c-press-count", - evidence: [ - { - excerpt: "forget the press count", - pointer: { sessionId: "session-1", entryStart: 8, entryEnd: 8 }, - source: "user", - }, - ], - }, - ], - ), - definition, - ); - const actor = findNode(model, "step:stamp")?.slots["who performs it"]; - expect(actor?.state).toBe("value"); - expect(actor?.captureIds).toEqual(["c-stamp-actor-2"]); - expect(findNode(model, "thing:press")?.slots["how many"]).toBeUndefined(); - expect(model.activeCaptureIds.has("c-stamp-actor")).toBe(false); - expect(model.activeCaptureIds.has("c-press-count")).toBe(false); - }); - - test("never interprets: an unreadable payload and an envelope-level absence are unmapped", () => { - const stray = { - ...assertionCapture( - "c-stray", - value("thing", "widget", "colour", "named", "blue"), - ), - }; - const envelopeAbsence = assertionCapture( - "c-envelope-absence", - value("thing", "widget", "distinctions", "named", "x"), - ); - const model = foldElicitedModel( - snapshotOf([ - stray, - { - ...envelopeAbsence, - content: { absence: "unknown-to-user" }, - dedupKey: "manual-key", - }, - { - ...assertionCapture( - "c-free", - value("thing", "widget", "how many", "range", 1), - ), - content: { value: { free: "text" } as JsonValue }, - dedupKey: "manual-key-2", - }, - ]), - definition, - ); - expect(model.nodes).toEqual([]); - expect(model.unmapped.map((entry) => entry.captureId).sort()).toEqual([ - "c-envelope-absence", - "c-free", - "c-stray", - ]); - expect( - model.unmapped.find((entry) => entry.captureId === "c-stray")?.reason, - ).toMatch(/not a `Must know` row/u); - }); -}); - -describe("competing readings", () => { - test("two different active values on one slot are a conflict, and an open conflict issue pins one", () => { - const captures = [ - assertionCapture( - "c-a", - value("step", "stamp", "who performs it", "named", "Ann"), - ), - assertionCapture( - "c-b", - value("step", "stamp", "who performs it", "named", "Bob"), - { - entry: 2, - }, - ), - assertionCapture( - "c-c", - value("thing", "widget", "distinctions", "spelled out", ["x"]), - ), - assertionCapture( - "c-d", - value("thing", "widget", "distinctions", "spelled out", ["x"]), - { entry: 3 }, - ), - ]; - const model = foldElicitedModel( - snapshotOf(captures, [ - { - id: "issue-1", - type: "conflicting", - origin: { type: "harness" }, - references: ["c-c", "c-d"], - canDefault: false, - }, - ]), - definition, - ); - expect(findNode(model, "step:stamp")?.slots["who performs it"]?.state).toBe( - "conflict", - ); - // Identical readings would merge, but the open issue keeps the slot in conflict. - expect(findNode(model, "thing:widget")?.slots.distinctions?.state).toBe( - "conflict", - ); - }); - - test("identical readings merge into one value citing every capture", () => { - const captures = [ - assertionCapture( - "c-a", - value("step", "stamp", "who performs it", "named", "Ann"), - ), - assertionCapture( - "c-b", - value("step", "stamp", "who performs it", "named", "Ann"), - { - entry: 2, - status: "inferred", - }, - ), - ]; - const slot = findNode( - foldElicitedModel(snapshotOf(captures), definition), - "step:stamp", - )?.slots["who performs it"]; - expect(slot).toMatchObject({ - state: "value", - status: "explicit", - captureIds: ["c-a", "c-b"], - }); - }); - - test("a prescribed and a practiced reading that differ are a divergence, not a conflict", () => { - const captures = [ - assertionCapture( - "c-manual", - value("step", "stamp", "who performs it", "named", "the operator", { - sourceRegime: "prescribed", - }), - ), - assertionCapture( - "c-floor", - value("step", "stamp", "who performs it", "named", "whoever is free", { - sourceRegime: "practiced", - }), - { entry: 2 }, - ), - ]; - const slot = findNode( - foldElicitedModel(snapshotOf(captures), definition), - "step:stamp", - )?.slots["who performs it"]; - expect(slot?.state).toBe("divergence"); - expect(slot?.captureIds).toEqual(["c-manual", "c-floor"]); - }); - - test("an absence is one reading like any other", () => { - const captures = [ - assertionCapture( - "c-a", - absence( - "step", - "stamp", - "how long it takes", - "unknown-to-user", - "the MES log", - ), - ), - ]; - expect( - findNode( - foldElicitedModel(snapshotOf(captures), definition), - "step:stamp", - )?.slots["how long it takes"], - ).toEqual({ - state: "absence", - absence: "unknown-to-user", - pointer: "the MES log", - status: "explicit", - evidenced: true, - captureIds: ["c-a"], - }); - }); -}); - -describe("revision", () => { - test("is stable for the same active set and changes when it changes", () => { - const base = completeCaptures(); - const first = foldElicitedModel(snapshotOf(base), definition).revision; - const again = foldElicitedModel( - snapshotOf([...base].reverse()), - definition, - ).revision; - const grown = foldElicitedModel( - snapshotOf([ - ...base, - assertionCapture( - "c-extra", - value("step", "pack", "who performs it", "named", "nobody"), - ), - ]), - definition, - ).revision; - expect(again).toBe(first); - expect(grown).not.toBe(first); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/instructions.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/instructions.test.ts deleted file mode 100644 index a5f26055583..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/instructions.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { - HARNESS_PREAMBLE, - renderGuidance, - renderInstructions, - renderRunbook, -} from "../src/instructions"; -import { - GUIDANCE_KEY_DESCRIPTIONS, - GUIDANCE_KEYS, - RUNBOOK_KEY_DESCRIPTIONS, - RUNBOOK_KEYS, -} from "../src/keys"; -import { readPluginDefinition } from "../src/plugin-definition"; -import { readRepertoire, type Repertoire } from "../src/repertoire"; -import { FIXTURE_PLUGIN_YAML, fixturePluginDefinition } from "./slot-fixtures"; - -const item = (name: string) => - ` - { name: ${name}, text: default ${name}., source: test }`; -const cell = (path: string) => ` ${path}:\n${item(`${path} default`)}`; - -/** A minimal repertoire: one sourced default under every key. */ -const REPERTOIRE_YAML = `repertoire: - version: repertoire/2026-08-25.1 - purpose: test -guidance: - lenses: -${item("lenses default")} - techniques: -${item("techniques default")} - movements: - slice: -${item("slice default")} - sweep: -${item("sweep default")} - licenses: -${item("licenses default")} - motifs: -${item("motifs default")} - smells: -${item("smells default")} - rabbit_holes: -${item("rabbit_holes default")} - failure_modes: - - { name: failure default, text: default failure., signature: a sign, source: test } -runbooks: - construct: -${cell("kickoff")} -${cell("trajectory")} -${cell("close")} - review-and-revise: -${cell("kickoff")} -${cell("trajectory")} -${cell("close")} -`; - -const repertoire: Repertoire = readRepertoire(REPERTOIRE_YAML); -const definition = fixturePluginDefinition(); - -const indexOfAll = (text: string, needles: readonly string[]): number[] => - needles.map((needle) => text.indexOf(needle)); - -const ascending = (positions: readonly number[]): boolean => - positions.every( - (position, index) => - position >= 0 && (index === 0 || position > positions[index - 1]!), - ); - -describe("renderInstructions", () => { - const text = renderInstructions(repertoire, definition); - - test("opens with what the harness enforces, then the contract, then guidance, then runbooks", () => { - expect( - ascending( - indexOfAll(text, [ - "## What the harness enforces", - HARNESS_PREAMBLE[0]!, - "## Purpose", - "## Kinds", - "## Must know", - "## Patterns", - `## ${GUIDANCE_KEY_DESCRIPTIONS.lenses.title}`, - `## ${GUIDANCE_KEY_DESCRIPTIONS.failure_modes.title}`, - "## Job: construct", - `### ${RUNBOOK_KEY_DESCRIPTIONS.close.title}`, - ]), - ), - ).toBe(true); - }); - - test("renders every guidance key in catalogue order: definition, default, then the plugin cell", () => { - const positions = indexOfAll( - text, - GUIDANCE_KEYS.map((key) => `## ${GUIDANCE_KEY_DESCRIPTIONS[key].title}`), - ); - expect(ascending(positions)).toBe(true); - expect( - ascending( - indexOfAll(text, [ - `## ${GUIDANCE_KEY_DESCRIPTIONS.lenses.title}`, - GUIDANCE_KEY_DESCRIPTIONS.lenses.definition, - "**lenses default**", - "**fixture lens**", - ]), - ), - ).toBe(true); - }); - - test("renders a blank plugin cell as the default alone, never as an empty heading", () => { - // The fixture leaves `techniques` blank: the default is the whole key. - const section = renderGuidance(repertoire, definition).find((part) => - part.startsWith(`## ${GUIDANCE_KEY_DESCRIPTIONS.techniques.title}`), - )!; - expect(section).toContain("**techniques default**"); - expect(section).not.toMatch(/\n\n$/u); - }); - - test("renders a repertoire item only when the plugin demands an applicable precision", () => { - const conditionalRepertoire = readRepertoire( - REPERTOIRE_YAML.replace( - item("techniques default"), - " - { name: techniques default, text: default techniques default., source: test, for_precision: [range, spread] }", - ).replace( - item("kickoff default"), - " - { name: kickoff default, text: default kickoff default., source: test, for_precision: [range, spread] }", - ), - ); - const nonNumericDefinition = readPluginDefinition( - FIXTURE_PLUGIN_YAML.replace( - "precision: range", - "precision: named", - ).replace("precision: spread", "precision: spelled out"), - ); - - expect( - renderGuidance(conditionalRepertoire, definition).join("\n"), - ).toContain("**techniques default**"); - expect( - renderGuidance(conditionalRepertoire, nonNumericDefinition).join("\n"), - ).not.toContain("**techniques default**"); - expect( - renderRunbook(conditionalRepertoire, definition, "construct"), - ).toContain("**kickoff default**"); - expect( - renderRunbook(conditionalRepertoire, nonNumericDefinition, "construct"), - ).not.toContain("**kickoff default**"); - }); - - test("splits movements into slice and sweep", () => { - expect( - ascending( - indexOfAll(text, [ - "### Slice", - "**slice default**", - "### Sweep", - "**sweep default**", - "**fixture sweep**", - ]), - ), - ).toBe(true); - }); - - test("renders a runbook only for the jobs the plugin declares", () => { - expect(text).toContain("## Job: construct"); - expect(text).not.toContain("## Job: review and revise"); - const runbook = renderRunbook(repertoire, definition, "construct"); - expect( - ascending( - indexOfAll( - runbook, - RUNBOOK_KEYS.map( - (key) => `### ${RUNBOOK_KEY_DESCRIPTIONS[key].title}`, - ), - ), - ), - ).toBe(true); - expect(runbook).toContain("**kickoff default**"); - expect(runbook).toContain("**fixture kickoff**"); - }); - - test("renders the contract from data: rows by kind, the floor, the declared anchor, the precision ladder", () => { - expect(text).toContain("the nodes it depends on — at least 1"); - expect(text).toContain('how many — range; "not applicable" is accepted'); - expect(text).toContain("at least 1 `objective`, 2 `thing`, 1 `step`"); - expect(text).toContain("before anything `objective`-relative counts"); - expect(text).toContain("completion is relative to `objective` nodes"); - expect(text).toContain("`spelled out` —"); - expect(text).toContain("**P02** — _when_ more than one thing competes"); - expect(text).toContain("_Signature:_ it says so"); - }); - - test("does not hardcode objective-relative completion when the anchor is another kind", () => { - const featureAnchored = readPluginDefinition( - FIXTURE_PLUGIN_YAML.replaceAll("objective", "feature"), - ); - const rendered = renderInstructions(repertoire, featureAnchored); - expect(rendered).toContain("before anything `feature`-relative counts"); - expect(rendered).toContain("completion is relative to `feature` nodes"); - expect(rendered).not.toContain("objective-relative"); - }); - - test("renders alternative demanded precisions as any-of", () => { - const withAlternatives = readPluginDefinition( - FIXTURE_PLUGIN_YAML.replace( - "precision: spread", - "precision: [spread, spelled out]", - ), - ); - expect(renderInstructions(repertoire, withAlternatives)).toContain( - "how long it takes — spread or spelled out", - ); - }); - - test("contains no template residue", () => { - expect(text).not.toMatch(/\$\{|undefined|\[object Object\]/u); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/naming.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/naming.test.ts index 4113e30b975..003926062a6 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/naming.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/naming.test.ts @@ -7,7 +7,7 @@ import { toolPrefix, type Operation, type ToolName, -} from "../src/naming"; +} from "../src/conversation/naming"; // Spec §12.3: architectural strings name identity, not function. The tool // prefix derives from the product name so the unresolved name-fog costs one diff --git a/libs/@hashintel/brunch-agent/packages/core/test/plugin-definition.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/plugin-definition.test.ts deleted file mode 100644 index c278977ffa6..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/plugin-definition.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { readFileSync } from "node:fs"; -import { join } from "node:path"; - -import { describe, expect, test } from "vitest"; - -import { GUIDANCE_KEYS, RUNBOOK_KEYS } from "../src/keys"; -import { - guidanceEntries, - mustKnowRowsFor, - PluginDefinitionError, - readPluginDefinition, - runbookEntries, - type PluginDefinition, -} from "../src/plugin-definition"; -import { CONTEXT_ROOT, contextRootPresent } from "./architecture/context-root"; -import { FIXTURE_PLUGIN_YAML, fixturePluginDefinition } from "./slot-fixtures"; - -describe("the synthetic fixture definition", () => { - const definition = fixturePluginDefinition(); - - test("reads the identity block and the kind catalog", () => { - expect(definition.version).toBe("fixture/2026-08-25.1"); - expect(definition.identity).toEqual({ - id: "fixture", - formalism: "fixture", - jobs: ["construct"], - purpose: "Interview someone about things and steps.", - }); - expect(definition.kinds.map((row) => row.kind)).toEqual([ - "objective", - "thing", - "step", - ]); - expect(definition.ontology.notKinds.map((row) => row.name)).toEqual([ - "queue", - ]); - }); - - test("reads demand rows with typed precision and the not-applicable flag", () => { - expect(mustKnowRowsFor(definition, "objective")).toEqual([ - { - kind: "objective", - slot: "the question", - precision: { kind: "word", word: "spelled out" }, - notApplicableAllowed: false, - why: "anchor", - }, - { - kind: "objective", - slot: "the nodes it depends on", - precision: { kind: "at-least", count: 1 }, - notApplicableAllowed: false, - why: "slice", - }, - ]); - expect(mustKnowRowsFor(definition, "thing")[1]?.notApplicableAllowed).toBe( - true, - ); - }); - - test("reads a list of alternative demanded precision words", () => { - const withAlternatives = readPluginDefinition( - FIXTURE_PLUGIN_YAML.replace( - "precision: spread", - "precision: [spread, spelled out]", - ), - ); - expect( - mustKnowRowsFor(withAlternatives, "step").find( - (row) => row.slot === "how long it takes", - )?.precision, - ).toEqual({ - kind: "any-of", - words: ["spread", "spelled out"], - }); - }); - - test("reads the anchor as a declaration, not a convention", () => { - expect(definition.anchor).toEqual({ - kind: "objective", - dependencySlot: "the nodes it depends on", - }); - expect(definition.floor).toEqual([ - { kind: "objective", atLeast: 1 }, - { kind: "thing", atLeast: 2 }, - { kind: "step", atLeast: 1 }, - ]); - }); - - test("indexes patterns by the kinds their trigger names", () => { - expect(definition.patterns.map((row) => [row.id, row.kinds])).toEqual([ - ["P01", ["step"]], - ["P02", ["thing"]], - ["P03", []], - ]); - }); - - test("reads a pattern predicate on one of its kinds' demanded slots", () => { - expect(definition.patterns.at(0)).toMatchObject({ - id: "P01", - slot: "how long it takes", - }); - }); - - test("flattens guidance and runbook cells with their key paths", () => { - expect( - guidanceEntries(definition.guidance).map((entry) => entry.path), - ).toEqual(["lenses", "movements.sweep", "failure_modes"]); - expect( - runbookEntries(definition.runbooks).map((entry) => entry.path), - ).toEqual(["construct.kickoff"]); - }); -}); - -describe("contract violations fail to load", () => { - test.each([ - [ - "a key the harness does not own", - FIXTURE_PLUGIN_YAML.replace("guidance:\n", "guidance:\n hints: []\n"), - /hints/u, - ], - [ - "a missing group", - FIXTURE_PLUGIN_YAML.replace(/machinery:[\s\S]*$/u, ""), - /machinery/u, - ], - [ - "a malformed version", - FIXTURE_PLUGIN_YAML.replace("fixture/2026-08-25.1", "fixture-1"), - /yyyy-mm-dd/u, - ], - [ - "a demand row for an unknown kind", - FIXTURE_PLUGIN_YAML.replace( - "{ kind: step, slot: who performs it", - "{ kind: queue, slot: who performs it", - ), - /`queue`, which is not in `ontology.kinds`/u, - ], - [ - "an unknown precision word", - FIXTURE_PLUGIN_YAML.replace("precision: spread", "precision: roughly"), - /precision/u, - ], - [ - "a kind with no demand row", - FIXTURE_PLUGIN_YAML.replace(/ {4}- \{ kind: step, slot[^\n]*\n/gu, ""), - /no row for kind `step`/u, - ], - [ - "an anchor slot that is not a row", - FIXTURE_PLUGIN_YAML.replace( - "depends_on: the nodes it depends on", - "depends_on: the things it needs", - ), - /not a `must_know` row/u, - ], - [ - "an anchor slot that is not a count", - FIXTURE_PLUGIN_YAML.replace( - "depends_on: the nodes it depends on", - "depends_on: the question", - ), - /at least N/u, - ], - [ - "a pattern on an unknown kind", - FIXTURE_PLUGIN_YAML.replace("on: [step]", "on: [queue]"), - /pattern P01 names kind `queue`/u, - ], - [ - "a pattern predicate on a slot its kind does not demand", - FIXTURE_PLUGIN_YAML.replace( - "on: [step], slot: how long it takes", - "on: [step], slot: queue capacity", - ), - /pattern P01 names slot `queue capacity`, which `step` does not demand/u, - ], - [ - "a wildcard pattern predicate on a slot no kind demands", - FIXTURE_PLUGIN_YAML.replace( - "id: P03, on: [], when:", - "id: P03, on: [], slot: queue capacity, when:", - ), - /pattern P03 names slot `queue capacity`, which no kind demands/u, - ], - [ - "a runbook for an undeclared job", - FIXTURE_PLUGIN_YAML.replace( - "runbooks:\n", - "runbooks:\n review-and-revise: { kickoff: [], trajectory: [], close: [] }\n", - ), - /`plugin.jobs` does not declare it/u, - ], - [ - "a guidance item without a name", - FIXTURE_PLUGIN_YAML.replace( - "{ name: fixture lens, text: Notice things. }", - "{ text: Notice things. }", - ), - /guidance.lenses.0.name/u, - ], - ["text that is not YAML", "plugin: [", /not valid YAML/u], - ])("%s", (_label, yaml, message) => { - expect(() => readPluginDefinition(yaml)).toThrow(PluginDefinitionError); - expect(() => readPluginDefinition(yaml)).toThrow(message); - }); -}); - -const readShipped = (packageName: string): PluginDefinition => - readPluginDefinition( - readFileSync( - join(CONTEXT_ROOT, "packages", packageName, "plugin.yaml"), - "utf8", - ), - ); - -/** Words that would mean the plugin knows a domain rather than a formalism. */ -const DOMAIN_WORDS = - /\b(hospital|patient|coating|vestera|truck|packaging|warehouse|factory|bakery|clinic)\b/iu; - -describe.skipIf(!contextRootPresent)("the shipped plugin definitions", () => { - test.each(["plugin-sdcpn", "plugin-gherkin"])( - "%s validates, adds no key, and names no domain", - (packageName) => { - const definition = readShipped(packageName); - expect(definition.identity.id).toBe(packageName.replace("plugin-", "")); - expect(definition.kinds.length).toBeGreaterThan(0); - expect( - definition.mustKnow.some( - (row) => - row.kind === definition.anchor.kind && - row.slot === definition.anchor.dependencySlot, - ), - ).toBe(true); - const text = JSON.stringify(definition); - expect(text).not.toMatch(DOMAIN_WORDS); - for (const key of GUIDANCE_KEYS) { - expect(definition.guidance).toHaveProperty(key); - } - for (const job of definition.identity.jobs) { - const cells = definition.runbooks[job]; - expect( - cells === undefined || RUNBOOK_KEYS.every((key) => key in cells), - ).toBe(true); - } - }, - ); - - test("the two plugins declare different anchors under the same schema", () => { - const sdcpn = readShipped("plugin-sdcpn"); - const gherkin = readShipped("plugin-gherkin"); - expect(sdcpn.anchor.kind).not.toBe(gherkin.anchor.kind); - expect(sdcpn.proposals.map((p) => p.type)).toEqual(["slot-asserted"]); - }); - - test("SDCPN accepts structural alternatives to numeric precision where the slot permits either", () => { - const sdcpn = readShipped("plugin-sdcpn"); - expect( - sdcpn.mustKnow - .filter((row) => - [ - 'what "better" means, and trade-off weights', - "the arrival or availability pattern", - ].includes(row.slot), - ) - .map((row) => [row.slot, row.precision]), - ).toEqual([ - [ - 'what "better" means, and trade-off weights', - { kind: "any-of", words: ["range", "spelled out"] }, - ], - [ - "the arrival or availability pattern", - { kind: "any-of", words: ["spread", "spelled out"] }, - ], - ]); - }); - - test("ambiguous kind-indexed patterns declare the slot that keeps them live", () => { - const sdcpn = readShipped("plugin-sdcpn"); - const gherkin = readShipped("plugin-gherkin"); - expect( - sdcpn.patterns - .filter((pattern) => ["P01", "P02"].includes(pattern.id)) - .map((pattern) => [pattern.id, pattern.slot]), - ).toEqual([ - ["P01", "how often it occurs, if it is an event rather than a step"], - ["P02", "what is lost when it changes the system's mode"], - ]); - expect( - gherkin.patterns - .filter((pattern) => ["P01", "P03"].includes(pattern.id)) - .map((pattern) => [pattern.id, pattern.slot]), - ).toEqual([ - ["P01", "the examples that illustrate it"], - ["P03", "the observable outcome"], - ]); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/plugin-schema.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/plugin-schema.test.ts deleted file mode 100644 index 2bb144c4d5d..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/plugin-schema.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { readFileSync, writeFileSync } from "node:fs"; -import { fileURLToPath } from "node:url"; - -import { expect, test } from "vitest"; - -import { pluginJsonSchema } from "../src/plugin-json-schema"; - -const target = fileURLToPath( - new URL("../schema/plugin.schema.json", import.meta.url), -); -const emitting = process.env.PLUGIN_SCHEMA_EMIT === "1"; - -/** - * `schema/plugin.schema.json` is the emitted view of `PluginDefinitionSchema`. - * `yarn schema:emit` rewrites it after a deliberate schema change (and the - * change goes in `schema/CHANGELOG.md`); an unrewritten drift fails here. The - * comparison is structural so that the repo formatter may lay the file out. - */ -test.runIf(emitting)("emits schema/plugin.schema.json", () => { - writeFileSync(target, `${JSON.stringify(pluginJsonSchema(), null, 2)}\n`); - expect(true).toBe(true); -}); - -test.skipIf(emitting)( - "schema/plugin.schema.json is the emitted view of PluginDefinitionSchema", - () => { - const committed = JSON.parse(readFileSync(target, "utf8")) as unknown; - expect(committed).toEqual(pluginJsonSchema()); - }, -); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/prompts.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/prompts.test.ts deleted file mode 100644 index 5f4e8740b57..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/prompts.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { readFileSync } from "node:fs"; - -import { describe, expect, test } from "vitest"; - -import { GUIDANCE_KEYS, JOBS, MOVEMENTS, RUNBOOK_KEYS } from "../src/keys"; -import { - guidanceEntries, - readPluginDefinition, - runbookEntries, -} from "../src/plugin-definition"; -import { repertoire } from "../src/prompts"; - -/** Words that would mean the repertoire teaches a formalism or a domain. */ -const FORMALISM_OR_DOMAIN = - /\b(petri|transition|place|token|sdcpn|gherkin|scenario|feature|hospital|coating|truck|packaging)\b/iu; - -const sentences = (text: string): string[] => - text - .split(/(?<=[.!?])\s+/u) - .map((sentence) => sentence.toLowerCase().replace(/\s+/gu, " ").trim()) - .filter((sentence) => sentence.length >= 40); - -describe("the shipped repertoire", () => { - test("fills every guidance key, both movements, and every runbook key of every job", () => { - const guidancePaths = new Set( - guidanceEntries(repertoire.guidance).map((entry) => entry.path), - ); - const expectedGuidance = GUIDANCE_KEYS.flatMap((key) => - key === "movements" - ? MOVEMENTS.map((movement) => `movements.${movement}`) - : [key], - ); - expect([...guidancePaths].sort()).toEqual([...expectedGuidance].sort()); - - const runbookPaths = new Set( - runbookEntries(repertoire.runbooks).map((entry) => entry.path), - ); - const expectedRunbooks = JOBS.flatMap((job) => - RUNBOOK_KEYS.map((key) => `${job}.${key}`), - ); - expect([...runbookPaths].sort()).toEqual([...expectedRunbooks].sort()); - }); - - test("every entry names its source and gives a failure mode its signature", () => { - const entries = [ - ...guidanceEntries(repertoire.guidance), - ...runbookEntries(repertoire.runbooks), - ]; - expect(entries.length).toBeGreaterThan(20); - expect( - entries - .filter(({ item }) => item.source === undefined) - .map(({ path, item }) => `${path}: ${item.name}`), - ).toEqual([]); - expect( - entries - .filter( - ({ path, item }) => - path === "failure_modes" && item.signature === undefined, - ) - .map(({ item }) => item.name), - ).toEqual([]); - }); - - test("teaches the harness's concepts, not a formalism or a domain", () => { - const text = [ - ...guidanceEntries(repertoire.guidance), - ...runbookEntries(repertoire.runbooks), - ] - .map(({ item }) => `${item.name} ${item.text} ${item.signature ?? ""}`) - .join("\n"); - expect(text).not.toMatch(FORMALISM_OR_DOMAIN); - }); - - test("conditions quantity and observed-practice methods on compatible precision demands", () => { - const entries = [ - ...guidanceEntries(repertoire.guidance), - ...runbookEntries(repertoire.runbooks), - ]; - expect( - entries - .filter(({ item }) => item.forPrecision !== undefined) - .map(({ item }) => [item.name, item.forPrecision]), - ).toEqual([ - ["Policy versus practice", ["range", "spread"]], - ["Mean or tail", ["number", "range", "spread"]], - ["Quantiles, never three points", ["spread"]], - ["The clairvoyant test", ["number", "range", "spread"]], - ["Premortem", ["range", "spread"]], - ["One incident is not a rate", ["range", "spread"]], - ["One property across one stratum", ["number", "range", "spread"]], - ["Quantify better when relevant", ["number", "range", "spread"]], - ]); - }); - - test("fills the selection, permission, warning, scope, and stopping guidance decided by the ADR", () => { - const entries = [ - ...guidanceEntries(repertoire.guidance), - ...runbookEntries(repertoire.runbooks), - ]; - const namesAt = (path: string) => - entries - .filter((entry) => entry.path === path) - .map((entry) => entry.item.name); - - expect(namesAt("licenses")).toEqual( - expect.arrayContaining([ - "Press without trapping", - "Decline a sweep", - "Propose structure for correction", - ]), - ); - expect(namesAt("smells")).toEqual( - expect.arrayContaining([ - "Schema-shaped questioning", - "Correction recorded twice", - ]), - ); - expect(namesAt("rabbit_holes")).toEqual( - expect.arrayContaining([ - "Clearinghouse as coverage", - "Whole-model restatement as progress", - "Document treated as practice", - ]), - ); - expect(namesAt("construct.kickoff")).toEqual( - expect.arrayContaining([ - "Define the boundary and horizon", - "Name factors and the accuracy bar", - ]), - ); - expect(namesAt("construct.trajectory")).toContain("Select by posture"); - expect(namesAt("construct.close")).toEqual( - expect.arrayContaining([ - "Name the stopping outcome", - "Separate assumptions from simplifications", - ]), - ); - }); - - test("plugin cells add to the repertoire without repeating its sentences", () => { - const repertoireSentences = new Set( - [ - ...guidanceEntries(repertoire.guidance), - ...runbookEntries(repertoire.runbooks), - ].flatMap(({ item }) => sentences(item.text)), - ); - const repeated = ["plugin-sdcpn", "plugin-gherkin"].flatMap( - (packageName) => { - const definition = readPluginDefinition( - readFileSync( - new URL(`../../${packageName}/plugin.yaml`, import.meta.url), - "utf8", - ), - ); - return [ - ...guidanceEntries(definition.guidance), - ...runbookEntries(definition.runbooks), - ].flatMap(({ path, item }) => - sentences(item.text) - .filter((sentence) => repertoireSentences.has(sentence)) - .map( - (sentence) => `${packageName}:${path}:${item.name}:${sentence}`, - ), - ); - }, - ); - - expect(repeated).toEqual([]); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/session-log.test.ts b/libs/@hashintel/brunch-agent/packages/core/test/session-log.test.ts index 235234d77ad..ce99793cb01 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/session-log.test.ts +++ b/libs/@hashintel/brunch-agent/packages/core/test/session-log.test.ts @@ -6,7 +6,7 @@ import { readArchivedEntryRange, resolveEvidenceQuotes, type SessionLogRead, -} from "../src/session-log"; +} from "../src/evidence/session-log"; const read = ( offset: string, diff --git a/libs/@hashintel/brunch-agent/packages/core/test/slot-fixtures.ts b/libs/@hashintel/brunch-agent/packages/core/test/slot-fixtures.ts deleted file mode 100644 index 39c5f4b170a..00000000000 --- a/libs/@hashintel/brunch-agent/packages/core/test/slot-fixtures.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Fixtures for the read path: a small synthetic plugin definition and a - * capture envelope builder. The synthetic definition keeps the tests - * independent of the SDCPN definition's row set; `plugin-definition.test.ts` - * reads the real definitions separately. - */ - -import { - captureDedupKey, - type CaptureEnvelope, - type CaptureIssue, - type CaptureStoreEvent, - type CaptureStoreSnapshot, -} from "../src/capture-store"; -import { - readPluginDefinition, - type PluginDefinition, -} from "../src/plugin-definition"; - -import type { JsonValue } from "../src/json-value"; -import type { SlotAssertion } from "../src/slot-assertion"; - -export const FIXTURE_PLUGIN_YAML = `plugin: - id: fixture - version: fixture/2026-08-25.1 - formalism: fixture - jobs: [construct] - purpose: Interview someone about things and steps. - -ontology: - preamble: Attributes apply to every kind. - kinds: - - { kind: objective, is: A question, projects_to: metrics } - - { kind: thing, is: A thing, projects_to: colours } - - { kind: step, is: A step, projects_to: transitions } - not_kinds: - - { name: queue, text: "A queue is a thing with a count, not a kind." } - attributes: - - name: status - on: every kind - values: [current, planned] - text: Whether the node exists today or is proposed. - -schema: - preamble: A slot is satisfied only by what the expert said. - anchor: { kind: objective, depends_on: the nodes it depends on } - floor: - - { kind: objective, at_least: 1 } - - { kind: thing, at_least: 2 } - - { kind: step, at_least: 1 } - must_know: - - { kind: objective, slot: the question, precision: spelled out, not_applicable: false, why: anchor } - - { kind: objective, slot: the nodes it depends on, precision: at least 1, not_applicable: false, why: slice } - - { kind: thing, slot: distinctions, precision: spelled out, not_applicable: false, why: types } - - { kind: thing, slot: how many, precision: range, not_applicable: true, why: population } - - { kind: step, slot: how long it takes, precision: spread, not_applicable: false, why: duration } - - { kind: step, slot: who performs it, precision: named, not_applicable: true, why: binding } - proposals: - - { type: slot-asserted, payload: slot-assertion } - -patterns: - preamble: Patterns fire on nodes. - items: - - { id: P01, on: [step], slot: how long it takes, when: a step is an event, ask: ask how often } - - { id: P02, on: [thing], when: more than one thing competes, ask: ask which wins } - - { id: P03, on: [], when: the expert says they do not know, ask: ask for a source } - -guidance: - lenses: - - { name: fixture lens, text: Notice things. } - techniques: [] - movements: - slice: [] - sweep: - - { name: fixture sweep, text: Sweep the things. } - licenses: [] - motifs: [] - smells: [] - rabbit_holes: [] - failure_modes: - - { name: fixture failure, text: It failed., signature: it says so } - -runbooks: - construct: - kickoff: - - { name: fixture kickoff, text: Ask the question first. } - trajectory: [] - close: [] - -machinery: - checks: [slot-assertion] - tools: [] -`; - -export const fixturePluginDefinition = (): PluginDefinition => - readPluginDefinition(FIXTURE_PLUGIN_YAML); - -export interface CaptureOptions { - readonly status?: "explicit" | "inferred" | "tentative"; - readonly excerpt?: string; - readonly supersedes?: string; - readonly entry?: number; -} - -/** A user-evidenced capture envelope whose content is one slot assertion. */ -export const assertionCapture = ( - id: string, - assertion: SlotAssertion, - options: CaptureOptions = {}, -): CaptureEnvelope => { - const entry = options.entry ?? 1; - const fields = { - confidence: "firm", - content: { value: assertion as unknown as JsonValue }, - evidence: [ - { - excerpt: options.excerpt ?? `quote for ${id}`, - pointer: { sessionId: "session-1", entryStart: entry, entryEnd: entry }, - source: "user" as const, - }, - ], - epistemicStatus: options.status ?? ("explicit" as const), - ...(options.supersedes === undefined - ? {} - : { supersedes: options.supersedes }), - }; - return { ...fields, id, dedupKey: captureDedupKey(fields) }; -}; - -export const value = ( - kind: string, - node: string, - slot: string, - precision: SlotAssertion["precision"], - content: JsonValue, - extra: Partial<Pick<SlotAssertion, "sourceRegime" | "rationale">> = {}, -): SlotAssertion => ({ - type: "slot-asserted", - kind, - node, - slot, - precision, - ...extra, - assertion: { value: content }, -}); - -export const absence = ( - kind: string, - node: string, - slot: string, - state: Extract<SlotAssertion["assertion"], { absence: unknown }>["absence"], - pointer?: string, -): SlotAssertion => ({ - type: "slot-asserted", - kind, - node, - slot, - assertion: { absence: state, ...(pointer === undefined ? {} : { pointer }) }, -}); - -export const snapshotOf = ( - captures: readonly CaptureEnvelope[], - issues: readonly CaptureIssue[] = [], - events: readonly CaptureStoreEvent[] = [], -): CaptureStoreSnapshot => ({ captures, issues, events }); - -/** - * A model that satisfies every fixture row for one objective, two things, and - * one step. Tests perturb one capture at a time from here. - */ -export const completeCaptures = (): CaptureEnvelope[] => [ - assertionCapture( - "c-objective-question", - value("objective", "throughput", "the question", "spelled out", { - question: "How many widgets per day?", - }), - ), - assertionCapture( - "c-objective-deps", - value("objective", "throughput", "the nodes it depends on", "named", [ - "thing:widget", - "thing:press", - "step:stamp", - ]), - ), - assertionCapture( - "c-widget-distinctions", - value("thing", "widget", "distinctions", "spelled out", ["small", "large"]), - ), - assertionCapture( - "c-widget-count", - absence("thing", "widget", "how many", "not-applicable"), - ), - assertionCapture( - "c-press-distinctions", - value("thing", "press", "distinctions", "spelled out", ["one press type"]), - ), - assertionCapture( - "c-press-count", - value("thing", "press", "how many", "range", { low: 2, high: 3 }), - ), - assertionCapture( - "c-stamp-duration", - value("step", "stamp", "how long it takes", "spread", { - typical: 3, - worse1in10: 5, - better1in10: 2, - unit: "minutes", - }), - ), - assertionCapture( - "c-stamp-actor", - value("step", "stamp", "who performs it", "named", "the press operator"), - ), -]; diff --git a/libs/@hashintel/brunch-agent/packages/core/turbo.json b/libs/@hashintel/brunch-agent/packages/core/turbo.json index d0fbf8a8ac8..728c36c59d5 100644 --- a/libs/@hashintel/brunch-agent/packages/core/turbo.json +++ b/libs/@hashintel/brunch-agent/packages/core/turbo.json @@ -9,12 +9,7 @@ "cache": false }, "test:unit": { - "dependsOn": [ - "codegen", - "^build", - "@hashintel/brunch-agent-plugin-gherkin#build", - "@hashintel/brunch-agent-plugin-sdcpn#build" - ], + "dependsOn": ["codegen", "^build"], "inputs": [ "$TURBO_DEFAULT$", "$TURBO_ROOT$/libs/@hashintel/brunch-agent/**", diff --git a/libs/@hashintel/brunch-agent/packages/core/vite.config.ts b/libs/@hashintel/brunch-agent/packages/core/vite.config.ts index c59aef178ec..291e5b9c4bf 100644 --- a/libs/@hashintel/brunch-agent/packages/core/vite.config.ts +++ b/libs/@hashintel/brunch-agent/packages/core/vite.config.ts @@ -11,18 +11,15 @@ export default defineConfig({ "client-tools": fileURLToPath( new URL("src/client-tools.ts", import.meta.url), ), + flue: fileURLToPath(new URL("src/flue.ts", import.meta.url)), index: fileURLToPath(new URL("src/index.ts", import.meta.url)), - prompts: fileURLToPath(new URL("src/prompts.ts", import.meta.url)), storage: fileURLToPath(new URL("src/storage.ts", import.meta.url)), - "testing/index": fileURLToPath( - new URL("src/testing/index.ts", import.meta.url), - ), }, fileName: (_format, entryName) => `${entryName}.js`, formats: ["es"], }, rolldownOptions: { - external: [/^node:/u, "valibot"], + external: [/^node:/u, /^@flue\/runtime(?:\/.*)?$/u, "valibot"], }, sourcemap: true, }, diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/.oxlintrc.json b/libs/@hashintel/brunch-agent/packages/plugin-dafny/.oxlintrc.json new file mode 100644 index 00000000000..f2a35d7a466 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/.oxlintrc.json @@ -0,0 +1,60 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "extends": ["../../../../../.config/oxlint/brunch/base.json"], + "categories": { + "correctness": "error", + "perf": "warn" + }, + "env": { + "builtin": true, + "es2026": true, + "node": true + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "rules": { + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "@hashintel/brunch-agent/storage", + "message": "Plugins receive harness capabilities and must remain storage-blind." + }, + { + "name": "@hashintel/petrinaut", + "message": "Brunch libraries must not depend on Petrinaut implementations." + } + ], + "patterns": [ + { + "group": ["@local/*"], + "message": "Brunch libraries must remain independent of unpublished HASH packages." + }, + { + "group": ["@hashintel/petrinaut/*", "@hashintel/petrinaut-*"], + "message": "Brunch libraries must not depend on Petrinaut implementations." + }, + { + "group": ["@earendil-works/*"], + "message": "Flue is the plugin's production runtime; lower-level Pi packages remain outside the plugin." + }, + { + "group": ["@hashintel/brunch-agent-*"], + "message": "A plugin may depend inward on the harness, not on Brunch extensions." + } + ] + } + ] + }, + "ignorePatterns": [ + "dist/**", + "build/**", + "coverage/**", + "*.gen.*", + "*.tsbuildinfo", + ".turbo/**" + ] +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/LICENSE.md b/libs/@hashintel/brunch-agent/packages/plugin-dafny/LICENSE.md new file mode 100644 index 00000000000..c7d627721e2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/LICENSE.md @@ -0,0 +1,607 @@ +GNU Affero General Public License +================================= + +_Version 3, 19 November 2007_ +_Copyright © 2007 Free Software Foundation, Inc. <<http://fsf.org/>>_ + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +## Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: **(1)** assert copyright on the software, and **(2)** offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions + +“This License” refers to version 3 of the GNU Affero General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a “modified version” of the +earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based +on the Program. + +To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” +to the extent that it includes a convenient and prominently visible +feature that **(1)** displays an appropriate copyright notice, and **(2)** +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code + +The “source code” for a work means the preferred form of the work +for making modifications to it. “Object code” means any non-source +form of a work. + +A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other +than the work as a whole, that **(a)** is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and **(b)** serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +“Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +### 2. Basic Permissions + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +### 4. Conveying Verbatim Copies + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +* **a)** The work must carry prominent notices stating that you modified +it, and giving a relevant date. +* **b)** The work must carry prominent notices stating that it is +released under this License and any conditions added under section 7. +This requirement modifies the requirement in section 4 to +“keep intact all notices”. +* **c)** You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it. +* **d)** If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +“aggregate” if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +* **a)** Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange. +* **b)** Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either **(1)** a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or **(2)** access to copy the +Corresponding Source from a network server at no charge. +* **c)** Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b. +* **d)** Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements. +* **e)** Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A “User Product” is either **(1)** a “consumer product”, which means any +tangible personal property which is normally used for personal, family, +or household purposes, or **(2)** anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, “normally used” refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms + +“Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +* **a)** Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or +* **b)** Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or +* **c)** Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or +* **d)** Limiting the use for publicity purposes of names of licensors or +authors of the material; or +* **e)** Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or +* **f)** Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors. + +All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +### 8. Termination + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated **(a)** +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and **(b)** permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents + +A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To “grant” such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either **(1)** cause the Corresponding Source to be so +available, or **(2)** arrange to deprive yourself of the benefit of the +patent license for this particular work, or **(3)** arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. “Knowingly relying” means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is “discriminatory” if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license **(a)** in connection with copies of the covered work +conveyed by you (or copies made from those copies), or **(b)** primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +### 13. Remote Network Interaction; Use with the GNU General Public License + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +### 14. Revised Versions of this License + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License “or any later version” applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 16. Limitation of Liability + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16 + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/docs/task-dependencies.json b/libs/@hashintel/brunch-agent/packages/plugin-dafny/docs/task-dependencies.json new file mode 100644 index 00000000000..75317b7db02 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/docs/task-dependencies.json @@ -0,0 +1,25 @@ +{ + "package": "@hashintel/brunch-agent-plugin-dafny", + "dependencies": [ + "@hashintel/brunch-agent" + ], + "tasks": { + "build": [ + "@hashintel/brunch-agent#build" + ], + "fix:eslint": [ + "@hashintel/brunch-agent#build", + "@local/eslint#build" + ], + "lint:eslint": [ + "@hashintel/brunch-agent#build", + "@local/eslint#build" + ], + "lint:tsc": [ + "@hashintel/brunch-agent#build" + ], + "test:unit": [ + "@hashintel/brunch-agent#build" + ] + } +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/package.json b/libs/@hashintel/brunch-agent/packages/plugin-dafny/package.json new file mode 100644 index 00000000000..c504a8d05c4 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/package.json @@ -0,0 +1,37 @@ +{ + "name": "@hashintel/brunch-agent-plugin-dafny", + "version": "0.0.0-private", + "private": true, + "description": "The software-correctness domain typology and Dafny target formalism: a stubbed Flue-native contribution bundle that pressure-tests the core/plugin boundary.", + "license": "AGPL-3.0", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./dist/index.js" + }, + "./flue": { + "types": "./src/flue.ts", + "import": "./dist/flue.js" + } + }, + "scripts": { + "build": "vite build", + "fix:eslint": "oxlint --fix --type-aware --type-check --report-unused-disable-directives-severity=error .", + "lint:eslint": "oxlint --type-aware --type-check --report-unused-disable-directives-severity=error .", + "lint:tsc": "tsgo --noEmit", + "test:unit": "vitest run" + }, + "dependencies": { + "@flue/runtime": "2.0.3", + "@hashintel/brunch-agent": "workspace:*" + }, + "devDependencies": { + "@types/node": "22.18.13", + "@typescript/native-preview": "7.0.0-dev.20260511.1", + "oxlint": "1.63.0", + "oxlint-tsgolint": "0.22.1", + "vite": "8.1.0", + "vitest": "4.1.10" + } +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/flue.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/flue.ts new file mode 100644 index 00000000000..cf9036a68ca --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/flue.ts @@ -0,0 +1,20 @@ +import { useInstruction, useSkill } from "@flue/runtime"; + +import dafnyAppend from "./prompts/APPEND_SYSTEM.md?raw"; +import { + DAFNY_VERIFICATION_SKILL_NAME, + dafnyVerificationSkill, +} from "./skills/dafny-verification/skill"; + +/** + * Mount the stub prompt material and skill owned by the Dafny plugin. + * + * Not composed by any application. Tools are added only when a real Dafny + * capability exists. + */ +export function useDafnyPlugin(): void { + useInstruction(dafnyAppend.trim()); + useSkill(dafnyVerificationSkill); +} + +export { DAFNY_VERIFICATION_SKILL_NAME, dafnyVerificationSkill }; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/index.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/index.ts new file mode 100644 index 00000000000..d97375d9b2e --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/index.ts @@ -0,0 +1,14 @@ +/** + * `@hashintel/brunch-agent-plugin-dafny` — the software-correctness domain + * typology paired with the Dafny target formalism. + * + * This is a stubbed contribution bundle. It holds the proposed homes for the + * third pairing so the two-level core/plugin architecture is pressure-tested + * against a formalism whose transformation boundary (formalization and proof) + * differs from both SDCPN and Gherkin. No definition, proposal type, tool, or + * verifier integration is earned yet; the `./flue` subpath mounts only the + * stub prompt and skill. + */ + +export const DAFNY_DOMAIN_TYPOLOGY = "software correctness obligations"; +export const DAFNY_TARGET_FORMALISM = "dafny"; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/prompts/APPEND_SYSTEM.md b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/prompts/APPEND_SYSTEM.md new file mode 100644 index 00000000000..31f6a974ef5 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/prompts/APPEND_SYSTEM.md @@ -0,0 +1,7 @@ +# Software Correctness Specification in Dafny + +> Stub. This append is a placeholder home, not authored guidance. It is not mounted by any application. + +Specialize the universal elicitation role to software correctness obligations represented as Dafny specification modules and program contracts. Keep the person's stated guarantee, the agent's formalization choices, and verifier evidence distinct: a verifier result establishes only the named obligation over the exact declarations it examined under the stated assumptions. + +Activate the `dafny-verification` skill before substantive interviewing, workpiece revision, specification authoring, or proof work. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/raw-imports.d.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/raw-imports.d.ts new file mode 100644 index 00000000000..005ef3c2c3b --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/raw-imports.d.ts @@ -0,0 +1,5 @@ +/** Vite's `?raw` imports ship authored resources inside the bundle. */ +declare module "*.md?raw" { + const markdown: string; + export default markdown; +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/SKILL.md b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/SKILL.md new file mode 100644 index 00000000000..b783bf4164d --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/SKILL.md @@ -0,0 +1,23 @@ +--- +name: dafny-verification +description: Stub. Elicit software correctness obligations, maintain a recoverable correctness workpiece, and author or review Dafny specifications with an honest account of what was stated, assumed, discharged, skipped, or trusted. Use for a correctness interview or a Dafny specification or proof review. +--- + +# Stub: capability-aware verification lifecycle + +This skill is a placeholder home. It records the proposed disclosure shape from the accepted Ampcode pressure test and authors no procedure yet. + +Proposed shape, not yet earned: + +```text +dafny-verification +├─ elicitation and workpiece maintenance +│ ├─ activate `elicitation` +│ ├─ references/software-correctness-elicitation.md +│ └─ templates/workpiece.md when recording or revising +└─ formalization and evidence + ├─ references/dafny-specification.md + └─ references/proof-checks.md +``` + +Whether specification and verification are one job skill or two (`dafny-specification`, `dafny-verification`) is an open cardinality question that this stub does not settle. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/skill.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/skill.ts new file mode 100644 index 00000000000..adb859075d0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/src/skills/dafny-verification/skill.ts @@ -0,0 +1,8 @@ +import { skillFromMarkdown } from "@hashintel/brunch-agent/flue"; + +import skillMarkdown from "./SKILL.md?raw"; + +export const DAFNY_VERIFICATION_SKILL_NAME = "dafny-verification"; + +/** Stub job skill: a placeholder home with no supporting resources yet. */ +export const dafnyVerificationSkill = skillFromMarkdown(skillMarkdown); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/test/dafny-verification-skill.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/test/dafny-verification-skill.test.ts new file mode 100644 index 00000000000..2a899453079 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/test/dafny-verification-skill.test.ts @@ -0,0 +1,10 @@ +import { expect, test } from "vitest"; + +import { dafnyVerificationSkill } from "../src/skills/dafny-verification/skill"; + +test("the stub skill is a valid Flue skill whose name matches its directory", () => { + expect(dafnyVerificationSkill.name).toBe("dafny-verification"); + expect(dafnyVerificationSkill.description).toMatch(/^Stub\./u); + expect(dafnyVerificationSkill.instructions).toContain("# Stub"); + expect(dafnyVerificationSkill.files).toBeUndefined(); +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/tsconfig.json b/libs/@hashintel/brunch-agent/packages/plugin-dafny/tsconfig.json new file mode 100644 index 00000000000..844edbd8e66 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2024", + "lib": ["ESNext"], + "types": ["node"], + "module": "preserve", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "noEmit": true, + "skipLibCheck": true, + "isolatedModules": true + }, + "include": ["src", "test"] +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/turbo.json b/libs/@hashintel/brunch-agent/packages/plugin-dafny/turbo.json new file mode 100644 index 00000000000..52a7d7aba61 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + } + } +} diff --git a/libs/@hashintel/brunch-agent/packages/plugin-dafny/vite.config.ts b/libs/@hashintel/brunch-agent/packages/plugin-dafny/vite.config.ts new file mode 100644 index 00000000000..7fb7eab74d2 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-dafny/vite.config.ts @@ -0,0 +1,29 @@ +import { fileURLToPath } from "node:url"; + +import { defineConfig } from "vitest/config"; + +const packageRoot = fileURLToPath(new URL(".", import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: { + flue: fileURLToPath(new URL("src/flue.ts", import.meta.url)), + index: fileURLToPath(new URL("src/index.ts", import.meta.url)), + }, + fileName: (_format, entryName) => `${entryName}.js`, + formats: ["es"], + }, + rolldownOptions: { + external: [ + /^@flue\/runtime(?:\/.*)?$/u, + /^@hashintel\/brunch-agent(?:\/.*)?$/u, + ], + }, + sourcemap: true, + }, + root: packageRoot, + test: { + include: ["test/**/*.test.ts"], + }, +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/.oxlintrc.json b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/.oxlintrc.json index 0b4ce17b4f7..f2a35d7a466 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/.oxlintrc.json +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/.oxlintrc.json @@ -23,10 +23,6 @@ "name": "@hashintel/brunch-agent/storage", "message": "Plugins receive harness capabilities and must remain storage-blind." }, - { - "name": "@hashintel/brunch-agent/prompts", - "message": "Plugins fill cells; they must not import harness default teaching (ADR-0008)." - }, { "name": "@hashintel/petrinaut", "message": "Brunch libraries must not depend on Petrinaut implementations." @@ -42,8 +38,8 @@ "message": "Brunch libraries must not depend on Petrinaut implementations." }, { - "group": ["@flue/*", "@earendil-works/*"], - "message": "Brunch plugins must remain substrate-independent." + "group": ["@earendil-works/*"], + "message": "Flue is the plugin's production runtime; lower-level Pi packages remain outside the plugin." }, { "group": ["@hashintel/brunch-agent-*"], diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/package.json b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/package.json index 5fcb5411090..2e6a17a0eeb 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/package.json +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/package.json @@ -2,13 +2,17 @@ "name": "@hashintel/brunch-agent-plugin-gherkin", "version": "0.0.0-private", "private": true, - "description": "The gherkin target: the tracer plugin, wired end-to-end first as the cheap mechanism proof.", + "description": "The software-behavior domain typology and Gherkin target formalism: Flue-native prompt and skill contribution bundle.", "license": "AGPL-3.0", "type": "module", "exports": { ".": { "types": "./src/index.ts", "import": "./dist/index.js" + }, + "./flue": { + "types": "./src/flue.ts", + "import": "./dist/flue.js" } }, "scripts": { @@ -19,8 +23,8 @@ "test:unit": "vitest run" }, "dependencies": { - "@hashintel/brunch-agent": "workspace:*", - "valibot": "1.4.2" + "@flue/runtime": "2.0.3", + "@hashintel/brunch-agent": "workspace:*" }, "devDependencies": { "@types/node": "22.18.13", diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/plugin.yaml b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/plugin.yaml deleted file mode 100644 index 1cdabc2ac61..00000000000 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/plugin.yaml +++ /dev/null @@ -1,216 +0,0 @@ -# The gherkin plugin definition (ADR-0006, ADR-0007). -# -# Keys are owned by the harness; see `packages/core/schema/plugin.schema.json` -# and `docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md`. -# This is the tracer plugin: it exists to prove that a second formalism, with a -# different anchor and a different model shape, fits the same keys without -# the keys bending toward process modelling. Cells are filled only where the -# formalism has something to say; a blank cell means the harness default is -# the whole of the guidance. - -plugin: - id: gherkin - version: gherkin/2026-08-26.2 - formalism: Gherkin — executable specifications as features, rules, and examples - jobs: [construct, review-and-revise] - purpose: | - Interview someone who knows how a piece of behaviour should work, and leave - with a feature specification they would sign: what the feature is for, the - rules it obeys, and for every rule at least one concrete example — a - context, an action, and an observable outcome — written in words the - person who gave them would recognise. - -ontology: - preamble: | - The specification is a tree: a feature has rules, a rule has examples, an - example has steps. Structure comes from the person's account of what the - behaviour does, never from a template. The steps are the only place the - words are constrained — each step phrase should bind to a step the team - already knows, or be flagged as new. - kinds: - - kind: feature - is: One capability, described by whom it is for, what it lets them do, and why that matters. - projects_to: a Feature with its narrative - - kind: rule - is: One business rule the feature obeys, stated generally, that examples then illustrate. - projects_to: a Rule block - - kind: example - is: One concrete instance of a rule — a context, an action, and the outcome that follows. - projects_to: a Scenario or Example under its Rule - - kind: step - is: One line of an example — a Given, When, or Then — in the team's step vocabulary. - projects_to: a step line bound to a step definition - not_kinds: - - name: background - text: A shared context is not a node; it is a context step that several examples repeat. Record it on each example and let the projection factor it. - - name: tag - text: Tags are how a team organises and selects features; they carry no behaviour and are not elicited. - - name: step definition - text: The code that binds a step phrase is the team's, not the interview's. The interview only needs to know whether a phrase is already known. - attributes: - - name: status - on: rule and example - values: [current, proposed] - text: Whether the behaviour exists today or is what the person wants to be true. - -schema: - preamble: | - An example is usable only when someone who has never seen the system could - read its three parts and tell whether the system passed. "The user logs in - and it works" is a story, not an example. - anchor: - kind: feature - depends_on: the rules and examples it covers - floor: - - { kind: feature, at_least: 1 } - - { kind: example, at_least: 1 } - must_know: - - kind: feature - slot: the narrative - precision: spelled out - not_applicable: false - why: Who the capability is for, what it lets them do, and why — everything below is relative to it. - - kind: feature - slot: the rules and examples it covers - precision: at least 1 - not_applicable: false - why: The anchor's slice — what completion is measured over. - - kind: rule - slot: the statement - precision: spelled out - not_applicable: false - why: A rule that cannot be stated generally cannot be checked by an example. - - kind: rule - slot: the examples that illustrate it - precision: at least 1 - not_applicable: false - why: A rule without an example is an opinion; an example is what gets checked. - - kind: example - slot: the context it starts from - precision: spelled out - not_applicable: false - why: The Given — without it the outcome cannot be reproduced. - - kind: example - slot: the action taken - precision: spelled out - not_applicable: false - why: The When — one action, so the outcome has one cause. - - kind: example - slot: the observable outcome - precision: spelled out - not_applicable: false - why: The Then — something a reader could see or measure, not an intention. - - kind: example - slot: the rule it illustrates - precision: named - not_applicable: true - why: Ties the example to the rule it checks; an example may hang directly off the feature. - - kind: step - slot: the phrase - precision: spelled out - not_applicable: false - why: The line as it will be written. - - kind: step - slot: the known step it binds to - precision: named - not_applicable: true - why: Ask the person to name the known team step; without an available lexicon, record the phrase as new rather than inventing a binding. - proposals: - - { type: statement-noted, payload: statement } - -patterns: - preamble: | - Patterns fire on nodes the harness holds. Each names the situation and the - question that resolves it; it does not schedule the question. - items: - - id: P01 - on: [rule] - slot: the examples that illustrate it - when: a rule has a statement but no example. - ask: Ask for the last time this rule mattered — what was the situation, what happened, what was seen. - - id: P02 - on: [example] - when: two examples share a context and an action but differ in outcome. - ask: Ask what distinguishes the two situations; the difference is a missing part of the context or a missing rule. - - id: P03 - on: [example] - slot: the observable outcome - when: the observable-outcome slot is unsatisfied; intentions such as "should work" and "is handled" are common below-grade forms. - ask: Ask what someone watching would see, and where. - - id: P04 - on: [step] - when: a step phrase resembles a known step but is not identical. - ask: Ask whether this is the same thing as the known step, or a different one; record which. - -guidance: - lenses: - - name: Rules hide in "always" and "never" - text: When the person says "we always", "it never", "whenever", or "unless", a rule is being stated in passing. Capture it as a rule and return to it for an example. - - name: Examples hide in stories - text: '"Last week", "for instance", "one customer" — a concrete case is being offered. It is worth more than a general statement; keep the details.' - techniques: - - name: Concretise - text: Turn a general statement into one example with specific values — a named context, one action, one outcome. Ask for the values; do not supply them. - - name: Contrast - text: For every rule, ask for the case where it does not hold. The contrasting example is what separates the rule from a coincidence. - movements: - slice: - - name: One example end to end - text: Take one concrete case through context, action, and outcome before touching another. The structure of the feature — its rules — comes from what the cases have in common. - sweep: - - name: Every rule has an example - text: Walk the rules and ask, for each without an example, for one. - - name: Every outcome is observable - text: Walk the examples and check each outcome names something a reader could see. - licenses: [] - motifs: - - name: Happy path and unhappy path - text: A rule usually has one example where it is satisfied and one where it is violated; ask whether the pair exists. - - name: Boundary - text: A rule with a threshold has an example at the threshold, just under, and just over; ask which of the three the person cares about. - - name: State-dependent outcome - text: The same action with a different outcome in a different state — the state belongs in the context. - smells: - - name: Two actions in one example - text: An example with two Whens has two causes for its outcome; split it. - - name: Outcome restates the action - text: '"When I save, then it is saved" checks nothing; ask what changes that someone could see.' - - name: Steps in gestures - text: Steps written as clicks and fields describe an interface, not behaviour; ask what the person is trying to do. - rabbit_holes: - - name: Writing the automation - text: Step definitions, fixtures, and test data are the team's work after the interview; do not design them here. - - name: Organising the suite - text: Tags, file layout, and naming conventions are not behaviour. - failure_modes: - - name: Untethered example - text: An example illustrates no stated rule and no one knows what it proves. - signature: An example whose rule slot is neither named nor marked not applicable. - - name: Contradiction - text: Two examples with the same context and action and different outcomes are both recorded. - signature: P02 fires and the session closes without a distinguishing context or a new rule. - -runbooks: - construct: - kickoff: - - name: Narrative first - text: Establish who the feature is for and what it lets them do before any rule; every rule is relative to it. - trajectory: - - name: Rule, then example, then contrast - text: For each rule as it surfaces, get one example, then the contrasting one; sweep for examples only after the slice has produced the rules. - close: - - name: Read the examples back - text: Read each example as it will be written and ask whether the person would sign it; list rules still without an example. - review-and-revise: - kickoff: - - name: Which rule changed - text: Establish whether the change is to a rule's statement, to an example, or to the feature's narrative; the affected slice follows from that. - trajectory: - - name: Re-check contradiction - text: After a change to an example, check P02 across the rule it illustrates before anything else. - close: [] - -machinery: - checks: - [parse-validity, step-lexicon-binding, rule-has-example, contradiction] - tools: [] diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/flue.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/flue.ts new file mode 100644 index 00000000000..20d5ea07a09 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/flue.ts @@ -0,0 +1,22 @@ +import { useInstruction, useSkill } from "@flue/runtime"; + +import gherkinAppend from "./prompts/APPEND_SYSTEM.md?raw"; +import { + GHERKIN_SPECIFICATION_SKILL_NAME, + gherkinSpecificationSkill, +} from "./skills/gherkin-specification/skill"; + +/** + * Mount the prompt material and skill owned by the Gherkin plugin. + * + * This contribution bundle is authored and packaged but not yet composed by + * any application. It exists as the second pairing that pressure-tests the + * core/plugin boundary; no parser, step-binding, or execution tool is earned + * yet, so the bundle mounts no tools. + */ +export function useGherkinPlugin(): void { + useInstruction(gherkinAppend.trim()); + useSkill(gherkinSpecificationSkill); +} + +export { GHERKIN_SPECIFICATION_SKILL_NAME, gherkinSpecificationSkill }; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts index 42b231ac254..6efd9357590 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/index.ts @@ -1,68 +1,13 @@ /** - * `@hashintel/brunch-agent-plugin-gherkin` — the gherkin target formalism (spec §13.1). + * `@hashintel/brunch-agent-plugin-gherkin` — the software-behavior domain + * typology paired with the Gherkin target formalism. * - * The tracer target: cheap enough to wire end-to-end first, and deliberately - * different in shape from the process-model plugin, so that the plugin - * contract is co-authored against two formalisms and freezes toward neither - * (spec §13's two-targets-on-each-axis rule; ADR-0007 decision 9). Its - * definition is `plugin.yaml` — a feature-anchored tree of rules, examples, - * and steps under the same harness-owned keys as every plugin. Its proposal - * stays at the verbatim floor in this cycle; `project` and `validate` land - * with their own slice. - * - * **This package resolves `@hashintel/brunch-agent` and nothing else** — never the binding, - * never Flue. Target policy has no business knowing which substrate it is - * running on, and it is storage-blind besides (spec §9.6). + * The plugin is a contribution bundle: `prompts/`, `skills/gherkin-specification/`, + * and `flue.ts`. No parser, step-binding, or execution tool is earned yet. The + * retired YAML definition and verbatim-floor proposal type were removed on + * 2026-09-02. This root export carries the pairing's identity only; the + * `./flue` subpath owns the contribution bundle. */ -import * as v from "valibot"; - -import { definePlugin, readPluginDefinition } from "@hashintel/brunch-agent"; - -import pluginYaml from "../plugin.yaml?raw"; - -const nonEmptyString = v.pipe(v.string(), v.nonEmpty()); -const evidenceQuote = v.strictObject({ excerpt: nonEmptyString }); - -export const StatementNotedProposal = v.pipe( - v.strictObject({ - evidence: v.pipe(v.array(evidenceQuote), v.minLength(1)), - epistemicStatus: v.literal("explicit"), - confidence: v.picklist(["firm", "hedged", "speculative"]), - content: v.strictObject({ - value: v.strictObject({ - type: v.literal("statement-noted"), - interior: v.strictObject({ verbatim: nonEmptyString }), - }), - }), - }), - v.check( - (proposal) => - proposal.evidence.some( - (evidence) => - evidence.excerpt === proposal.content.value.interior.verbatim, - ), - "The verbatim interior must equal one cited user quote.", - ), -); - -export type StatementNotedProposalInput = v.InferInput< - typeof StatementNotedProposal ->; - -/** The plugin definition; reading fails loudly at module load if the contract is broken. */ -export const gherkinDefinition = readPluginDefinition(pluginYaml); - -export const gherkin = definePlugin({ - name: "plugin-gherkin", - targetFormalism: "gherkin", - definition: gherkinDefinition, - proposalCatalog: [ - { - name: "statement-noted", - description: - "Record one condition-shaped statement at the verbatim grade floor, with no parsed structure.", - schema: StatementNotedProposal, - }, - ], -}); +export const GHERKIN_DOMAIN_TYPOLOGY = "software behavior"; +export const GHERKIN_TARGET_FORMALISM = "gherkin"; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/prompts/APPEND_SYSTEM.md b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/prompts/APPEND_SYSTEM.md new file mode 100644 index 00000000000..8becfbf2f1c --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/prompts/APPEND_SYSTEM.md @@ -0,0 +1,11 @@ +# Software Behavior Specification in Gherkin + +Specialize the universal elicitation role to software behavior represented as Gherkin feature documents. Help a person make intended or current behavior explicit as purpose-bearing rules and concrete examples, maintain an evidence-faithful behavior workpiece, and author or revise Gherkin that the person would recognize. + +Activate the `gherkin-specification` skill before substantive interviewing, workpiece revision, Gherkin authoring, or review. + +During elicitation, speak about the software in the person's vocabulary—situations, events, actions, rules, and observable outcomes—rather than requiring `Feature`, `Rule`, `Background`, `Scenario`, `Given`, `When`, or `Then` phrasing. Keep current behavior distinct from proposed behavior. Target syntax may organize a draft; it must not supply behavior the person did not establish. + +The behavior workpiece is the recoverable source for authoring whenever authorship, conflict, uncertainty, or open matters remain. Do not treat a polished `.feature` document as evidence that those matters are resolved. + +Do not claim a document is parse-valid without parser evidence. Do not claim its steps bind to existing step definitions without an available step lexicon or codebase check, and do not call it executable merely because it parses. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/raw-imports.d.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/raw-imports.d.ts index d6729bb002e..005ef3c2c3b 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/raw-imports.d.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/raw-imports.d.ts @@ -1,5 +1,5 @@ -/** Vite's `?raw` import: the plugin definition ships inside the bundle as a string. */ -declare module "*.yaml?raw" { - const yaml: string; - export default yaml; +/** Vite's `?raw` imports ship authored resources inside the bundle. */ +declare module "*.md?raw" { + const markdown: string; + export default markdown; } diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/SKILL.md b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/SKILL.md new file mode 100644 index 00000000000..2b3105cd466 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/SKILL.md @@ -0,0 +1,52 @@ +--- +name: gherkin-specification +description: Elicit or revise software behavior, maintain a recoverable behavior workpiece, and author or review honest Gherkin feature documents. Use for a behavior-specification interview, Gherkin document, executable-specification draft, or review of any of them. +--- + +# Capability-aware specification lifecycle + +Use one conceptual lifecycle: orient, elicit or revise behavior, maintain the workpiece, author or revise Gherkin when useful, check, and deliver. Authoring is a thin projection and correction surface, not a separate modelling world. The current conversation may expose only part of the lifecycle; do not claim an unavailable check occurred. + +## Select the runtime branch + +### Interactive elicitation or revision + +Activate the `elicitation` skill and read `references/gherkin-elicitation.md` before substantive questions or revision. Interview in the person's software and product vocabulary. Read `templates/workpiece.md` when creating or materially revising the behavior account. Read `references/gherkin-authoring-and-checks.md` before drafting, reviewing, or delivering target text. + +An early Gherkin draft may be offered after one coherent rule and example are understood when seeing the wording will help correction. Mark the wording as your rendering; agreement with it does not retroactively make every phrase person-originated evidence. + +### Render or check only + +Use the supplied behavior workpiece or Gherkin document as the complete input. Do not interview. Read `references/gherkin-authoring-and-checks.md`, preserve unaffected material, and perform only the checks the available capabilities support. If a consequential ambiguity prevents faithful authoring or review, report it and the smallest question a later interactive conversation must answer rather than inventing the behavior. + +## Procedure + +### Orient + +Establish enough purpose and context to select one useful behavior thread: who needs the capability, what it enables, whether the account is current or proposed, the relevant software boundary, the intended readers, and whether an existing feature document or step vocabulary is available. Do not administer these concerns as an opening form. + +### Elicit or revise behavior + +For a new account, follow one concrete example through its starting context, one focal event or action, and observable outcome. Use contrasts and boundary cases to expose the rule it illustrates. For an existing account, first locate the disputed rule, example, or feature narrative and the behavior it changes. Use the `elicitation` skill's universal guidance and `references/gherkin-elicitation.md` without turning their registers or the workpiece headings into question order. + +### Maintain the workpiece + +Keep a near-target behavior account in the person's vocabulary. Record feature purpose, rules, examples, domain terms, current-versus-proposed status, authorship, and consequential open matters. A target-shaped draft does not replace these distinctions while they remain load-bearing. + +Whenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before render-only handoff and before workpiece-only delivery. A delta or a `.feature` document without its open matters is not the full recoverable account. + +### Author or revise Gherkin + +Read `references/gherkin-authoring-and-checks.md`. Translate only settled workpiece meaning into target structure. Preserve team language, localization, aliases, tags, and suite conventions when supplied. Do not invent step-definition bindings or implementation detail to make the document look executable. + +For revision, preserve unaffected features, rules, examples, descriptions, comments, tags, and phrasing unless the changed behavior or a named check requires a delta. + +### Check and deliver + +Apply the checks supported by the current capabilities. Deliver the current behavior workpiece when open matters or authorship distinctions remain material. Deliver Gherkin text with a plain account of whether it was only authored, parsed, checked against a supplied step vocabulary, or actually executed elsewhere. Name unillustrated rules, ambiguous behavior, new or unchecked step phrases, assumptions, and omitted cases. + +An explicit stop opens no new topic. Return the best useful workpiece and target draft with consequential gaps visible. + +## Resource discipline + +Read resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or use target grammar as the sequence or vocabulary of ordinary interview questions. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-authoring-and-checks.md b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-authoring-and-checks.md new file mode 100644 index 00000000000..74d081c25fc --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-authoring-and-checks.md @@ -0,0 +1,95 @@ +# Gherkin Authoring and Checks + +Read this when drafting, revising, reviewing, or delivering Gherkin. Consume the current behavior workpiece or supplied feature document; do not reread the transcript as the primary behavior model. + +Authoring translates recorded software behavior into Gherkin structure. It may normalize wording, factor repeated setup, or choose an equivalent keyword alias. It may not invent behavior, step bindings, tags, examples, or suite conventions to make the document look complete. + +The [Cucumber Gherkin reference](https://cucumber.io/docs/gherkin/reference) is the public semantic authority for this draft. An installed parser and its version are the authority for a concrete project's syntax acceptance; this resource routes and interprets checks rather than replacing either source. + +## Authoring boundary + +Before authoring, confirm that the feature purpose is intelligible and each target example has a usable starting context, focal event or action, and observable outcome. If materially different behaviors remain possible because one distinction is missing, formulate the smallest resolving question. Ask it only when interactive elicitation is available; in render-only execution, report it and stop the unsupported rendering path. + +Honor the person's spoken language and supplied team conventions. If the document uses a non-English Gherkin locale, put the appropriate `# language: <code>` header on the first line. Otherwise English is the default. Do not translate the domain language merely to fit an assumed suite convention. + +## Document structure + +- Emit one `Feature:` per `.feature` document. Give it a short name and a description that preserves the capability's purpose and value when useful. +- Use `Rule:` only when it expresses a genuine business rule and groups examples that illustrate that rule. Gherkin permits examples directly under a feature; do not manufacture a rule solely to fill structure. +- Use `Scenario:` or its `Example:` synonym consistently with supplied team convention. Each example should tell one concise behavior story; Cucumber recommends three to five steps, but semantic clarity—not a step-count gate—decides when to split. +- Map supported starting context to `Given`, the focal event or action to `When`, and externally observable results to `Then`. Use `And` and `But` to continue the preceding semantic role. Do not use the keyword to disguise a second unrelated action or outcome. +- Keep implementation detail out of steps unless the interface or protocol is itself the behavior contract. Prefer the actor's goal and externally visible messages, reports, state, or responses over clicks, selectors, functions, and database inspection. +- A step's keyword is not part of Cucumber's definition match. Do not author identical step text under different semantic keywords as though they were distinct definitions. + +## Factoring and data + +### Background + +Use `Background:` only for context shared by every following example at the same `Feature` or `Rule` level. It runs before each example, after before hooks. Keep it short and vivid; the Cucumber reference recommends no more than four lines before considering higher-level phrasing or another grouping. Do not move behavior essential to understanding an example out of sight merely to remove repetition. + +Only one `Background` is allowed per `Feature` or `Rule`. Different setup families usually indicate separate rules, features, or explicit context in each example. + +### Scenario Outline and Examples + +Use `Scenario Outline:` when the same behavior structure is supported for several explicit value combinations. Every `<placeholder>` must name an `Examples:` table header, and the outline must have at least one data row. Do not turn materially different rules into one table merely because their sentences are similar. + +Parameters may appear in step text, descriptions, Doc Strings, and Data Tables. Preserve cell values exactly enough to discriminate the supported examples. + +### Step arguments + +Use a Data Table when one supported step consumes a list or record-shaped value, not as a substitute for several behavioral examples. Escape newline as `\n`, a literal pipe as `\|`, and a backslash as `\\` inside table cells. + +Use a Doc String for supported multiline text. Prefer `"""` delimiters for broad editor support; a content type may follow the opening delimiter when supplied or useful. Preserve indentation relative to the opening delimiter. + +## Descriptions, tags, and comments + +Free-form descriptions may follow `Feature`, `Rule`, `Background`, `Scenario` or `Example`, and `Scenario Outline`; Markdown is permitted and ignored during execution. Use descriptions for purpose or rationale that helps readers, not for unresolved claims presented as settled behavior. + +Tags are metadata rather than evidence of behavior; a test suite may use them for selection or conditional hooks. Preserve supplied tags or report the need for suite policy; do not invent organizational metadata during elicitation. + +Comments begin with `#` at the start of a new line after optional indentation. Gherkin has no block comments. Do not hide a second epistemic workpiece in comments; keep unsupported behavior and open matters in the companion workpiece. + +## Checks + +### Behavior fidelity + +- Each feature description, rule, example, and step traces to the current workpiece or supplied document; authoring choices remain distinguishable from person-supplied wording where consequential. +- Every `Rule` has at least one example that illustrates it, or the missing example is reported outside the target as a delivery gap. +- A reader can identify the starting state, focal event or action, and observable outcome of each example without guessing hidden implementation. +- Examples with apparently identical context and action do not assert different outcomes unless a named condition, rule, or unresolved conflict distinguishes them. +- Current behavior has not silently replaced proposed behavior or vice versa. + +### Gherkin structure + +- The first primary keyword is `Feature:` and the file contains exactly one feature. +- Keywords that require a colon have one; step keywords do not gain one. The parser, when available, is the authority for exact grammar. +- A `Background` appears before the first example at its level and no level has more than one. +- Every Scenario Outline placeholder is supplied by each applicable `Examples` table, and every table has a header and at least one row. +- Doc String delimiters close, Data Table rows are well formed, comments begin on their own lines, and localization is declared consistently. + +### Step language and execution claims + +- Step phrases use the team's domain language and do not differ accidentally in tense, synonyms, or incidental wording. +- When a step lexicon or codebase index is available, each phrase is classified as an exact known binding, an intentional new phrase, or an unresolved near match. Without such a source, binding remains unchecked. +- Parse validity proves only that a Gherkin parser accepts the document. Binding validity additionally requires matching step definitions. Executability additionally requires the relevant test runtime, hooks, fixtures, and system path. Never substitute one claim for another. + +### Revision + +- The changed behavior and target delta are named before editing. +- Unaffected descriptions, rules, examples, tags, comments, and team phrasing remain unchanged unless a supported factoring or check requires movement. +- Factoring repeated context into a Background or examples into an Outline preserves behavior and does not hide a meaningful distinction. +- New or changed phrases have an explicit binding status rather than silently inheriting an old step's implementation. + +## Delivery + +Deliver each target document as a complete `.feature` text, labeled with its intended filename when known. Also deliver the current behavior workpiece when unresolved authorship, assumptions, conflicts, unillustrated rules, unchecked bindings, or other consequential gaps remain. + +State plainly: + +- what behavior was elicited, revised, authored, or merely reformatted; +- which examples illustrate which rules and what remains unsupported; +- whether each document was authored only, parser-checked, binding-checked against a named source, or executed elsewhere; +- which assumptions, authoring normalizations, new step phrases, omissions, and open matters remain; and +- the smallest consequential question, reference source, or capability needed next. + +Do not replace that account with a closed outcome label or call a parser-valid document an executable specification without the corresponding evidence. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-elicitation.md b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-elicitation.md new file mode 100644 index 00000000000..8a5e50f8ad6 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/references/gherkin-elicitation.md @@ -0,0 +1,153 @@ +# Software-Behavior and Gherkin Elicitation + +This reference adds software-behavior and Gherkin-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance. + +The registers are not a questionnaire or phase sequence. **Recognition** suggests behavior distinctions that may be present. **Operations** select ways to investigate an active gap. **Coverage** says what a signable behavior account may need. **Verification** checks the current interview and workpiece. Gherkin grammar and document checks live in `gherkin-authoring-and-checks.md`. + +## Directives + +### Specify behavior in the person's language + +Ask about situations, actors or external systems, events, actions, rules, and observable outcomes in the vocabulary used by the people who need the behavior. Keep `Given`/`When`/`Then`, file structure, automation code, fixtures, and selectors backstage until target authoring. + +### Keep intended and current behavior distinct + +Normative language may be the desired product, not a defective report of practice. Establish whether the person is describing what happens now, what should happen, or a discrepancy that matters. Do not force a proposed rule through a last-occurrence test as though only observed behavior were legitimate. + +### Let examples illustrate rules + +Use concrete examples to discriminate and correct a general rule. Do not promote one memorable case into a universal rule without checking its boundary, and do not leave a load-bearing rule with no example showing how a reader would decide whether it held. + +### Keep observable behavior separate from implementation + +Describe outcomes visible to a person or external system. Interface gestures, database records, function calls, selectors, and test fixtures are not the behavior unless the stated purpose specifically makes that interface or integration contract observable. + +### Do not invent suite integration + +Step definitions, tags, locale, aliases, and naming conventions may be supplied by a team or available project. Without that source, preserve the intended phrase and mark its binding or convention as new, unavailable, or unchecked. + +## Recognition + +Recognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread. + +### Rule-shaped language + +“Always,” “never,” “only,” “unless,” “whenever,” “must,” “may,” and “cannot” may state a business rule, permission, invariant, or exception. Determine its scope and find a concrete example that would distinguish it from a slogan. + +### Example-shaped stories + +“Last time,” “for example,” “one user,” and narratives with specific values may already contain a context, event, and outcome. Preserve the details before generalizing. + +### Same action, different outcome + +Two accounts with the same apparent context and action but different outcomes may expose a missing state, actor distinction, business rule, or genuine conflict. + +### Expected but unobservable result + +“It works,” “it is handled,” “it succeeds,” and “the record is updated” may name an intention or hidden implementation state rather than an outcome a user or external system can observe. + +### Gesture-shaped description + +Clicks, screens, buttons, fields, API methods, and internal components may be the person's natural route into the example while obscuring the capability or externally meaningful event. Preserve them when they are contractually observable; otherwise ask what the actor is trying to accomplish or what another system sends or receives. + +### Boundary and partition language + +Thresholds, ranges, roles, lifecycle states, permissions, dates, limits, and categories may divide behavior into equivalence classes. Boundaries just below, at, and above a threshold may deserve separate examples when the rule changes there. + +### Repeated context or data shape + +Context repeated across several examples may become a `Background`; examples differing only by named values may become a `Scenario Outline`. These are target-authoring possibilities, not behavior facts and not reasons to manufacture repetition. + +## Operations + +Use the universal Operations as the primary interviewing repertoire. These additions bind them to software-behavior specification. + +### Follow one behavior end to end + +Choose one concrete case and establish the relevant starting context, one focal event or action, and what a person or external system can observe afterward. Keep incidental setup and multiple downstream behaviors out of the example unless they are necessary to understand the rule. + +### State the candidate rule for correction + +After one or more examples expose a stable relationship, offer the general rule in domain language and ask for correction. Mark it as your proposed normalization until the person settles it. + +### Contrast satisfaction and violation + +For a consequential rule, ask for a nearby example where it does not apply, is refused, or yields another outcome. Vary one relevant condition so the contrast reveals the rule rather than creating an unrelated story. + +### Probe a boundary + +When behavior changes at a threshold, ask which cases just below, at, and just above matter. Record only supported values and outcomes; the familiar boundary triad is a prompt for attention, not an automatic requirement. + +### Separate current from proposed with the same example + +When the person is changing behavior, ask what the selected example does now and what it should do. Preserve both statuses without presenting the desired result as observed or the current result as accepted. + +### Ground reusable step language + +When an actual step lexicon or repository is available, compare the intended phrase with known team language. Ask whether a near match expresses the same behavior or a distinct one. Without a lexicon, retain the domain phrase and defer binding; do not ask the person to remember hidden implementation names as a substitute for inspection. + +### Sweep rules and examples + +After a concrete slice exposes the feature's structure, sweep one concern: rules without illustrating examples, examples without observable outcomes, edge classes without coverage, current/proposed ambiguity, or phrases whose binding remains unchecked. Do not traverse target keywords merely because they exist. + +## Coverage + +Coverage identifies what the behavior workpiece may need for its purpose and downstream Gherkin authoring. It is neither question order nor a demand to populate irrelevant categories. + +### Capability, value, boundary, and status + +Preserve who or what benefits, what the capability enables, why it matters, what software or interaction boundary is in scope, and whether each account describes current or proposed behavior. + +### Business rules and rationale + +Preserve each rule generally enough to apply beyond one story, its scope and exceptions, why the distinction matters when useful, and at least one example that illustrates it or a visible gap where none is yet supported. + +### Concrete behavior examples + +Preserve the starting context that selects the behavior, the focal event or action, and the externally observable outcome. Retain specific values, actors, states, channels, and timing only where they discriminate the rule. + +### Contrasts, failures, and boundaries + +Preserve supported unhappy paths, refusals, absent permissions, invalid inputs, failures, state-dependent results, and threshold cases that materially define the rule. Do not demand one example from every familiar test-design category. + +### Actors, external systems, and domain language + +Preserve roles and systems whose differences change behavior, consequential terms in the team's language, and any supplied step vocabulary or naming convention. Do not turn every noun into an independently elicited target element. + +### Shared context and tabular variation + +Preserve repeated preconditions and repeated value dimensions so authoring can decide whether `Background`, `Scenario Outline`, or separate examples communicate them best. The target structure is a later choice; the workpiece keeps the behavior readable before factoring. + +### Target-document conventions and integration inputs + +When supplied, preserve spoken-language locale, preferred keyword aliases, tags, file naming, step lexicon, and the source against which binding or execution could be checked. Suite organization carries no behavior by itself and should not consume interview time without a delivery need. + +## Verification + +Apply these checks while eliciting and maintaining the workpiece. Grammar, authoring, parse, and binding checks live in `gherkin-authoring-and-checks.md`. + +### Purpose, rules, and examples + +- The feature purpose states who or what benefits, what capability is enabled, and why it matters at the depth the intended readers need. +- Each load-bearing rule is stated generally and has a supported concrete example or a visible gap. +- Each example has enough starting context to select the behavior, one focal event or action, and an observable outcome. +- Contrasting examples differ on a named consequential condition rather than accidentally contradicting each other. + +### Behavior and authorship + +- Current and proposed behavior remain distinguishable. +- An outcome names something visible to a person or external system rather than an intention or hidden implementation state. +- Agent-supplied rules, phrasings, values, examples, and partitions retain agent authorship until settled. +- A single story has not silently become a universal rule, and a familiar test pattern has not generated unsupported cases. +- Unknown behavior, open conflicts, unillustrated rules, and unchecked step bindings remain visible rather than disappearing into polished target text. + +### Failure signals and repairs + +- **Syntax-led interview:** questions traverse `Feature`, `Rule`, `Scenario`, or step keywords. Return to one concrete behavior in the person's language. +- **Story without rule:** examples accumulate but no one can say what behavior each discriminates. Propose the smallest candidate rule for correction. +- **Rule without witness:** a general rule has no concrete example. Ask for a supported case or mark the gap. +- **Outcome restates action:** “when it saves, then it is saved” supplies no observable result. Ask what a person or external system notices. +- **Implementation capture:** steps become clicks, selectors, function calls, or database assertions without a purpose that makes them observable. Translate back to behavior or name the interface contract explicitly. +- **Missing selector:** apparently identical context and action yield different outcomes. Preserve both and investigate the state, rule, or conflict that distinguishes them. +- **Desired-as-observed:** proposed behavior is presented as current evidence. Restore status and authorship. +- **Invented binding:** a phrase is described as an existing executable step without a lexicon or code check. Mark it new or unchecked. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/skill.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/skill.ts new file mode 100644 index 00000000000..63d51b57e18 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/skill.ts @@ -0,0 +1,15 @@ +import { skillFromMarkdown } from "@hashintel/brunch-agent/flue"; + +import gherkinAuthoringAndChecks from "./references/gherkin-authoring-and-checks.md?raw"; +import gherkinElicitation from "./references/gherkin-elicitation.md?raw"; +import skillMarkdown from "./SKILL.md?raw"; +import workpieceTemplate from "./templates/workpiece.md?raw"; + +export const GHERKIN_SPECIFICATION_SKILL_NAME = "gherkin-specification"; + +/** The plugin's one job skill: software-behavior elicitation, workpiece, and Gherkin authoring/checks. */ +export const gherkinSpecificationSkill = skillFromMarkdown(skillMarkdown, { + "references/gherkin-authoring-and-checks.md": gherkinAuthoringAndChecks, + "references/gherkin-elicitation.md": gherkinElicitation, + "templates/workpiece.md": workpieceTemplate, +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/templates/workpiece.md b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/templates/workpiece.md new file mode 100644 index 00000000000..820ca46a9be --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/src/skills/gherkin-specification/templates/workpiece.md @@ -0,0 +1,102 @@ +# Behavior-Specification Workpiece and Recording Contract + +The workpiece is the shared, recoverable software-behavior account. Elicitation and revision maintain it; Gherkin authoring consumes it. The transcript remains evidence, and the `.feature` document remains a target rendering rather than the sole home of unresolved meaning. + +Follow the person's thread during conversation and file material into the workpiece afterward. Its headings are recording homes, not question order. The structure is near the target because software-behavior examples map closely to Gherkin, but it does not require target keywords or step decomposition. + +## Recording distinctions + +Use the authorship and uncertainty distinctions from the `elicitation` skill's universal guidance; do not redeclare them as a Gherkin ontology. This workpiece adds only distinctions the software-behavior/Gherkin pairing requires: + +- **Behavior status** — Whether an account describes current or proposed behavior, or a discrepancy between them. +- **Rule and example** — The general behavior and the concrete case illustrating it. A case does not become a rule merely because target text can be written for it. +- **Behavior content and authoring choice** — What the software must do versus how the agent names, groups, phrases, or factors it in Gherkin. +- **Integration status** — Whether target text is only authored, accepted by a parser, matched against a named step-definition source, or executed through a named runtime path. + +## Workpiece template + +```markdown +# Behavior-Specification Workpiece + +## Purpose and scope + +### Feature value narrative + +Who or what benefits, what the capability enables, and why it matters. + +### Current, proposed, or mixed account + +### Intended readers and use + +### Software boundary and deliberate non-goals + +### What the result must not claim + +## Domain language and integration context + +### Actors and external systems + +### Consequential terms and meanings + +### Supplied locale, conventions, tags, or step lexicon + +## Rules and examples + +### Rule: <person's words for the rule> + +#### Working statement, behavior status, and authorship + +#### Why this distinction matters + +#### Example: <memorable behavior name> + +##### Starting context + +##### Focal event or action + +##### Observable outcome + +##### Rule distinction, boundary, or contrast demonstrated + +##### Exact person evidence and authoring choices where needed + +#### Contrasting or boundary example: <name> + +Add only when it exposes a consequential condition the first example does not. + +Repeat rules and examples as needed. An example may remain directly under the feature when no separate business rule is useful; state what it demonstrates. + +## Authoring candidates + +### Context shared across examples + +### Value dimensions that may form an outline + +### Candidate target files and feature grouping + +### New, known, and unchecked step phrases + +## Open matters and authorship + +For each consequential matter, record its universal state—agent proposal or assumption, unknown, not yet asked, declined, deferred, conflict, correction, contextual coexistence, or deliberate omission—plus what it affects and what would resolve or re-enter it. + +Record target-formalism and integration gaps separately from unknown behavior. A supported rule can be clear while its phrase binding or runtime capability remains unavailable. + +## Delivery status + +### What this workpiece currently supports + +### Consequential gaps + +### Gherkin status and check evidence +``` + +## Maintenance + +- Prefer the person's domain terms for rules, examples, actors, and outcomes. +- Keep one authoritative home for each active rule and example. Record corrections without leaving the obsolete and current forms as competing behavior. +- Do not force a person-supplied example into step lines while interviewing. Context, event or action, and outcome are enough for the workpiece; authoring owns target decomposition. +- Keep current and proposed versions side by side only when their contrast is the subject; otherwise state which account is active and preserve the old one as correction history. +- Remove irrelevant empty sections. Record an unresolved state only when it matters to later work. +- If authoring requires transcript archaeology to recover a load-bearing rule or outcome, the workpiece is incomplete at that boundary. +- Record separately whether target text was not authored, authored only, parser-checked, binding-checked against a named source, or executed by an external test path. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/gherkin-specification-skill.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/gherkin-specification-skill.test.ts new file mode 100644 index 00000000000..ad691e20fda --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/gherkin-specification-skill.test.ts @@ -0,0 +1,36 @@ +import { readFileSync } from "node:fs"; + +import { describe, expect, test } from "vitest"; + +import { gherkinSpecificationSkill } from "../src/skills/gherkin-specification/skill"; + +const skillDirectory = new URL( + "../src/skills/gherkin-specification/", + import.meta.url, +); +const readSkillFile = (fileName: string): string => + readFileSync(new URL(fileName, skillDirectory), "utf8"); + +describe("the authored gherkin-specification skill directory", () => { + test("is one Flue skill whose packaged paths equal the authored paths", () => { + expect(gherkinSpecificationSkill.name).toBe("gherkin-specification"); + expect(Object.keys(gherkinSpecificationSkill.files ?? {}).sort()).toEqual([ + "references/gherkin-authoring-and-checks.md", + "references/gherkin-elicitation.md", + "templates/workpiece.md", + ]); + for (const path of Object.keys(gherkinSpecificationSkill.files ?? {})) { + expect(gherkinSpecificationSkill.files?.[path]).toBe(readSkillFile(path)); + } + }); + + test("routes universal judgment to core's elicitation skill and names only packaged resources", () => { + const instructions = gherkinSpecificationSkill.instructions; + expect(instructions).toContain("Activate the `elicitation` skill"); + for (const referenced of instructions.matchAll( + /`((?:references|templates)\/[\w-]+\.md)`/gu, + )) { + expect(gherkinSpecificationSkill.files).toHaveProperty(referenced[1]!); + } + }); +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/statement-noted.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/statement-noted.test.ts deleted file mode 100644 index c1223cca7da..00000000000 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/test/statement-noted.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as v from "valibot"; -import { describe, expect, test } from "vitest"; - -import { createSweepExtractionResultSchema } from "@hashintel/brunch-agent"; - -import { gherkin, gherkinDefinition } from "../src/index"; - -const quote = "Payment is authorized before fulfillment."; -const statementNoted = { - evidence: [{ excerpt: quote }], - epistemicStatus: "explicit" as const, - confidence: "firm" as const, - content: { - value: { - type: "statement-noted" as const, - interior: { verbatim: quote }, - }, - }, -}; - -describe("the Gherkin verbatim-grade proposal floor", () => { - test("leaves rule coverage to the sweep, pattern, and check without a duplicate failure mode", () => { - expect( - gherkinDefinition.guidance.failure_modes.map( - (failureMode) => failureMode.name, - ), - ).not.toContain("Rule without example"); - }); - - test("declares exactly one statement proposal and compiles it into sweep extraction", () => { - expect(gherkin.proposalCatalog.map((proposal) => proposal.name)).toEqual([ - "statement-noted", - ]); - expect( - v.parse(createSweepExtractionResultSchema(gherkin), { - proposals: [statementNoted], - }), - ).toEqual({ proposals: [statementNoted] }); - }); - - test("refuses silent hardening and structure above the verbatim grade", () => { - const schema = createSweepExtractionResultSchema(gherkin); - expect(() => - v.parse(schema, { - proposals: [ - { - ...statementNoted, - content: { - value: { - type: "statement-noted", - interior: { verbatim: "authorization precedes fulfillment" }, - }, - }, - }, - ], - }), - ).toThrow(/verbatim/i); - expect(() => - v.parse(schema, { - proposals: [ - { - ...statementNoted, - content: { - value: { - type: "statement-noted", - interior: { verbatim: quote, parsed: { operator: "before" } }, - }, - }, - }, - ], - }), - ).toThrow(v.ValiError); - expect(() => - v.parse(schema, { - proposals: [ - { - ...statementNoted, - evidence: [ - { - excerpt: quote, - pointer: { sessionId: "invented", entryStart: 1, entryEnd: 1 }, - }, - ], - }, - ], - }), - ).toThrow(v.ValiError); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/vite.config.ts b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/vite.config.ts index 62daeca5b55..7fb7eab74d2 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-gherkin/vite.config.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-gherkin/vite.config.ts @@ -7,12 +7,18 @@ const packageRoot = fileURLToPath(new URL(".", import.meta.url)); export default defineConfig({ build: { lib: { - entry: fileURLToPath(new URL("src/index.ts", import.meta.url)), - fileName: "index", + entry: { + flue: fileURLToPath(new URL("src/flue.ts", import.meta.url)), + index: fileURLToPath(new URL("src/index.ts", import.meta.url)), + }, + fileName: (_format, entryName) => `${entryName}.js`, formats: ["es"], }, rolldownOptions: { - external: [/^@hashintel\/brunch-agent(?:\/.*)?$/u, "valibot"], + external: [ + /^@flue\/runtime(?:\/.*)?$/u, + /^@hashintel\/brunch-agent(?:\/.*)?$/u, + ], }, sourcemap: true, }, diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/.oxlintrc.json b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/.oxlintrc.json index 0b4ce17b4f7..fb927d5c744 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/.oxlintrc.json +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/.oxlintrc.json @@ -23,10 +23,6 @@ "name": "@hashintel/brunch-agent/storage", "message": "Plugins receive harness capabilities and must remain storage-blind." }, - { - "name": "@hashintel/brunch-agent/prompts", - "message": "Plugins fill cells; they must not import harness default teaching (ADR-0008)." - }, { "name": "@hashintel/petrinaut", "message": "Brunch libraries must not depend on Petrinaut implementations." @@ -38,12 +34,12 @@ "message": "Brunch libraries must remain independent of unpublished HASH packages." }, { - "group": ["@hashintel/petrinaut/*", "@hashintel/petrinaut-*"], - "message": "Brunch libraries must not depend on Petrinaut implementations." + "group": ["@hashintel/petrinaut/*"], + "message": "Brunch libraries must not depend on Petrinaut implementations; plugin tools may consume published @hashintel/petrinaut-core contracts." }, { - "group": ["@flue/*", "@earendil-works/*"], - "message": "Brunch plugins must remain substrate-independent." + "group": ["@earendil-works/*"], + "message": "Flue is the plugin's production runtime; lower-level Pi packages remain outside the plugin." }, { "group": ["@hashintel/brunch-agent-*"], diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/docs/task-dependencies.json b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/docs/task-dependencies.json index 8da20f547f3..22c6c8551e5 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/docs/task-dependencies.json +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/docs/task-dependencies.json @@ -1,25 +1,31 @@ { "package": "@hashintel/brunch-agent-plugin-sdcpn", "dependencies": [ - "@hashintel/brunch-agent" + "@hashintel/brunch-agent", + "@hashintel/petrinaut-core" ], "tasks": { "build": [ - "@hashintel/brunch-agent#build" + "@hashintel/brunch-agent#build", + "@hashintel/petrinaut-core#build" ], "fix:eslint": [ "@hashintel/brunch-agent#build", + "@hashintel/petrinaut-core#build", "@local/eslint#build" ], "lint:eslint": [ "@hashintel/brunch-agent#build", + "@hashintel/petrinaut-core#build", "@local/eslint#build" ], "lint:tsc": [ - "@hashintel/brunch-agent#build" + "@hashintel/brunch-agent#build", + "@hashintel/petrinaut-core#build" ], "test:unit": [ - "@hashintel/brunch-agent#build" + "@hashintel/brunch-agent#build", + "@hashintel/petrinaut-core#build" ] } } diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json index 78489fed6e6..f8ff9d303f2 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/package.json @@ -2,13 +2,17 @@ "name": "@hashintel/brunch-agent-plugin-sdcpn", "version": "0.0.0-private", "private": true, - "description": "The SDCPN target formalism: the process-model plugin definition and its slot-assertion proposal type.", + "description": "The operational-process domain typology and SDCPN target formalism: Flue-native prompt, job skill, and Petrinaut construction tools.", "license": "AGPL-3.0", "type": "module", "exports": { ".": { "types": "./src/index.ts", "import": "./dist/index.js" + }, + "./flue": { + "types": "./src/flue.ts", + "import": "./dist/flue.js" } }, "scripts": { @@ -19,7 +23,9 @@ "test:unit": "vitest run" }, "dependencies": { + "@flue/runtime": "2.0.3", "@hashintel/brunch-agent": "workspace:*", + "@hashintel/petrinaut-core": "workspace:*", "valibot": "1.4.2" }, "devDependencies": { diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/plugin.yaml b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/plugin.yaml deleted file mode 100644 index 3e4a25ef885..00000000000 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/plugin.yaml +++ /dev/null @@ -1,544 +0,0 @@ -# The SDCPN plugin — authored under the harness-owned keys of ADR-0007. -# -# Every key below is defined and taught by the harness; this file specialises each for one target -# formalism. Cells add to the harness default and never override it; a cell left blank means the -# default suffices. Nothing here may name a domain: the same file must serve any operational -# system unchanged. A case that seems to need a new row or a new key is a finding about the -# abstraction, recorded in the schema changelog, never content added here. -# -# Migrated from plugin.md (sdcpn/2026-08-25.1) with no semantic change to the three tables. The -# guidance the v0 prompt contributed — open with objectives, slice then sweep, probe, keep the -# ledger, close honestly — has moved to the repertoire (`@hashintel/brunch-agent/prompts`), where -# every plugin inherits it; what remains here is what is true of this formalism and not of -# interviewing. - -plugin: - id: sdcpn - version: sdcpn/2026-08-26.2 - formalism: stochastic dynamic coloured Petri nets (Petrinaut) - jobs: [construct, review-and-revise] - purpose: | - Interview someone who knows an operational system deeply — but is not a modeller — and derive a - process model that a simulation can run. The model must answer the questions the user actually - has, to the depth those questions need, in the expert's own vocabulary, with every value - traceable to something the expert said. Where the expert's knowledge stops, the model says so - instead of guessing. - - The interviewer does not build the net. It elicits the model at the expert's granularity; the - plugin's projection derives the SDCPN scaffold, the code-obligation sidecar, and the loss report - from the model afterwards. Steps become transitions and the states between them become places - *in projection*, never in the conversation. - -# ─── Contract data ──────────────────────────────────────────────────────────────────────────────── - -ontology: - preamble: | - The model is a graph of nodes. Every node has exactly one kind. Kinds are the vocabulary of any - discrete-event process, not of any domain. Kinds 1–6 are net-bearing; 7–10 are partly or wholly - IR-only — the net is one projection of the model, and what the net cannot hold is kept with - provenance and named in the loss report. - kinds: - - kind: entity-type - is: >- - A kind of thing that flows through, is operated on, or does the work — and the distinctions - the process treats differently, including state that rides along. - projects_to: colours, typed elements - - kind: boundary-condition - is: >- - What the system starts with and what reaches it from outside: initial populations, arrivals - and departures, calendars, external inputs and their reliability. - projects_to: scenario initial state and parameters, source transitions - - kind: activity - is: >- - Something that happens, as the expert states it: a work step, a setup, a repair, an - inspection, a hand-off, an interruption — with its actors, preconditions, outcomes, and - duration. - projects_to: factored transitions and the places between them - - kind: ordering/flow - is: "How activities relate: sequence, branching, merging, triggers." - projects_to: arcs, arc types, guards - - kind: policy - is: >- - The rule applied when more than one thing could happen: who wins a contended resource, what - goes next, when to switch, when to release. - projects_to: guards and priorities where compilable; otherwise IR-only - - kind: dynamics - is: "A quantity that evolves continuously while nothing discrete happens: wear, temperature, level, charge." - projects_to: differential equations on real-valued colour elements - - kind: objective - is: >- - A question the model must answer or a decision it must inform; what "better" means; - trade-off weights. - projects_to: metrics where scalar over simulation state; weights IR-only - - kind: constraint - is: >- - A limit that must hold: capacity, eligibility, compatibility, qualification, a regulatory or - quality rule — written or unwritten; conservation laws. - projects_to: guards and capacities partially; otherwise IR-only - - kind: data-binding - is: A model variable that a real data feed could drive. - projects_to: nothing today - - kind: validation-criterion - is: How the expert would know the model is right. - projects_to: nothing today - not_kinds: - - name: resource - text: >- - A resource (a machine, a team, a vehicle, a bay) is an `entity-type` whose instances are - contended for. Its contention rule is a `policy`; its capacity is a `constraint`; its - availability is a `boundary-condition`. - - name: queue, buffer, or waiting state - text: >- - Not elicited as a node. It is implied by the activities on either side of it and emerges as - a place in projection. - - name: scenario - text: Not elicited; it is assembled at simulation time from `boundary-condition` nodes. - attributes: - - name: quantity - on: any kind - text: >- - Any duration, rate, probability, count, or capacity. Preserve the value grade the expert - reached; the row's demand says what further narrowing completion requires. - - name: source-regime - on: any kind - values: [prescribed, practiced] - text: >- - One model, not two: when the manual and the floor disagree, both are recorded on the same - node and the divergence is an ordinary typed conflict for the expert to resolve — - elicitation gold, not an error. - -schema: - preamble: | - For every node the conversation discovers, its kind decides what must be known about it and how - precisely. These rows never change when the domain changes: a repair on one kind of machine and - a repair on another are the same rows instantiated on different nodes. - anchor: - kind: objective - depends_on: the nodes it depends on - floor: - - { kind: objective, at_least: 1 } - - { kind: entity-type, at_least: 2 } - - { kind: activity, at_least: 1 } - - { kind: ordering/flow, at_least: 1 } - must_know: - - kind: objective - slot: "the question, in the expert's words" - precision: spelled out - not_applicable: false - why: "everything else is elicited relative to it" - - kind: objective - slot: "the nodes it depends on" - precision: at least 1 - not_applicable: false - why: "an objective that depends on nothing is unsupported by the model" - - kind: objective - slot: 'what "better" means, and trade-off weights' - precision: [range, spelled out] - not_applicable: true - why: "quantified objectives need a metric; some are qualitative" - - kind: entity-type - slot: "the distinctions the process treats apart" - precision: spelled out - not_applicable: false - why: "two things are one type only if the process treats them the same everywhere" - - kind: entity-type - slot: "state that rides along with each instance" - precision: spelled out - not_applicable: true - why: "colour elements; many types carry none" - - kind: entity-type - slot: "how many there are, or the population's shape" - precision: range - not_applicable: true - why: "initial populations for contended resources; unbounded is an allowed answer" - - kind: boundary-condition - slot: "the starting state" - precision: spelled out - not_applicable: false - why: "scenario initial state" - - kind: boundary-condition - slot: "the arrival or availability pattern" - precision: [spread, spelled out] - not_applicable: false - why: "source rates and calendars; a single average hides the shape" - - kind: activity - slot: "what it needs before it can start" - precision: spelled out - not_applicable: false - why: "transition preconditions" - - kind: activity - slot: "what it produces or changes" - precision: spelled out - not_applicable: false - why: "transition outcomes" - - kind: activity - slot: "who or what performs it" - precision: named - not_applicable: true - why: "resource binding; some activities are unattended" - - kind: activity - slot: "how long it takes" - precision: spread - not_applicable: false - why: "duration distribution; a point value simulates as a falsehood" - - kind: activity - slot: "how often it occurs, if it is an event rather than a step" - precision: range - not_applicable: true - why: "interruptions, failures, and arrivals have a rate; steps in the flow do not" - - kind: activity - slot: "what is lost when it changes the system's mode" - precision: range - not_applicable: true - why: "setup, changeover, restart, and warm-up losses are routinely never asked" - - kind: activity - slot: "whether its quantities vary by type" - precision: named - not_applicable: false - why: "the answer is load-bearing either way" - - kind: ordering/flow - slot: "the order things happen in" - precision: spelled out - not_applicable: false - why: "the net's structure" - - kind: ordering/flow - slot: "how a branch or merge is decided" - precision: spelled out - not_applicable: true - why: "routing; only where the flow branches" - - kind: policy - slot: "the rule as actually practiced" - precision: spelled out - not_applicable: false - why: "guards and priorities; the tacit rule, not the poster on the wall" - - kind: policy - slot: "what overrides it" - precision: spelled out - not_applicable: true - why: "exceptions are where the simulation and reality diverge" - - kind: dynamics - slot: "what changes, in which direction, at what rate" - precision: range - not_applicable: false - why: "the differential law; a direction with no rate cannot be simulated" - - kind: dynamics - slot: "how it varies around that change" - precision: spread - not_applicable: true - why: "noise can dominate threshold timing; deterministic change may explicitly have none" - - kind: dynamics - slot: "what happens at a threshold" - precision: spelled out - not_applicable: true - why: "most continuous quantities exist to trigger something" - - kind: constraint - slot: "the limit and what happens when it is hit" - precision: spelled out - not_applicable: false - why: "a capacity without a consequence cannot be simulated" - - kind: data-binding - slot: "the variable and its feed" - precision: named - not_applicable: true - why: "IR-only today; recorded so the loss report can name it" - - kind: validation-criterion - slot: "how the expert would know the model is right" - precision: spelled out - not_applicable: true - why: "IR-only; anchors the acceptance conversation" - proposals: - - type: slot-asserted - payload: slot-assertion - -patterns: - preamble: | - Patterns are discretionary. Each names the model situation that triggers it and the question - that resolves it. None names a domain; each applies wherever its trigger appears. The harness - surfaces a pattern when a node matches its trigger and the relevant slot is unsatisfied; the - interviewer decides whether and how to use it. - items: - - id: P01 - on: [activity] - slot: "how often it occurs, if it is an event rather than a step" - when: >- - an `activity` is an event that can befall the system — a failure, an interruption, an - unplanned arrival — rather than a step in the flow - ask: >- - occurrence and duration are two slots. Ask how often, as a range, for each named event - separately; then how long, as a spread. Keep the value grade the expert actually gave; never - round a range up to a spread. - - id: P02 - on: [activity] - slot: "what is lost when it changes the system's mode" - when: >- - an `activity` changes the system's mode — a setup, changeover, restart, warm-up, - reconfiguration, handover - ask: >- - ask what is lost in the transition, as a range, after a *named* transition. If the expert - does not know, ask what they would treat as an authoritative source — never convert - "unknown" into a value. Ask before recording an explicit "not applicable"; an ordinary - activity with no mode change is a useful negative answer, not a reason to skip the slot. - - id: P03 - on: [ordering/flow] - when: an `ordering/flow` moves things in groups — batches, runs, lots, loads - ask: >- - ask what the group is, the smallest sensible one, whether a group must stay together, and - what an extra split costs (extra mode changes, extra loss) on the activities it touches. - - id: P04 - on: [policy, boundary-condition] - when: >- - a `policy` or `boundary-condition` gates when something may proceed — a release, a start, an - admission - ask: >- - replace any time-shaped approximation ("about two days before") with the practiced event or - state that makes it runnable, who or what flips it, and where that is observable. - - id: P05 - on: [entity-type] - when: more than one thing can want the same `entity-type` instance at once - ask: >- - ask which wins, what overrides that, how ties break, and for a recent borderline case that - shows the practiced rule. Never infer the rule from a schedule or a document. - - id: P07 - on: [entity-type] - when: a quantity has been given for one `entity-type` and others exist - ask: ask explicitly whether it varies by type. Record "no" as a value; it is load-bearing. - - id: P08 - on: [] - when: any node has both a prescribed and a practiced form - ask: >- - record both on the same node under `source-regime`, with the expert's account of when they - diverge. Do not average them and do not pick one. - - id: P13 - on: [dynamics] - when: a `dynamics` node has been named - ask: >- - ask what it triggers when it crosses a threshold, and which `activity` resets it. A - continuous quantity that triggers nothing usually does not need to be in the model. - -# ─── Guidance ───────────────────────────────────────────────────────────────────────────────────── -# Each cell adds to the harness default under the same key. Blank means the default suffices. - -guidance: - lenses: - - name: a resource named in passing - text: >- - A machine, team, vehicle, or bay mentioned as an aside is an `entity-type` whose instances - are contended for; the contention rule it implies is a `policy`, and it is usually the - expert's least-examined knowledge. - - name: '"it depends"' - text: >- - Hides either a branch in the `ordering/flow`, a `policy` deciding it, or a quantity that - varies by `entity-type`. Ask which before moving on. - - name: '"sometimes it breaks", "we have to wait for"' - text: >- - An event-shaped `activity` with a rate and a duration, or a `boundary-condition` the system - does not control. Both are routinely left out of a first account of the flow. - - name: warming up, wearing down, filling - text: >- - A `dynamics` node — something changing continuously while nothing discrete happens — or a - mode change with a loss. The expert rarely volunteers the rate; the model cannot run - without it. - - name: '"always" and "never"' - text: >- - A `constraint` or a `policy` stated in passing. Ask what enforces it, what it limits, and - whether an exception has ever overridden it. - - name: duration that depends on the clock - text: >- - An `activity` duration whose elapsed time crosses a calendar boundary depends on a - `boundary-condition`, not only on the work itself. Ask for both the work time and the - availability rule. - techniques: - - name: value grade is not evidence - text: >- - "About three hours" from the expert is an honest `number` below the demanded grade; "three - hours" supplied by the interviewer may look narrow enough and is not evidence at all. Track - grade and evidence separately and let neither substitute for the other. - - name: stress the binding resource - text: >- - Before deepening every duration into a distribution, ask which `entity-type` actually - constrains the objective and whether its utilisation or variability makes stochastic detail - consequential. - - name: turn an unknown into an observable threshold - text: >- - When the expert cannot give an exact quantity, ask what boundary would change a decision or - become visibly unacceptable. Record the threshold they can judge; do not invent the value - beyond it. - - name: ask what is conserved - text: >- - When quantities enter and leave a process, ask what total should remain constant and where - loss is possible. A conservation answer belongs as a `constraint`, in the expert's units. - movements: - slice: - - name: one instance, arriving to leaving - text: >- - One case in this formalism is one instance of the `entity-type` that flows, followed from - the moment it reaches the system to the moment it leaves. If several things flow, first - ask which unit defines one case — order, batch, item, or another named unit. Create nodes - as they appear; as each `objective` becomes clearer, link it to the nodes it depends on. - An `objective` that depends on nothing yet is unsupported — say so and go find its - structure. - sweep: - - name: strata are kinds, net-bearing first - text: >- - After kickoff has established each `objective` and its `validation-criterion`, a stratum - is one kind. Sweep `entity-type` through `dynamics` first because they bear the net, then - complete the remaining IR-only rows. - - name: the unwritten constraints - text: >- - Close the `constraint` stratum with the unwritten rules: "what would a newcomer get wrong - in the first week?", "what do you always or never do that is written nowhere?", "which rule - exists because something once went wrong?" - - name: what can befall each activity - text: >- - Close the `activity` stratum by asking across exception types: work-item failure, deadline - expiry, resource unavailability, external trigger, and constraint violation. For each - named exception, ask what happens to the work, the case, and the recovery. - licenses: [] - motifs: - - name: shared resource - text: >- - Several activities want one `entity-type`'s instances. Ask whether the server is indivisible - or splittable by role, how many instances act together, which activity wins, and what - overrides that rule. - - name: batch, lot, load - text: >- - An `ordering/flow` that moves things in groups. Ask whether formation fires by count, by a - clock, or by either; whether the group must stay together; and what a split costs. - - name: gate or release - text: >- - A `policy` or `boundary-condition` that lets things proceed. Ask whether the gate is a state, - an event, or a person's decision; where it is observed; and what overrides it. - - name: mode change - text: >- - A setup, changeover, restart, or warm-up. Ask whether loss depends on direction, whether the - change is local or cascading, and which time, material, or capacity components are lost. - - name: event, not step - text: >- - A failure or interruption that befalls the system. Ask occurrence and duration separately, - then whether the rate is independent or changes with a named state. - - name: threshold on a continuous quantity - text: >- - A `dynamics` node. Ask what it triggers, which `activity` resets it, and — when several - components evolve — whether they combine additively or the weakest component decides. - smells: - - name: a quantity for one type and no other - text: given for one `entity-type` when others exist and never asked whether it varies (P07). - - name: a continuous quantity that triggers nothing - text: a `dynamics` node with no threshold and no consequence usually does not belong in the model. - - name: a queue as a node - text: a buffer or waiting state elicited as if it were an activity; it is implied and emerges in projection. - - name: a policy read off a document - text: the rule as posted taken for the rule as practiced; the practiced one is the slot. - - name: a point where a spread is demanded - text: a single average standing in for a duration or arrival pattern; it simulates as a falsehood. - - name: two regimes averaged - text: prescribed and practiced blended into one value instead of both recorded on the node. - rabbit_holes: - - name: building the net in conversation - text: >- - Places, transitions, arcs, and colours are projection output. Naming them to the expert - buys nothing and costs the expert's vocabulary. - - name: eliciting queues or scenarios - text: >- - Neither is a node. Ask about the activities on either side of a wait; assemble scenarios - from `boundary-condition` nodes at simulation time. - - name: depth on IR-only kinds - text: >- - Establish the `validation-criterion` and accuracy bar at kickoff even though they do not - project today. For `data-binding`, stop after the variable, source, and any evidence gap; - implementation detail belongs in the loss report. - - name: grade finer than what the expert observes - text: >- - Do not decompose a quantity below the granularity the expert can observe. Record the coarser - value and a deposit naming where finer evidence could come from. - - name: eliciting the objective's answer - text: >- - The interview builds what the model needs to answer an `objective`; it does not ask the - expert to predict that answer and store the prediction as model structure. - failure_modes: - - name: dead net - signature: no `ordering/flow` with its order spelled out; activities exist but nothing connects them - text: the floor catches presence; only the sweep catches an order that was never actually stated. - - name: unsupported objective - signature: an `objective` whose dependency slot names no node in the model - text: the model cannot answer the question it was built for; the slice never reached it. - -# ─── Runbooks ───────────────────────────────────────────────────────────────────────────────────── -# One cell set per job this plugin supports. The harness default runbook for each job comes first; -# these cells add what is true of this formalism. - -runbooks: - construct: - kickoff: - - name: what "no model exists" means here - text: >- - The user knows the system; the interviewer knows the kinds. Capture each thing the user - wants the model to answer or decide as an `objective` node. Recast an optimisation request - as a comparison among candidate policies, and name the time resolution over which the - answer must remain useful. - trajectory: - - name: kind order - text: >- - Slice one instance end to end first; the shape of the model comes from the slice. Then - sweep the nodes the slice revealed in kind order, net-bearing kinds before IR-only ones, - checking each node's rows and every pattern its state matches. - close: - - name: the deliverable - text: >- - Summarise per kind. Deliver the model with every node in the expert's own vocabulary, - each slot's value grade as actually obtained and its source-regime where both were - given; the assumption ledger; and a loss section — what the model deliberately leaves - out, which slots are open and why, which objectives are unsupported, and which kinds the - net cannot carry. - - name: what the interviewer does not claim - text: >- - The SDCPN scaffold, the code-obligation sidecar, and the typed loss report are derived by - the plugin's projection. The interviewer does not write them and must not claim the model - is loadable, compiled, or simulated. - - name: stopping outcomes - text: >- - Named and distinct: `complete-under-declared-demands`, `partial-with-open-slots`, - `unsupported-objective`, `data-deposit-required`, `expert-stopped`. - review-and-revise: - kickoff: - - name: what "a model exists" means here - text: >- - A model with its captures and a projected net. The reviewer arrives with an element of - the net in view. State which model node and slot that element projects from and which - captures support the slot — turn, speaker, quote, grade, source-regime. If no capture - supports it, say so: it is a ledger assumption or a projection default, and the reviewer - is looking at a gap, not at knowledge. - trajectory: - - name: the affected slice in this formalism - text: >- - The scope the harness computes is the node, its slots, every `objective` whose dependency - slice contains it, and every projected net element those produce. Apply the node's rows - and the patterns its state triggers, smallest delta first. - - name: the delta in the net - text: >- - Projection re-runs over the whole model, deterministically. Show which net elements - changed, which are unchanged, and which code obligations the change reopened. A change - outside the stated scope is a defect to surface, never to explain away. - close: - - name: stopping outcomes - text: >- - Named and distinct: `corrected-and-projected`, `corrected-obligation-open`, - `conflict-unresolved`, `scope-exceeded`, `reviewer-stopped`. - - name: the delta report - text: >- - In place of the whole model: the superseding captures made, the slots and objectives - whose state moved, the net elements changed and the elements confirmed unchanged, the - obligations reopened, and the stopping outcome. - - name: before handing off, verify - text: >- - Every changed net element traces to a superseding capture made in this session; no capture - outside the scope changed; the projection outside the scope is identical before and after; - the ledger records any default the correction displaced. - -# ─── Machinery ──────────────────────────────────────────────────────────────────────────────────── -# Code, declared here and exported from src/. The harness enforces completion, the sweep list, the -# ledger, and the affected slice itself; those are not plugin machinery. - -machinery: - checks: [slot-assertion] - tools: [] diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts new file mode 100644 index 00000000000..3c48ef1a56f --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/flue.ts @@ -0,0 +1,56 @@ +import { + useInitialData, + useInstruction, + useSkill, + useTool, +} from "@flue/runtime"; +import * as v from "valibot"; + +import sdcpnAppend from "./prompts/APPEND_SYSTEM.md?raw"; +import { + SDCPN_MODELLING_SKILL_NAME, + sdcpnModellingSkill, +} from "./skills/sdcpn-modelling/skill"; +import { petrinautConstructionTools } from "./tools/petrinaut-construction"; +import { + READ_PETRINAUT_DOC_TOOL_NAME, + readPetrinautDoc, +} from "./tools/read-petrinaut-doc"; + +export const VALIDATED_CONSTRUCTION_MODE = "validated-construction"; + +export const sdcpnInitialDataSchema = v.optional( + v.object({ + mode: v.literal(VALIDATED_CONSTRUCTION_MODE), + }), +); + +export type SdcpnInitialData = v.InferOutput<typeof sdcpnInitialDataSchema>; + +/** Mount the prompt material, skill, and conditional tools owned by the SDCPN plugin. */ +export function useSdcpnPlugin(): void { + const initialData = useInitialData<SdcpnInitialData>(); + + useInstruction(sdcpnAppend.trim()); + useSkill(sdcpnModellingSkill); + useTool(readPetrinautDoc); + + if (initialData?.mode === VALIDATED_CONSTRUCTION_MODE) { + useInstruction( + ` +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. +`.replace(/^\s+|\s+$/gu, ""), + ); + for (const constructionTool of petrinautConstructionTools) { + useTool(constructionTool); + } + } +} + +export { READ_PETRINAUT_DOC_TOOL_NAME, readPetrinautDoc }; +export { SDCPN_MODELLING_SKILL_NAME }; +export { + PETRINAUT_CONSTRUCTION_TOOL_NAMES, + petrinautConstructionTools, + type PetrinautConstructionToolName, +} from "./tools/petrinaut-construction"; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts index ab23c7a868e..a21e822400c 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/index.ts @@ -1,63 +1,14 @@ /** - * `@hashintel/brunch-agent-plugin-sdcpn` — the SDCPN target formalism (ADR-0006). + * `@hashintel/brunch-agent-plugin-sdcpn` — the operational-process domain + * typology paired with the SDCPN target formalism. * - * The plugin is `plugin.yaml`: data under the harness-owned keys (ADR-0007) - * whose contract keys the harness reads into the model vocabulary, the demand - * list, and the pattern index, and whose guidance and runbook cells specialise - * what the repertoire teaches. This module loads that definition and declares - * the one proposal type a kind-and-slot plugin needs: a slot assertion - * addressed to a kind, node, and slot the definition names. The definition - * names no domain, and neither does this code. - * - * **This package resolves `@hashintel/brunch-agent` and nothing else** — never the - * binding, never Flue, and it is storage-blind (spec §9.6). `project` and - * `validate` (ADR-0005) land with the realization slice. - */ - -import * as v from "valibot"; - -import { - createSlotAssertionSchema, - definePlugin, - readPluginDefinition, -} from "@hashintel/brunch-agent"; - -import pluginYaml from "../plugin.yaml?raw"; - -const nonEmptyString = v.pipe(v.string(), v.nonEmpty()); -const evidenceQuote = v.strictObject({ excerpt: nonEmptyString }); - -/** The plugin definition; reading fails loudly at module load if the contract is broken. */ -export const sdcpnDefinition = readPluginDefinition(pluginYaml); - -/** - * One slot assertion, quote-anchored, restricted to the definition's kinds and slots. - * The harness resolves quotes to evidence spans at apply time; the proposal - * carries excerpts only. + * The plugin is a contribution bundle: `prompts/` for always-on policy, + * `skills/sdcpn-modelling/` for the job skill and its resources, `tools/` for + * executable Petrinaut capabilities, and `flue.ts` for the selected mounting. + * The retired YAML definition and typed slot-assertion proposal path were + * removed on 2026-09-02. This root export carries the pairing's identity only; + * the `./flue` subpath owns the production contribution. */ -export const SlotAssertedProposal = v.strictObject({ - evidence: v.pipe(v.array(evidenceQuote), v.minLength(1)), - epistemicStatus: v.picklist(["explicit", "inferred", "tentative"]), - confidence: v.picklist(["firm", "hedged", "speculative"]), - content: v.strictObject({ - value: createSlotAssertionSchema(sdcpnDefinition), - }), -}); - -export type SlotAssertedProposalInput = v.InferInput< - typeof SlotAssertedProposal ->; -export const sdcpn = definePlugin({ - name: "plugin-sdcpn", - targetFormalism: "sdcpn", - definition: sdcpnDefinition, - proposalCatalog: [ - { - name: "slot-asserted", - description: - "Record what the expert said about one slot of one node: its kind, node, slot, the precision the answer reached, and the value or explicit absence, with verbatim quotes.", - schema: SlotAssertedProposal, - }, - ], -}); +export const SDCPN_DOMAIN_TYPOLOGY = "operational processes"; +export const SDCPN_TARGET_FORMALISM = "sdcpn"; diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/prompts/APPEND_SYSTEM.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/prompts/APPEND_SYSTEM.md new file mode 100644 index 00000000000..194d51818c0 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/prompts/APPEND_SYSTEM.md @@ -0,0 +1,11 @@ +# Operational Process Modelling for SDCPN + +Specialize the universal elicitation role to operational processes represented as stochastic dynamic coloured Petri nets (SDCPNs) in Petrinaut. Help a person develop or revise an evidence-faithful process-model workpiece and, when the available evidence and tools support it, construct and check a net from that workpiece. + +Activate the `sdcpn-modelling` skill before substantive interviewing, workpiece revision, or construction. + +During interactive elicitation, speak about the operation in the person's vocabulary rather than places, transitions, arcs, colours, tokens, firing rules, or workpiece headings. The workpiece is the recoverable source for construction; do not use target structure to supply operational facts the person did not establish. + +Use mounted Petrinaut construction tools for net changes when they are available. Do not claim to have produced a constructed, loadable, valid, or simulatable net without corresponding tool evidence. When evidence or capabilities are insufficient, deliver the best honest workpiece or construction-gap report instead. + +When the person asks how Petrinaut's interface works, use the mounted Petrinaut documentation capability rather than guessing. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts index d6729bb002e..005ef3c2c3b 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/raw-imports.d.ts @@ -1,5 +1,5 @@ -/** Vite's `?raw` import: the plugin definition ships inside the bundle as a string. */ -declare module "*.yaml?raw" { - const yaml: string; - export default yaml; +/** Vite's `?raw` imports ship authored resources inside the bundle. */ +declare module "*.md?raw" { + const markdown: string; + export default markdown; } diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md new file mode 100644 index 00000000000..9552111c938 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/SKILL.md @@ -0,0 +1,50 @@ +--- +name: sdcpn-modelling +description: Elicit or revise an operational process model, maintain its recoverable workpiece, and construct a checked SDCPN when Petrinaut capabilities are available. Use for a process-modelling interview, Petri net, or analysis or revision of either artifact. +--- + +# Capability-aware lifecycle + +Use one conceptual lifecycle: orient, elicit or revise, maintain the workpiece, construct when supported, check, and deliver. The current conversation may expose only one branch of that lifecycle. Do not claim that an unavailable transition occurred. + +## Select the runtime branch + +### Interactive elicitation or revision + +Interview in the person's operational vocabulary. Activate the `elicitation` skill and read `references/profile.md` before substantive questions or revision. Read `templates/workpiece.md` when creating or materially revising the shared workpiece. Construct only when the mounted capabilities actually permit construction in this conversation. + +### Construct-only execution + +Use the supplied workpiece as the complete modelling input. Do not interview. Read `references/pn-construction.md` and `references/checks.md`, then use the mounted construction tools. If a consequential workpiece gap prevents faithful construction, report the gap and the smallest question a later interactive elicitation must answer; do not ask it or invent an answer in this conversation. + +## Procedure + +### Orient + +Establish enough purpose and context to select one focused next action: the intended question or decision, audience, boundary, horizon, accuracy need, and available time. Orientation need not settle every concern before elicitation begins. + +### Elicit or revise + +For a new account, follow one concrete case and re-evaluate the active gap after each useful answer. For an existing account, first locate the disputed or changed material and its consequence for the objective. Use the `elicitation` skill's universal guidance and `references/profile.md` for detailed operations and coverage; do not turn their register order into question order. + +### Maintain the workpiece + +Treat the workpiece as the recoverable account construction will consume. Update it after a useful stretch rather than waiting until the end. Preserve unrelated material unless new evidence affects it. + +Whenever the workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit it again before construction and before workpiece-only delivery. A delta or prose promise is not a recoverable workpiece. + +### Construct + +Construct only from the current workpiece. Read `references/pn-construction.md` and `references/checks.md` before beginning. Use mounted Petrinaut tools for every net change and inspect the resulting definition rather than emitting free-form net JSON. If the required tools are absent, limit the result to the workpiece and construction-ready notes. + +Construction may infer a representation from recorded operational meaning; it may not invent operational facts. Record construction inferences, approximations, defaults, and target losses in the workpiece. + +### Check and deliver + +Apply `references/checks.md` whenever construction is prepared or attempted. Deliver the current workpiece in every branch. Deliver a net only when the mounted tool path has produced and checked one. State what the result can support, what remains open, what was assumed or simplified, and what the target or current tools could not represent. + +An explicit stop opens no new topic. In an interactive conversation, emit the best current workpiece and any already-checked net with limitations visible. In construct-only execution, report a blocking gap rather than opening an interview. + +## Resource discipline + +Read resources directly from this skill's advertised resource list, using the exact `/.flue/packaged-skills/...` path shown in the activation briefing; the relative name is a label only. Do not treat Markdown links as includes, follow references recursively, or read construction material merely to frame ordinary interview questions. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/checks.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/checks.md new file mode 100644 index 00000000000..69d0845b87f --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/checks.md @@ -0,0 +1,116 @@ +# Workpiece, Construction, and Delivery Checks + +Read this when preparing to construct, after construction changes, and before delivering a net. For workpiece-only delivery, apply the universal and plugin Verification registers without loading this construction resource. + +A failed check triggers the smallest relevant repair available in the current runtime branch: amend the workpiece, ask during interactive elicitation, revise construction, or report a visible limitation and re-entry question for a later conversation. + +## Evidence levels + +Report the highest level actually reached. Passing one level does not imply the next. + +### 1. Tool-schema acceptance + +The mounted construction tools accepted the submitted payloads, and the latest inspected definition contains the accepted changes. This establishes conformance to those tool input schemas and the shape returned by inspection. It does not establish correspondence with the workpiece, reachability, resource conservation, exclusivity over executions, loadability in another consumer, or simulated behavior. + +### 2. Agent-reviewed structural correspondence + +The agent compared the inspected definition with the workpiece and found visible structures corresponding to the recorded process. This can establish that named elements, connections, candidate paths, guards, resource-return structures, and parameters are present and apparently aligned. It remains a review judgment over static structure, not behavioral proof. + +### 3. Behavioral execution or stronger analysis + +An actual simulation, state-space exploration, invariant check, or other named analysis exercised the constructed definition. State exactly which method, scenario, initial state, parameters, paths, and observations were covered. A simulation run establishes only the behavior observed in that run; a universal claim such as “resources cannot leak” requires an analysis whose scope genuinely covers every relevant execution. + +If no behavioral execution or stronger analysis occurred, say so. Do not convert tool acceptance or visual inspection into behavioral validation. + +## Before construction + +- The intended question, comparison, or decision is stated in the person's terms. +- The boundary and a meaningful concrete case are cold-readable from the workpiece. +- The process spine says what flows, what admits it, what happens and in what order, what changes the path, where waiting comes from, and what outcome or handoff ends it. +- Inputs that matter are distinguished as consumed, reserved/released, or read. +- Required resource availability and release are recorded or visibly unknown. +- Consequential quantities retain their context and supported precision. +- Practiced and prescribed rules, corrections, conflicts, and contextual variants are not silently collapsed. +- Construction can proceed without recovering a load-bearing fact from transcript memory. +- Assumptions, unresolved matters, omissions, and anticipated losses are visible. + +If the missing material admits materially different process structures, formulate the smallest resolving question before constructing. Ask it only during interactive elicitation; in construct-only execution, return it as a blocking re-entry question. If the person has stopped, deliver the partial workpiece instead of opening a new topic. + +## Tool-schema acceptance checks + +- Every intended construction call was accepted or its rejection remains explicitly unresolved. +- The latest inspected definition contains each accepted place, transition, type, parameter, and connection under the identifier returned or supplied. +- Every referenced endpoint exists in the inspected definition. +- Arc weights or multiplicities are positive and conform to the mounted schema. +- No later step depends on a rejected or absent change. + +Re-inspect after dependent stages and once at the end. Record rejected calls and repairs. Describe this result as **tool-schema accepted**, not valid, runnable, or simulated. + +## Agent-reviewed structural correspondence + +Compare the latest inspected definition with the authoritative workpiece claims. + +- The definition contains at least one meaningful place and transition corresponding to the process account. +- It contains a candidate structural path from a represented initial or admitted condition toward an outcome. This does not establish that the path can fire. +- Visible branches, joins, loops, and recovery structures correspond to the workpiece's stated ordering and conditions. +- For each enumerated resource-holding path, the intended acquisition and return structures are present. This does not establish conservation over every execution. +- Consumed inputs lack an unintended return structure; reserved inputs have an intended return structure; read-only information remains visibly available by the chosen representation. +- Mutually exclusive outcomes or modes have apparently exclusive guards or structure. This does not establish that they can never overlap at runtime. +- Direction-dependent mode changes retain distinct structural losses where the workpiece requires them. +- Continuous dynamics have a recorded quantity, consequential threshold or effect, and workpiece support. +- Required parameters and initial populations are represented or explicitly named as external inputs. +- Waiting is explained by recorded surrounding conditions rather than an unsupported queue object. + +Record discrepancies and the agent judgment used to resolve or preserve them. Describe a passing result as **structurally reviewed against the workpiece**. + +## Behavioral evidence + +Only report observations produced by an actual execution or named stronger analysis. + +- Record the exact definition revision, scenario, initial state, parameters, duration or stopping condition, and analysis method. +- State which process path or property was exercised. +- For a simulation, report only observed progress, resource balances, mode states, outputs, and failures from the runs performed. +- For state-space or invariant analysis, report the explored scope, assumptions, and any unexamined behaviors. +- Relate each observation back to the workpiece objective it bears on. +- Preserve failures and counterexamples; do not summarize them as a pass because another run succeeded. + +No behavioral tool or result means no behavioral claim. + +## Fidelity and uncertainty + +- Every load-bearing net choice traces to an authoritative workpiece claim or a named construction inference, approximation, or default. +- No hedge has been hardened solely to satisfy a schema. +- No conflict has been averaged and no contextual value has been made universal without an accepted simplification. +- Assumptions state why they were introduced, what they affect, and how they could be checked. +- Material retained only in the workpiece is named as a target or tooling loss rather than omitted silently. +- The delivery distinguishes accepted structure, agent review, observed behavior, and universal guarantees. + +## Revision checks + +When revising an existing workpiece or analyzing a requested net change: + +- the changed or disputed workpiece material is explicit; +- the prior and current account are distinguishable as correction, conflict, or contextual coexistence; +- the desired net delta follows from changed workpiece meaning; +- unsupported update or removal operations are reported rather than imitated with competing additive structure; +- any applied additive net changes preserve the intended existing structure at the level actually inspected; +- assumptions and losses displaced or introduced by the revision are reported; +- the delivery distinguishes what changed from what was only inspected and says what the model can now support that it could not support before, or vice versa. + +## Delivery + +Always deliver the current recoverable workpiece. Deliver a net only if construction occurred through available tools and the resulting definition was inspected. + +State plainly: + +- what question or decision the result is intended to support; +- whether the workpiece is sufficient for that purpose or partial with named gaps; +- whether construction was not attempted, blocked, partial, or tool-schema accepted; +- whether an agent-reviewed structural comparison occurred and what discrepancies remain; +- whether behavior was untested, observed in named simulations, or established to the stated scope by stronger analysis; +- what the agent inferred, approximated, defaulted, simplified, or omitted; +- what remains unknown, unasked, declined, deferred, conflicting, or unsupported; +- what the target formalism or current tooling could not represent; +- what smallest next evidence would change the result. + +Do not collapse these levels into “validated,” “correct,” “runnable,” or “simulatable” without naming the evidence that supports that exact claim. Do not convert the delivery descriptions into a closed completion algebra. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/pn-construction.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/pn-construction.md new file mode 100644 index 00000000000..6f2dcb443fb --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/pn-construction.md @@ -0,0 +1,127 @@ +# SDCPN Construction + +Read this only when constructing, revising, or checking a net. Consume the current process-model workpiece; do not reread the transcript as the primary model. + +Construction translates recorded operational meaning into SDCPN structure. It may choose a representation, introduce a visibly named approximation, or report a loss. It may not invent operational facts to make the net complete. + +## Construction boundary + +Before constructing, confirm that the workpiece states what the model must support and contains a usable process spine: what flows, what admits it, what happens and in what order, what changes the path, what resources are occupied, and what outcome ends or hands off the case. + +If materially different nets remain possible because one operational distinction is missing, formulate the smallest resolving question. Ask it only when interactive elicitation is available; in construct-only execution, report it as the required re-entry and stop the unsupported path. + +When Petrinaut construction tools are mounted, their accepted schemas and the inspected resulting definition are the authority for payload fields and net state. Use the tools for every net change; do not emit free-form net JSON. When tools are absent, leave construction-ready notes and do not claim a loadable net. + +## Mapping principles + +| Recorded operational meaning | Possible SDCPN interpretation | +| --- | --- | +| Things that flow, are acted on, or do work | Typed tokens and colour elements when distinctions change behavior | +| Initial populations, arrivals, departures, calendars, and external inputs | Initial marking, parameters, boundary conditions, or source and sink transitions where representable | +| Logical activities | Transitions, factored into start, in-progress state, and completion only when timing or resource semantics require it | +| Waiting, availability, and occupied state | Places derived from the activities and conditions on either side, not independently elicited queue nodes | +| Ordering, branching, joining, triggers, and practiced decision rules | Arcs, guards, priorities, and explicit enabling state | +| Resource consumption, reservation, release, and read-only use | Consumed tokens, held and returned resource tokens, or read behavior | +| Continuous change | Dynamics on real-valued colour elements when a rate, threshold, or objective makes it consequential | +| Metrics and objectives | Simulation metrics where representable; qualitative goals and unsupported weights remain in the workpiece | +| Data bindings and validation criteria | Workpiece obligations until a separate integration represents them | + +A physical location becomes target structure only through its recorded operational effect; it is not automatically a Petri-net place. A simulation scenario is assembled from initial state, boundary conditions, parameters, and candidate policies rather than represented as one process node. + +## Petrinaut tool sequence + +When the corresponding tools are mounted: + +1. Call `getLatestNetDefinition` before changing the net. +2. Add only workpiece-supported token types and tunable parameters with `addType` and `addParameter`. +3. Add places and transitions with `addPlace` and `addTransition`; establish stable identifiers before connecting them. +4. Add connections with `addArc`. Arc weights are positive token multiplicities, not switches for mutually exclusive modes. +5. Re-inspect with `getLatestNetDefinition` after each dependent stage and at the end. +6. Correct rejected calls in the same conversation or state why construction remains partial. + +The mounted schemas, not this prose, govern exact payload fields. + +## Construction patterns + +Patterns are candidate transformations whose premises must already be present in the workpiece. They do not supply missing facts. + +### Timed work + +When a logical activity occupies consequential time, represent start, in-progress state, and completion separately. Preserve what remains occupied while work runs. Use a constant or named parameter when only a typical duration is supported; do not invent a distribution family or tail. + +### Conditional or probabilistic outcome + +Represent mutually exclusive outcomes with distinct enabled paths. Use a recorded rule, condition, parameter, or probability. If no probability is supported, do not manufacture an even split; preserve a symbolic parameter, use a non-probabilistic condition when available, or report the gap. + +### Contended resource + +Hold available instances in shared resource state. A work-start transition acquires the required tokens; competing work cannot use them while held; success, failure, cancellation, or recovery returns them when the workpiece says they become available. Preserve changed wear, qualification, location, or other consequential state on return. + +Compile practiced contention rules into guards or priorities only when their selecting conditions are recorded. + +### Consumed, reserved, and read inputs + +- **Consumed or transformed:** remove the input from its source state and produce only the outputs the workpiece records. +- **Reserved:** remove or lock availability at start, carry the association through work, and return the input at release. +- **Read:** allow the activity to depend on the input without making it unavailable to other work. + +Confirm that the target's actual arc semantics implement the intended use; syntactic convenience does not override operational meaning. + +### Gate, release, trigger, or prerequisite + +Represent the observable enabling condition and the event or actor that changes it. Use a guard, state place, external source, or timed event appropriate to the workpiece. Preserve overrides rather than silently weakening the gate. + +### Batch, lot, load, or grouped movement + +Represent formation by the recorded count, clock, or combined release rule. Preserve whether the group stays together and any split, merge, setup, or capacity cost. Do not infer a preferred batch size from a maximum. + +### Mode change + +Represent source and destination availability states with directional transitions when setup, changeover, restart, handover, or reconfiguration changes behavior. Attach time, material, scrap, or capacity loss to the direction where it occurs. + +### Event, failure, retry, and recovery + +Represent disruptions separately from normal progress when they befall the process rather than advance it. Place the return path at the recorded retry scope: failed activity, repeated subsequence, whole-case restart, diversion, or scrap. Preserve the work, state, and occupied resources that survive or reset. + +### Continuous quantity and threshold + +Carry a changing quantity in state with the supported evolution law. Fire consequential behavior at the recorded threshold and add a reset only when one is supported. Omit a floating continuous variable that affects no objective or process behavior. + +### Spatial transfer + +Represent transfer as an activity when location change consumes time or resources. Reserve transport capacity when contended and preserve origin-to-destination dependence when supported. + +### Hidden waiting + +Derive waiting from unavailable resources, unmet prerequisites, calendar state, batching, transport, policy, or disruption. An intermediate place may be required, but its meaning comes from those surrounding conditions rather than an elicited queue object. + +## Inference, approximation, and target loss + +Name every representational choice not directly supported by the operational account. Preserve its reason, consequence, and route to checking in the workpiece. + +Potentially acceptable when purpose-relative and visible: + +- collapsing several named micro-steps when no objective depends on their internal order; +- representing an unknown rate as a parameter rather than a value; +- using a constant for variation judged immaterial to the stated purpose; +- choosing one of several behaviorally equivalent net factorizations; and +- supplying layout positions that carry no operational meaning. + +Not acceptable: + +- filling an empty workpiece concern from generic operations knowledge; +- averaging conflicting or context-dependent values; +- interpreting “unknown” as a conventional distribution; +- treating a posted rule as practiced behavior; +- inventing release, recovery, retry, or branch semantics; or +- claiming a net is loadable, valid, or simulated without corresponding tool evidence. + +Record workpiece material the target or current tools cannot faithfully carry, including qualitative objectives without usable metrics, policy whose deciding condition remains tacit, live data bindings not connected by the current path, validation judgments outside net semantics, and contextual distinctions collapsed by an accepted simplification. + +## Existing-net analysis and bounded change + +Start from the changed or disputed workpiece material and inspect the current net before mutation. Identify the elements whose meaning depends on that material and the desired delta. + +Do not claim general net revision unless mounted capabilities can update or remove existing structure. With an add-and-inspect subset, apply only genuinely additive changes that preserve the intended existing structure; otherwise stop after analysis and describe the unsupported update or removal. Never simulate replacement by adding competing elements beside obsolete ones. + +After a supported change, report what was added, what was only inspected, which objective consequences changed, and which assumptions or losses opened or closed. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/profile.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/profile.md new file mode 100644 index 00000000000..6298196e85b --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/references/profile.md @@ -0,0 +1,222 @@ +# Operational-Process and SDCPN Elicitation + +This reference adds operational-process and SDCPN-specific guidance to the universal guidance in the `elicitation` skill. Apply both under the same registers. The additions below do not restate or replace universal elicitation guidance. + +The registers are not a questionnaire or phase sequence. **Recognition** suggests what may be present. **Operations** select ways to investigate an active gap. **Coverage** says what the operational account may need to explain. **Verification** checks the current interview and workpiece. Petri-net construction mechanics remain in `pn-construction.md` and are not interview vocabulary. + +## Directives + +### Build the operational account the purpose needs + +For the person's simulation question, comparison, or decision, establish what should improve or be avoided, what may be varied, how outcomes are judged, and what observation would make the model credible enough for its intended use. + +Every objective needs a traceable dependency on process material. Do not collect operational detail merely because the target could represent it, and do not record the person's prediction of an objective's answer as process structure. + +### Keep target structure backstage + +Ask about work, things, people, resources, conditions, decisions, time, failures, and outcomes in the person's vocabulary. Places, transitions, arcs, colours, tokens, firing, target schemas, and workpiece headings may guide attention but must not become the language or order of ordinary questions. + +### Preserve operational context + +A value without its applicable item, activity, direction, mode, operating regime, calendar, load, location, resource, or other condition may simulate as a falsehood. Preserve context when the operation treats cases differently. Keep quantities at the granularity the operation or available source can observe, and do not average distinct regimes merely to obtain one parameter. + +### Treat operational patterns as hypotheses + +Recurring operational shapes expose conditional information needs; they do not generate facts or model elements. Ask whether a shape is present and let the person's account supply its structure and vocabulary. + +## Recognition + +Recognition entries identify possibilities to test, not facts to record automatically or reasons to abandon the active conversational thread. + +### Language and account signals + +- **“It depends”** may indicate a branch, practiced decision rule, contextual quantity, or distinction among kinds of work or resource. +- **“Sometimes it breaks,” “we have to wait,” or “it arrives unexpectedly”** may indicate a disruption, blocked prerequisite, unavailable resource, calendar, or uncontrolled boundary input. +- **“Always” or “never”** may indicate a constraint, policy, enforcement mechanism, or unexamined exception. +- **Warming up, wearing down, charging, filling, cooling, or draining** may indicate continuous change, a consequential threshold, or a directional mode change. +- **Duration that crosses a shift, opening window, or calendar boundary** may depend on availability rather than work time alone. +- **A person, machine, team, vehicle, bay, tool, dataset, or location named in passing** may participate as performer, contended resource, prerequisite, information source, transport concern, boundary, or merely context. +- **Approval, instruction, receipt, schedule, threshold, or event language** may identify what starts or enables work rather than an ordinary process activity. + +### Operational situation patterns + +#### Timed work + +An activity occupies time or timing affects the objective. Possible distinctions include start and finish, what remains unavailable while it runs, typical versus tail duration, and dependence on load, kind, calendar, location, or resource availability. + +#### Conditional or probabilistic outcome + +An activity is not guaranteed to succeed or different next states may follow. Possible distinctions include what decides the outcome, what each path produces, whether an observed rate exists, which contexts alter it, and what recovery follows. + +#### Contended resource + +Several activities or cases want the same person, machine, bay, vehicle, tool, space, or capability. Possible distinctions include count, indivisibility, joint staffing, eligibility, reservation and release, practiced priority, tie-breaking, overrides, and changed state on return. + +#### Consumed, reserved, or read input + +An activity may consume or transform an input, reserve it so others cannot use it until release, or read it without making it unavailable. The same named thing may play different roles in different activities. + +#### Gate, release, trigger, or prerequisite + +Work becomes enabled by an observable event or condition. Possible distinctions include what is observed, who or what changes it, where it is visible, whether it is external, and what can override it. + +#### Continuous quantity and threshold + +A level, temperature, charge, wear state, count, or other quantity changes while no discrete activity occurs. Possible distinctions include direction and rate, noise or spread, consequential thresholds, triggered effects, and reset behavior. + +#### Mode change + +Setup, changeover, restart, warm-up, handover, reconfiguration, or cleaning changes what can happen next. Possible distinctions include direction, time, scrap, material, capacity, and effects beyond the local activity. + +#### Batch, lot, load, or grouped movement + +Work moves or is processed in groups. Possible distinctions include the grouping unit, minimum or preferred size, count-versus-clock release, whether members stay together, splitting or merging, and the cost of breaking the group. + +#### Spatial transfer + +A change of location consumes time, capacity, or a transport resource or changes eligibility. Possible distinctions include what moves, origin and destination, duration by direction, transport contention, and whether the transfer is inside the modeled boundary. + +#### Event, failure, retry, and recovery + +A disruption befalls the operation rather than advancing normal work, or a case repeats part or all of its process. Possible distinctions include occurrence, affected work and resources, duration, retry scope, surviving state, rollback or compensation, retry limit, diversion, and terminal outcome. + +#### Policy under pressure + +More than one action is possible or more than one claimant wants the same capability. Possible distinctions include posted and practiced rules, local judgment, tie-breaking, exception authority, and the conditions selecting a different rule. + +#### Hidden waiting + +A gap between activities may be caused by unavailable input, contention, a calendar, release policy, batching, transport, approval, or recovery. Waiting is evidence to explain through surrounding behavior, not automatically an activity or independently elicited node. + +## Operations + +Use the universal Operations as the primary interviewing repertoire. These additions bind them to operational-process concerns. + +### Choose the case unit before slicing + +When several things flow, ask which unit makes one concrete case intelligible—an order, item, batch, patient, vehicle, request, or another term the person supplies. Follow it from admission or trigger to termination or handoff. + +### Link the slice to the objective + +As the case unfolds, note which activities, decisions, resources, conditions, and outcomes the stated objective depends on. If the objective depends on nothing yet recorded, continue the case rather than collecting detached detail. + +### Expose the process spine + +Ask what starts the case, what happens next, what each activity needs and changes, how branches are decided, where waiting occurs and why, and what ends the case. A list of activities without order, triggers, or outcomes is not a process spine. + +### Sweep operational concerns, not headings + +After a slice, choose one property that matters to the objective and examine it across the relevant things already discovered. Useful sweeps include duration, input-use mode, availability, contextual variation, practiced policy, failure and recovery, boundary behavior, or evidence quality. + +### Distinguish consumed, reserved, and read inputs + +For each load-bearing activity and input, ask whether the input is used up or transformed, made unavailable and later released, or observed while remaining available. If reserved, establish when and in what state it returns. + +### Sweep what can befall an activity + +Across the activities exposed by a slice, investigate relevant disruptions: work-item failure, deadline expiry, unavailable resource, external event, and constraint violation. Establish what happens to the work, the case, occupied resources, and recovery. + +### Test practiced policy with a borderline case + +When a stated rule decides what happens next, ask about the last contested or exceptional case to expose tie-breaking, overrides, and the conditions selecting a different practiced rule. + +### Close a resource account + +For a contended resource, establish usable instance count or an honest unknown, eligibility, joint requirements, acquisition, unavailability while held, release, changed state on return, and the rule used when demand exceeds availability. + +### Close a mode change in both directions + +Ask whether A-to-B differs from B-to-A and whether losses propagate beyond the local activity. Preserve time, material, capacity, and sequencing consequences separately when the operation does. + +### Turn waiting into a causal question + +Ask what the case is waiting for and what observable event makes it able to continue. Record the surrounding prerequisite, resource, policy, calendar, batch, transport, or disruption rather than treating waiting itself as an activity. + +### Ask what is conserved + +When quantities enter and leave a process, ask what total should remain constant, where loss is possible, and which units the operation uses. Treat the answer as a constraint only when the person's account supports it. + +### Establish retry scope + +Ask whether failure repeats one activity, a subsequence, or the whole case; diverts or scraps the case; and which work, state, and occupied resources survive or reset. + +### Establish validation from observable behavior + +Ask what observation, replay, historical comparison, or expert judgment would make the model credible for its intended question. Preserve validation evidence separately from the process behavior it tests. + +## Coverage + +Coverage identifies what the process-model workpiece may need for the stated purpose and downstream SDCPN construction. It is neither question order nor a demand to populate irrelevant categories. + +### Purpose, goals, measures, constraints, and thresholds + +Preserve what the model must answer or compare, for whom, what should improve or be avoided, what may be varied, what “better” means, and any importance, trade-off, safety condition, tolerated probability, or threshold the person can actually judge. Qualitative goals must not be forced into invented scalar weights. + +### Process boundary, triggers, prerequisites, and initial conditions + +Preserve what is inside and outside, what enters or leaves, what starts or resumes a case, calendars and availability, approvals or instructions, external events, initial populations, and the reliability of boundary inputs where consequential. + +### Participants, locations, flowing things, and resources + +Preserve who or what flows, performs, decides, supplies, occupies, or constrains the process; distinctions the operation treats differently; relevant carried state; counts and population shape; qualifications and compatibility; and how locations or transfers affect behavior. + +### Activities, inputs, outputs, and resource use + +For each consequential logical activity, preserve its operational name, prerequisites, performer, inputs, whether each input is consumed, reserved, or read, outputs and state changes, duration, success and failure outcomes, and contextual variation. One logical activity may later require several Petri-net elements; do not split it into target nodes during elicitation. + +### Flow, branching, joining, failure, retry, and recovery + +Preserve activity order, what decides branches and joins, what can interrupt normal flow, conditions for unhappy paths, retry and recovery scope, what happens to work and resources, and terminal outcomes. + +### Time, quantities, arrivals, and stochastic behavior + +Preserve durations, rates, counts, capacities, probabilities, arrival and availability patterns, relevant typical and tail behavior, contextual dependence, continuous quantities, direction and rate of change, variation, thresholds, and resets at the precision the evidence supports. + +### Policies, exceptions, practiced rules, and contextual regimes + +Preserve rules applied when more than one thing could happen, contention priorities, tie-breaking, release conditions, overrides, prescribed versus practiced accounts, and the context in which each account holds. + +### Validation, evidence sources, and data bindings + +Preserve how the person would know the model is credible, what observations or historical data could test it, which variables a real feed might drive, the source or dataset for each binding, who or what could answer missing values, and where evidence remains unavailable. + +### Things not independently elicited as target nodes + +- A queue, buffer, or waiting state is ordinarily explained by surrounding activities and conditions and may emerge as construction structure. +- A scenario is assembled from boundary conditions, initial state, parameters, and candidate policies at simulation time. +- A resource is an operational role combining relevant identity, capacity, availability, acquisition, release, and policy; it has no single universal target shape. +- A physical location becomes target structure only through a recorded operational effect; it is not automatically a Petri-net place. + +## Verification + +Apply these checks while eliciting and maintaining the workpiece. Construction and delivery checks live in `checks.md`. + +### Purpose and process + +- At least one simulation question, comparison, or decision is stated in the person's terms. +- Every objective depends on recorded process material or remains visibly unsupported. +- A concrete case has an admission or trigger, ordered activities, relevant branch conditions, and an outcome or handoff. + +### Operational semantics + +- Load-bearing inputs are distinguished as consumed, reserved and later released, or read while remaining available. +- A contended resource has count or an honest unknown, acquisition, unavailability while held, release, and practiced contention policy where required. +- Failure records the fate of the work, retry scope, surviving state, occupied resources, recovery, and terminal result where consequential. +- Hidden waiting has not silently become an activity or unexplained queue. +- Mode-change and spatial-transfer effects preserve direction and context where they differ. + +### Quantities and context + +- A duration, rate, probability, count, or threshold retains the item, activity, mode, direction, load, location, calendar, or other condition that selects it. +- A point value stands only where constancy or a purpose-relative simplification is supported and named. +- Prescribed and practiced regimes or contextual variants have not been averaged into one false value. + +### Failure signals and repairs + +- **Unsupported objective:** the model question depends on no recorded process material. Return to a concrete case that bears on it. +- **Dead process spine:** activities exist but their order, triggers, or outcomes do not. Continue the slice or ask the smallest connecting question. +- **Resource disappearance:** a reusable capability is acquired but its release or changed return state is missing. Re-enter resource-use Coverage. +- **Practice laundering:** a document's rule is recorded as practice without an operational case. Use a borderline or last-occurrence operation. +- **Context collapse:** values differing by item, direction, mode, location, load, or source regime become one unconditional value. Restore the selecting context. +- **Pattern-generated fact:** a situation pattern supplied structure the person did not establish. Remove or mark the assumption, then ask whether the pattern applies. +- **Target leakage:** questions are framed as places, transitions, arcs, or workpiece fields. Translate back to operational events, conditions, things, and consequences. diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts new file mode 100644 index 00000000000..1ff8d916f5f --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/skill.ts @@ -0,0 +1,17 @@ +import { skillFromMarkdown } from "@hashintel/brunch-agent/flue"; + +import checks from "./references/checks.md?raw"; +import pnConstruction from "./references/pn-construction.md?raw"; +import profile from "./references/profile.md?raw"; +import skillMarkdown from "./SKILL.md?raw"; +import workpieceTemplate from "./templates/workpiece.md?raw"; + +export const SDCPN_MODELLING_SKILL_NAME = "sdcpn-modelling"; + +/** The plugin's one job skill: operational-process elicitation, workpiece, construction, and checks. */ +export const sdcpnModellingSkill = skillFromMarkdown(skillMarkdown, { + "references/checks.md": checks, + "references/pn-construction.md": pnConstruction, + "references/profile.md": profile, + "templates/workpiece.md": workpieceTemplate, +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md new file mode 100644 index 00000000000..f5b27c5bd13 --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/skills/sdcpn-modelling/templates/workpiece.md @@ -0,0 +1,106 @@ +# Process-Model Workpiece Template + +This domain-primary workpiece is maintained during elicitation and revision and consumed during construction. It is structurally organized but not a closed semantic claim system. Follow the person's thread during the conversation; do not read these headings aloud as a questionnaire. + +## Locality rule + +Every operational claim has one authoritative home under the relevant purpose or operational concern. Keep exact expert wording, normalized interpretation, agent inference, uncertainty, assumptions, corrections, conflicts, and contextual variation beside that claim when those distinctions matter. Do not repeat the claim in a centralized evidence section or ledger. + +Labels such as **Expert evidence**, **Working account**, **Agent inference**, **Assumed**, **Unknown**, **Not yet asked**, **Declined**, **Deferred**, **Conflict**, **Correction**, **Contextual variation**, **Omitted**, and **Loss** are optional annotations, not mandatory fields or a closed type system. An assumption states why it was introduced and how it could be checked. A correction identifies the account it replaces without leaving both active. Contextual coexistence keeps each account beside the condition selecting it. + +Use the cross-cutting issue ledger only when an unresolved matter affects several authoritative claims or needs a later return path. Ledger entries reference those claims; they do not summarize them again. + +Whenever this workpiece changes substantially, emit the full current document in a fenced block whose language tag is exactly `runbook-ir`. Emit the full latest document again before a construction handoff and before workpiece-only delivery. + +```markdown +# Process-Model Workpiece + +## Purpose and posture + +### What the model must answer, compare, or support + +### Who will use it and how + +### Boundary, horizon, and accuracy expectation + +### Available time and assumption appetite + +### What the result must not claim + +## Operational account + +These are filing homes, not interview order. Use only the sections relevant to the stated purpose; keep a consequential omission visible. Place each operational claim once and attach evidence or epistemic annotations at that location when needed. + +### Goals, measures, constraints, and thresholds + +### Boundary conditions, triggers, prerequisites, and initial state + +### Participants, locations, flowing things, and resources + +### Activities, inputs, outputs, and resource use + +For each load-bearing input, preserve whether it is consumed or transformed, reserved and later released, or read while remaining available. Describe each activity locally here; put its place in the ordered case only in the process-spine section below. + +### Case and process spine: flow, branching, joining, failure, retry, and recovery + +Give the authoritative cold-readable ordered account in the person's vocabulary. Begin with a concrete case: what admits it to the process, what flows, which named activities occur and in what order, what decisions or conditions change the path, where it waits and why, what failure and recovery do to the case, and what outcome or handoff ends it. Reference activity and resource entries instead of restating their local details. + +#### Primary case: <person's name for the case> + +##### Trigger or admission + +##### Ordered account and references + +##### Branches, joins, waits, failures, recovery, and outcomes + +##### Objective dependencies + +#### Additional or contrasting case: <name> + +Add only when a different case exposes structure the primary case does not. + +### Time, quantities, arrivals, and stochastic behavior + +### Policies, exceptions, practiced rules, and contextual regimes + +### Validation evidence and data sources + +## Cross-cutting issue ledger + +Use only for an unresolved matter that affects several concerns or needs later re-entry. In one compact entry, reference the authoritative claim locations, state what remains unresolved and what it prevents, and name the evidence or event that would re-enter it. Do not copy the affected claims here. + +- **<issue>** — affects: <heading references>; unresolved: <gap, conflict, assumption, deferral, or other matter>; consequence: <what it prevents>; re-enter when: <source, observation, decision, or question>. + +## Construction notes + +Open this section when construction begins; do not use it to script ordinary elicitation. Reference authoritative workpiece claims rather than reproducing them. + +### Candidate target structures + +### Construction inferences, approximations, and defaults + +### Questions reopened by construction + +### Target-representation losses + +## Delivery status + +Summarize status by reference to the authoritative account and issue ledger; do not create a second model summary. + +### What this workpiece currently supports + +### Consequential gaps + +### Net status + +State whether construction was not attempted, blocked, partial, or tool-schema accepted; whether the inspected definition was structurally reviewed against the workpiece; and whether behavior was untested, observed in named simulations, or established to a stated scope by stronger analysis. Do not infer a higher level from a lower one. +``` + +## Maintenance guidance + +- Prefer the person's terms for names and process descriptions. +- Update the claim at its authoritative location when understanding changes; do not append a competing summary elsewhere. +- Keep evidence and epistemic treatment local even when a cross-cutting issue references the claim. +- Update the authoritative case-and-process-spine section when ordering or case behavior changes; reference local activity and resource claims rather than repeating them. +- Empty sections may be removed when irrelevant. Use **Not yet asked**, **Unknown**, or **Omitted** only when that state itself matters to later work. +- Construction consumes this workpiece. If construction needs transcript archaeology to recover a load-bearing fact, the workpiece is incomplete at that point. diff --git a/apps/brunch-agent/src/tools/petrinaut-construction.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts similarity index 95% rename from apps/brunch-agent/src/tools/petrinaut-construction.ts rename to libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts index 87a6931bbda..9f45f05ad1f 100644 --- a/apps/brunch-agent/src/tools/petrinaut-construction.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/petrinaut-construction.ts @@ -1,12 +1,9 @@ import { defineTool } from "@flue/runtime"; import * as v from "valibot"; +import { AWAITING_CLIENT } from "@hashintel/brunch-agent/client-tools"; 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", diff --git a/apps/brunch-agent/src/tools/read-petrinaut-doc.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts similarity index 91% rename from apps/brunch-agent/src/tools/read-petrinaut-doc.ts rename to libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts index 7b5bf99f7f4..68dc902502b 100644 --- a/apps/brunch-agent/src/tools/read-petrinaut-doc.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/src/tools/read-petrinaut-doc.ts @@ -1,13 +1,12 @@ import { defineTool } from "@flue/runtime"; import * as v from "valibot"; +import { AWAITING_CLIENT } from "@hashintel/brunch-agent/client-tools"; import { petrinautDocNames, readPetrinautDocToolName, } from "@hashintel/petrinaut-core/ai"; -import { AWAITING_CLIENT } from "../client-tool.ts"; - export const READ_PETRINAUT_DOC_TOOL_NAME = readPetrinautDocToolName; export const readPetrinautDoc = defineTool({ diff --git a/apps/brunch-agent/test/petrinaut-construction-tools.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/construction-tools.test.ts similarity index 100% rename from apps/brunch-agent/test/petrinaut-construction-tools.test.ts rename to libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/construction-tools.test.ts diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/plugin.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/plugin.test.ts deleted file mode 100644 index 7a345a2ac20..00000000000 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/plugin.test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import * as v from "valibot"; -import { describe, expect, test } from "vitest"; - -import { - captureDedupKey, - completionDemands, - createSweepExtractionResultSchema, - evaluateCompletion, - foldElicitedModel, - type CaptureEnvelope, - type JsonValue, - type SlotAssertion, -} from "@hashintel/brunch-agent"; - -import { sdcpn, sdcpnDefinition } from "../src/index"; - -const proposalOf = (assertion: SlotAssertion) => ({ - evidence: [{ excerpt: "quote" }], - epistemicStatus: "explicit" as const, - confidence: "firm" as const, - content: { value: assertion }, -}); - -const asserted = ( - kind: string, - node: string, - slot: string, - precision: SlotAssertion["precision"], - value: JsonValue, -): SlotAssertion => ({ - type: "slot-asserted", - kind, - node, - slot, - precision, - assertion: { value }, -}); - -const notApplicable = ( - kind: string, - node: string, - slot: string, -): SlotAssertion => ({ - type: "slot-asserted", - kind, - node, - slot, - assertion: { absence: "not-applicable" }, -}); - -let entry = 0; -const capture = (assertion: SlotAssertion): CaptureEnvelope => { - entry += 1; - const fields = { - confidence: "firm" as const, - content: { value: assertion as unknown as JsonValue }, - evidence: [ - { - excerpt: `quote ${entry}`, - pointer: { sessionId: "s", entryStart: entry, entryEnd: entry }, - source: "user" as const, - }, - ], - epistemicStatus: "explicit" as const, - }; - return { ...fields, id: `c-${entry}`, dedupKey: captureDedupKey(fields) }; -}; - -describe("the SDCPN plugin", () => { - test("is the parsed file plus one slot-assertion proposal type", () => { - expect(sdcpn.targetFormalism).toBe("sdcpn"); - expect(sdcpn.definition).toBe(sdcpnDefinition); - expect(sdcpnDefinition.version).toBe("sdcpn/2026-08-26.2"); - expect(sdcpn.proposalCatalog.map((proposal) => proposal.name)).toEqual([ - "slot-asserted", - ]); - }); - - test("keeps motif variants specific without repeating generic quantile teaching", () => { - const motifs = new Map( - sdcpnDefinition.guidance.motifs.map((motif) => [motif.name, motif.text]), - ); - expect(motifs.get("shared resource")).toMatch(/indivisible.*splittable/iu); - expect(motifs.get("batch, lot, load")).toMatch(/count.*clock/iu); - expect(motifs.get("threshold on a continuous quantity")).toMatch( - /weakest|combine/iu, - ); - expect( - sdcpnDefinition.guidance.techniques.map((technique) => technique.name), - ).not.toContain("quantiles, never triangles"); - expect( - sdcpnDefinition.guidance.failure_modes.map( - (failureMode) => failureMode.name, - ), - ).not.toContain("overconfident triangle"); - expect( - sdcpnDefinition.mustKnow.find( - (row) => - row.kind === "dynamics" && - row.slot === "how it varies around that change", - )?.precision, - ).toEqual({ kind: "word", word: "spread" }); - }); - - test("does not collect unsupported rationale on every kind", () => { - expect( - sdcpnDefinition.ontology.attributes.map((attribute) => attribute.name), - ).not.toContain("rationale"); - }); - - test("accepts an assertion addressed to a kind and slot the file names", () => { - const schema = createSweepExtractionResultSchema(sdcpn); - const proposal = proposalOf( - asserted("activity", "fill", "how long it takes", "spread", { - typical: 4, - worse1in10: 6, - better1in10: 3, - unit: "minutes", - }), - ); - expect(v.parse(schema, { proposals: [proposal] })).toEqual({ - proposals: [proposal], - }); - }); - - test("refuses kinds and slots the file does not name, and values without a precision", () => { - const schema = createSweepExtractionResultSchema(sdcpn); - const reject = (assertion: SlotAssertion, message: RegExp) => - expect(() => - v.parse(schema, { proposals: [proposalOf(assertion)] }), - ).toThrow(message); - reject(asserted("queue", "q", "how long it takes", "spread", 1), /kind/u); - reject(asserted("activity", "fill", "its colour", "named", "x"), /slot/u); - reject( - { - ...asserted("activity", "fill", "how long it takes", "spread", 1), - precision: undefined, - }, - /precision/u, - ); - }); - - test("folds and completes a minimal model under the file's own rows", () => { - const captures = [ - capture( - asserted( - "objective", - "cycle", - "the question, in the expert's words", - "spelled out", - { - question: "How many items finish per shift?", - }, - ), - ), - capture( - asserted("objective", "cycle", "the nodes it depends on", "named", [ - "entity-type:item", - "entity-type:station", - "activity:fill", - "ordering/flow:main", - ]), - ), - capture( - notApplicable( - "objective", - "cycle", - 'what "better" means, and trade-off weights', - ), - ), - capture( - asserted( - "entity-type", - "item", - "the distinctions the process treats apart", - "spelled out", - ["small", "large"], - ), - ), - capture( - notApplicable( - "entity-type", - "item", - "state that rides along with each instance", - ), - ), - capture( - notApplicable( - "entity-type", - "item", - "how many there are, or the population's shape", - ), - ), - capture( - asserted( - "entity-type", - "station", - "the distinctions the process treats apart", - "spelled out", - ["one station type"], - ), - ), - capture( - notApplicable( - "entity-type", - "station", - "state that rides along with each instance", - ), - ), - capture( - asserted( - "entity-type", - "station", - "how many there are, or the population's shape", - "range", - { - low: 2, - high: 3, - }, - ), - ), - capture( - asserted( - "activity", - "fill", - "what it needs before it can start", - "spelled out", - ["an item", "a free station"], - ), - ), - capture( - asserted( - "activity", - "fill", - "what it produces or changes", - "spelled out", - ["a filled item"], - ), - ), - capture(notApplicable("activity", "fill", "who or what performs it")), - capture( - asserted("activity", "fill", "how long it takes", "spread", { - typical: 4, - worse1in10: 6, - better1in10: 3, - unit: "minutes", - }), - ), - capture( - notApplicable( - "activity", - "fill", - "how often it occurs, if it is an event rather than a step", - ), - ), - capture( - notApplicable( - "activity", - "fill", - "what is lost when it changes the system's mode", - ), - ), - capture( - asserted( - "activity", - "fill", - "whether its quantities vary by type", - "named", - "no", - ), - ), - capture( - asserted( - "ordering/flow", - "main", - "the order things happen in", - "spelled out", - ["fill"], - ), - ), - capture( - notApplicable( - "ordering/flow", - "main", - "how a branch or merge is decided", - ), - ), - ]; - const model = foldElicitedModel( - { captures, issues: [], events: [] }, - sdcpnDefinition, - ); - expect(model.unmapped).toEqual([]); - const report = evaluateCompletion( - model, - completionDemands(sdcpnDefinition), - ); - expect(report.failures).toEqual([]); - expect(report.complete).toBe(true); - - const withoutDuration = captures.filter((c) => c.id !== "c-13"); - const partial = evaluateCompletion( - foldElicitedModel( - { captures: withoutDuration, issues: [], events: [] }, - sdcpnDefinition, - ), - completionDemands(sdcpnDefinition), - ); - expect(partial.complete).toBe(false); - expect( - partial.failures.map((f) => [f.diagnostic, f.nodeId, f.slot]), - ).toEqual([["unaddressed", "activity:fill", "how long it takes"]]); - }); -}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/sdcpn-modelling-skill.test.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/sdcpn-modelling-skill.test.ts new file mode 100644 index 00000000000..f90ab1b2f9f --- /dev/null +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/test/sdcpn-modelling-skill.test.ts @@ -0,0 +1,73 @@ +import { readFileSync } from "node:fs"; + +import { describe, expect, test } from "vitest"; + +import { sdcpnModellingSkill } from "../src/skills/sdcpn-modelling/skill"; + +const skillDirectory = new URL( + "../src/skills/sdcpn-modelling/", + import.meta.url, +); +const readSkillFile = (fileName: string): string => + readFileSync(new URL(fileName, skillDirectory), "utf8"); + +describe("the authored sdcpn-modelling skill directory", () => { + test("is one Flue skill whose packaged paths equal the authored paths", () => { + expect(sdcpnModellingSkill.name).toBe("sdcpn-modelling"); + expect(sdcpnModellingSkill.description).toContain("process model"); + expect(Object.keys(sdcpnModellingSkill.files ?? {}).sort()).toEqual([ + "references/checks.md", + "references/pn-construction.md", + "references/profile.md", + "templates/workpiece.md", + ]); + for (const path of Object.keys(sdcpnModellingSkill.files ?? {})) { + expect(sdcpnModellingSkill.files?.[path]).toBe(readSkillFile(path)); + } + expect(sdcpnModellingSkill.instructions).not.toMatch(/^---/u); + expect(sdcpnModellingSkill.instructions).toContain( + "# Capability-aware lifecycle", + ); + }); + + test("routes universal judgment to core's elicitation skill instead of packaging it", () => { + const instructions = sdcpnModellingSkill.instructions; + expect(instructions).toContain("Activate the `elicitation` skill"); + expect(Object.keys(sdcpnModellingSkill.files ?? {})).not.toContain( + "references/universal-elicitation.md", + ); + for (const referenced of instructions.matchAll( + /`((?:references|templates)\/[\w-]+\.md)`/gu, + )) { + expect(sdcpnModellingSkill.files).toHaveProperty(referenced[1]!); + } + }); + + test("keeps reusable teaching free of scenario nouns and target vocabulary leaks", () => { + const profile = readSkillFile("references/profile.md"); + const workpiece = readSkillFile("templates/workpiece.md"); + const construction = readSkillFile("references/pn-construction.md"); + const checks = readSkillFile("references/checks.md"); + expect(profile).not.toMatch(/Vestera|truck fleet|semiconductor/iu); + expect(workpiece).toContain( + "Every operational claim has one authoritative home", + ); + expect(checks).toContain("Tool-schema acceptance"); + expect(checks).toContain("Agent-reviewed structural correspondence"); + expect(checks).toContain("Behavioral execution or stronger analysis"); + expect(construction).toContain("getLatestNetDefinition"); + expect(construction).not.toContain("```json"); + expect(construction).not.toContain("```pn-json"); + }); + + test("the always-on append routes to the job skill and stays compact", () => { + const append = readFileSync( + new URL("../src/prompts/APPEND_SYSTEM.md", import.meta.url), + "utf8", + ); + expect(append).toContain("Activate the `sdcpn-modelling` skill"); + expect(append).not.toContain("references/"); + expect(append).not.toContain("templates/"); + expect(append.split(/\s+/u).length).toBeLessThan(300); + }); +}); diff --git a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/vite.config.ts b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/vite.config.ts index 62daeca5b55..059795dcdc1 100644 --- a/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/vite.config.ts +++ b/libs/@hashintel/brunch-agent/packages/plugin-sdcpn/vite.config.ts @@ -7,12 +7,20 @@ const packageRoot = fileURLToPath(new URL(".", import.meta.url)); export default defineConfig({ build: { lib: { - entry: fileURLToPath(new URL("src/index.ts", import.meta.url)), - fileName: "index", + entry: { + flue: fileURLToPath(new URL("src/flue.ts", import.meta.url)), + index: fileURLToPath(new URL("src/index.ts", import.meta.url)), + }, + fileName: (_format, entryName) => `${entryName}.js`, formats: ["es"], }, rolldownOptions: { - external: [/^@hashintel\/brunch-agent(?:\/.*)?$/u, "valibot"], + external: [ + /^@flue\/runtime(?:\/.*)?$/u, + /^@hashintel\/brunch-agent(?:\/.*)?$/u, + /^@hashintel\/petrinaut-core(?:\/.*)?$/u, + "valibot", + ], }, sourcemap: true, }, diff --git a/yarn.lock b/yarn.lock index 44d722ef213..b1e7b541220 100644 --- a/yarn.lock +++ b/yarn.lock @@ -434,13 +434,17 @@ __metadata: version: 0.0.0-use.local resolution: "@apps/brunch-agent@workspace:apps/brunch-agent" dependencies: + "@anthropic-ai/sdk": "npm:0.74.0" "@earendil-works/pi-ai": "npm:0.83.0" + "@earendil-works/pi-tui": "npm:0.84.3" "@flue/opentelemetry": "npm:2.0.3" "@flue/react": "npm:2.0.3" "@flue/runtime": "npm:2.0.3" "@flue/sdk": "npm:2.0.3" "@flue/vite": "npm:2.0.3" + "@hashintel/brunch-agent": "workspace:*" "@hashintel/brunch-agent-binding-flue": "workspace:*" + "@hashintel/brunch-agent-plugin-sdcpn": "workspace:*" "@hashintel/brunch-agent-transport-aisdk": "workspace:*" "@hashintel/petrinaut-core": "workspace:*" "@opentelemetry/api": "npm:1.9.1" @@ -454,6 +458,7 @@ __metadata: oxlint-tsgolint: "npm:0.22.1" react: "npm:19.2.6" react-dom: "npm:19.2.6" + typebox: "npm:1.3.7" valibot: "npm:1.4.2" vite: "npm:8.1.0" vitest: "npm:4.1.10" @@ -5133,6 +5138,16 @@ __metadata: languageName: node linkType: hard +"@earendil-works/pi-tui@npm:0.84.3": + version: 0.84.3 + resolution: "@earendil-works/pi-tui@npm:0.84.3" + dependencies: + get-east-asian-width: "npm:1.6.0" + marked: "npm:18.0.5" + checksum: 10c0/be2d5d5ba278a929c1ab7c77ce3a5b39607f2f4bf40bb7aeff5775c319ba38571d653ab3503b74468c4fe5d9ffb3b5b5c1d92e0592b07cd16b38dd9047735217 + languageName: node + linkType: hard + "@effect/cluster@npm:0.50.6": version: 0.50.6 resolution: "@effect/cluster@npm:0.50.6" @@ -7398,7 +7413,21 @@ __metadata: "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" oxlint: "npm:1.63.0" oxlint-tsgolint: "npm:0.22.1" - valibot: "npm:1.4.2" + vite: "npm:8.1.0" + vitest: "npm:4.1.10" + languageName: unknown + linkType: soft + +"@hashintel/brunch-agent-plugin-dafny@workspace:libs/@hashintel/brunch-agent/packages/plugin-dafny": + version: 0.0.0-use.local + resolution: "@hashintel/brunch-agent-plugin-dafny@workspace:libs/@hashintel/brunch-agent/packages/plugin-dafny" + dependencies: + "@flue/runtime": "npm:2.0.3" + "@hashintel/brunch-agent": "workspace:*" + "@types/node": "npm:22.18.13" + "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" + oxlint: "npm:1.63.0" + oxlint-tsgolint: "npm:0.22.1" vite: "npm:8.1.0" vitest: "npm:4.1.10" languageName: unknown @@ -7408,22 +7437,24 @@ __metadata: version: 0.0.0-use.local resolution: "@hashintel/brunch-agent-plugin-gherkin@workspace:libs/@hashintel/brunch-agent/packages/plugin-gherkin" dependencies: + "@flue/runtime": "npm:2.0.3" "@hashintel/brunch-agent": "workspace:*" "@types/node": "npm:22.18.13" "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" oxlint: "npm:1.63.0" oxlint-tsgolint: "npm:0.22.1" - valibot: "npm:1.4.2" vite: "npm:8.1.0" vitest: "npm:4.1.10" languageName: unknown linkType: soft -"@hashintel/brunch-agent-plugin-sdcpn@workspace:libs/@hashintel/brunch-agent/packages/plugin-sdcpn": +"@hashintel/brunch-agent-plugin-sdcpn@workspace:*, @hashintel/brunch-agent-plugin-sdcpn@workspace:libs/@hashintel/brunch-agent/packages/plugin-sdcpn": version: 0.0.0-use.local resolution: "@hashintel/brunch-agent-plugin-sdcpn@workspace:libs/@hashintel/brunch-agent/packages/plugin-sdcpn" dependencies: + "@flue/runtime": "npm:2.0.3" "@hashintel/brunch-agent": "workspace:*" + "@hashintel/petrinaut-core": "workspace:*" "@types/node": "npm:22.18.13" "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" oxlint: "npm:1.63.0" @@ -7454,16 +7485,15 @@ __metadata: resolution: "@hashintel/brunch-agent@workspace:libs/@hashintel/brunch-agent/packages/core" dependencies: "@anthropic-ai/sdk": "npm:0.74.0" + "@flue/runtime": "npm:2.0.3" "@types/node": "npm:22.18.13" "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" - "@valibot/to-json-schema": "npm:1.7.1" fast-check: "npm:4.9.0" oxlint: "npm:1.63.0" oxlint-tsgolint: "npm:0.22.1" valibot: "npm:1.4.2" vite: "npm:8.1.0" vitest: "npm:4.1.10" - yaml: "npm:2.9.0" languageName: unknown linkType: soft @@ -20113,7 +20143,7 @@ __metadata: languageName: node linkType: hard -"@valibot/to-json-schema@npm:1.7.1, @valibot/to-json-schema@npm:^1.3.0": +"@valibot/to-json-schema@npm:^1.3.0": version: 1.7.1 resolution: "@valibot/to-json-schema@npm:1.7.1" peerDependencies: @@ -29307,10 +29337,10 @@ __metadata: languageName: node linkType: hard -"get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.3.1, get-east-asian-width@npm:^1.5.0": - version: 1.5.0 - resolution: "get-east-asian-width@npm:1.5.0" - checksum: 10c0/bff8bbc8d81790b9477f7aa55b1806b9f082a8dc1359fff7bd8b96939622c86b729685afc2bfeb22def1fc6ef1e5228e4d87dd4e6da60bc43a5edfb03c4ee167 +"get-east-asian-width@npm:1.6.0, get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.3.1, get-east-asian-width@npm:^1.5.0": + version: 1.6.0 + resolution: "get-east-asian-width@npm:1.6.0" + checksum: 10c0/7e72e9550fd49ca5b246f9af6bb2afc129c96412845ff6556b3274fd44817a381702ca17028efe9866b261a3d44254cbf21e6c90cf05b4b61675630af776d431 languageName: node linkType: hard @@ -34402,6 +34432,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:18.0.5": + version: 18.0.5 + resolution: "marked@npm:18.0.5" + bin: + marked: bin/marked.js + checksum: 10c0/22763935a0a243c41851b010a086ea85a09951e379948b6aa629b6cecdcd66e3a5e8c4acac2f6b29c183c20609d3e5102495270207dfd5f4c46ddd773a4ddb9b + languageName: node + linkType: hard + "marked@npm:4.3.0": version: 4.3.0 resolution: "marked@npm:4.3.0"