practice: the agent's data drawer — full transparency on every conclude card (round 17)

Owner ask (2026-10-06): reveal everything the agent filled in when
writing the problem and concluding it, at the very bottom of the
conclude card, collapsible, arriving collapsed.

The fields, both calls:
- problem: mode (recall/discrimination/composition), target
  proposition ids, statement, modelSolution (the last two ARE the
  visible card — the drawer shows the metadata only)
- conclude: outcome, hint rung (R0–R5/null), miss class
  (cue/discrimination/execution/careless/structural/null), retest.

Seams: problemInputForConclude (native-messages) walks the reveal's
walk and folds the WHOLE problem-call input onto the conclude part
(projectView join; same refused-pose law — an unacked pose folds
nothing); ArtifactPartProps grows problemInput. Targets resolve to
proposition NAMES at render (one lazy module-cached /api/graph fetch;
failure degrades to raw ids — observability never breaks the card).
The drawer: divider + chevron 'AGENT'S DATA' toggle (mono-caps
eyebrow register), definition rows with mono values, 'untagged'/
'none' honest empties, absent entirely when the calls filled nothing
(the dismissal line, legacy bare records).
This commit is contained in:
amosblomqvist
2026-10-06 04:18:26 +03:00
parent 0af3514d9b
commit 793638d6ce
6 changed files with 172 additions and 3 deletions
+4
View File
@@ -298,6 +298,10 @@ export interface ArtifactPartProps {
toolCallId: string;
input?: unknown;
output?: unknown;
/** The pose-side fold (owner round 17): the whole problem-call
* arguments joined onto the conclude part by projectView — the
* observability drawer's data. */
problemInput?: unknown;
/** The view-seam join for conclude cards (owner round, 2026-10-05):
* the modelSolution committed at POSE time, folded onto the part by
* projectView — the reveal is never re-authored. */
+17 -2
View File
@@ -364,6 +364,20 @@ export function modelSolutionForConclude(
messages: readonly LhMessage[],
concludeCallId: string,
): string | undefined {
const input = problemInputForConclude(messages, concludeCallId);
return typeof input?.modelSolution === "string" ? input.modelSolution : undefined;
}
/** The full pose-side fold (owner round 17, 2026-10-06 — observability):
* EVERYTHING the agent filled into the problem call — mode, target ids,
* statement, the committed solution — joined onto the conclude part so
* the card's collapsible "agent's data" drawer renders pure. Same walk
* and refusal law as the reveal: a pose the record never acked (a
* refused call) folds nothing. */
export function problemInputForConclude(
messages: readonly LhMessage[],
concludeCallId: string,
): { statement?: unknown; mode?: unknown; target?: unknown; modelSolution?: unknown } | undefined {
let mi = messages.findIndex(
(m) =>
(m as { role?: string }).role === "assistant" &&
@@ -381,8 +395,9 @@ export function modelSolutionForConclude(
const block = assistant.content[bi]!;
if (block.type !== "toolCall" || block.name !== PRACTICE_PROBLEM_TOOL) continue;
if (!clean.has(block.id)) continue; // a refused pose revealed nothing
const solution = (block.arguments as { modelSolution?: unknown } | undefined)?.modelSolution;
return typeof solution === "string" ? solution : undefined;
return (block.arguments ?? undefined) as
| { statement?: unknown; mode?: unknown; target?: unknown; modelSolution?: unknown }
| undefined;
}
}
return undefined;
+2 -1
View File
@@ -34,7 +34,7 @@
import type { AssistantMessage, ToolResultMessage } from "@earendil-works/pi-ai";
import type { LhMarkerMessage, LhMessage } from "./native-messages.ts";
import { modelSolutionForConclude, PRACTICE_CONCLUDE_TOOL } from "./native-messages.ts";
import { modelSolutionForConclude, PRACTICE_CONCLUDE_TOOL, problemInputForConclude } from "./native-messages.ts";
import { activeCompactionOf, type CompactionDetails } from "./compaction.ts";
import { QUESTION_TOOLS } from "./pending.ts";
import type { SubagentArtifactData, SubagentResultMeta } from "./contracts/jobs.ts";
@@ -223,6 +223,7 @@ export function projectView(messages: readonly LhMessage[], live?: LiveTurnState
// time — a record fold, enriched here so the card renders pure.
if (block.name === PRACTICE_CONCLUDE_TOOL) {
part.reveal = modelSolutionForConclude(messages, block.id);
part.problemInput = problemInputForConclude(messages, block.id);
}
rest.push(part);
}