practice: resolveTargetId + ids-first context marker (round 9 slice 1)

The mastery-trace contract: problem targets must be traceable to
propositions. resolveTargetId (pure, chains.ts) resolves a target
string to a canonical node id — id exact first, then name
case-insensitive — against the WHOLE graph (pickless sessions and
future graph-tools poses stay valid). The graph-context marker now
introduces each pick as [id] name: statement (ids were never shown to
the model before — names only); chains stay names: comprehension
context, not tags.
This commit is contained in:
amosblomqvist
2026-10-05 06:26:21 +03:00
parent 1cac08117a
commit d47b3564ce
4 changed files with 37 additions and 7 deletions
+7 -2
View File
@@ -203,11 +203,16 @@ export interface GraphContextPick {
* feeds the transcript's collapsible GraphContextCard. No more seed
* text glued into the first prompt. */
export function newGraphContextMessage(picks: readonly GraphContextPick[], chains: readonly string[]): LhMarkerMessage {
// IDS FIRST (owner round 9, 2026-10-05 — the mastery-trace contract):
// each proposition is introduced ONCE with its id, name, and statement;
// the problem tool's `target` field takes these ids (names normalize to
// ids at the seam). Chains below stay NAMES — they are comprehension
// context, not tags; names read, ids reference.
const lines = [
"Contextualization — the learner picked these propositions from their knowledge map when starting this session:",
"",
"Propositions:",
...picks.map((p) => `- ${p.name}: ${p.statement}`),
"Propositions (id — name: statement; the problem tool's target field uses these ids):",
...picks.map((p) => `- [${p.id}] ${p.name}: ${p.statement}`),
];
if (chains.length > 0) {
lines.push("", "Reasoning chains among them (premises → conclusion):", ...chains.map((c) => `- ${c}`));