experiment: audit provenance in update prompt

This commit is contained in:
2026-08-07 14:07:28 +01:00
parent ec167f2689
commit 90472de766
2 changed files with 152 additions and 5 deletions
+146 -2
View File
@@ -5285,9 +5285,9 @@ b) At `applyGraphUpdate` calls (lines 2882, 2912): only `graphSnapshot` and `pro
### Experiment Conclusion
**Update origin is partially knowable but already merged before application.**
**Input origin remains explicit before application, but per-node supplied-versus-inferred provenance is not represented in the validated proposal and is lost before durable graph mutation.**
The raw user answer and validated model proposal are explicitly separate at the `applyValidatedProposal` function call boundary (orchestrator.js:683-688). This means deterministic code CAN identify "which argument is the user answer" and "which argument is the model proposal." However, this distinction does not translate to per-node provenance because:
The raw user answer and validated model proposal are explicitly separate at the `applyValidatedProposal` function call boundary (orchestrator.js:683-688). Whole-input origin is explicit: `answer` = user supplied; `proposal` = model produced. However, per-node origin inside the proposal is not deterministically recoverable from the validated proposal alone. This distinction does not translate to per-node provenance because:
1. The graphUpdateSchema / situationNodeSchema has no provenance field on nodes or edges.
2. The raw user answer text has no structural mapping to proposal node boundaries — the LLM consumes the answer as context and generates additions independently, so there is no way to deterministically say "this node contains user information" versus "this node contains model inference."
@@ -5319,3 +5319,147 @@ Provenance loss occurs at the intersection of proposal schema (no provenance fie
No test run required; Experiment 54C is a source-trace audit.
## Experiment 54D — Does the Update Prompt Already Preserve User-vs-Model Origin? (2026-08-07)
### Objective
Inspect the production graph-update prompt to determine whether it marks which content is the user's answer versus model-generated interpretation strongly enough that provenance could, in principle, be preserved downstream. This is a source-inspection experiment only. Do not implement provenance.
Two distinct questions:
- **Prompt-level source identity:** Can the model tell "this text is the user's answer"?
- **Proposal-level provenance:** Can downstream code tell "this particular proposed node directly represents supplied content rather than model inference"?
These may have different answers. The first may be explicit while the second is absent.
### Hypothesis
The existing update prompt may already contain clearly separated sections such as: previous graph/context; current question; user answer; instructions for graph changes. If that structure is explicit, the upstream information needed to distinguish user-supplied input from model-generated additions may already exist at prompt time. If the prompt blends everything into undifferentiated text, provenance is weaker even before proposal parsing.
### Files Actually Inspected
- `lib/graph/orchestrator.js` — lines 617-638 (caller passing answer to buildPrompt); line 20 (import statement)
- `lib/graph/prompt-builder.js` — full file (buildGraphUpdatePrompt function and its helpers: formatEnumValues, formatGraph, formatExampleAnswerBlock)
Production files inspected: `lib/graph/orchestrator.js`, `lib/graph/prompt-builder.js`.
### Prompt Builder Function Inspected
Function: `buildGraphUpdatePrompt` in `lib/graph/prompt-builder.js`, exported as `buildGraphUpdatePrompt` (line 26), aliased as `buildUpdatePrompt` (line 134).
### Arguments Passed Into Prompt Builder
- `situationGraph` — full current SituationGraph object
- `previousQuestion` — string (the previously selected question)
- `answer` — string (raw user answer, min 1 char, max 5000 chars per updateCaseRequestSchema)
- `promptVersion` — string, defaults to "v0.4"
The caller in orchestrator.js:633 passes these four arguments directly from function parameters and a config value. No provenance metadata is constructed or passed at the call site.
### User Answer Source Identity in Prompt
Status: **explicit**
Section `## User Answer` (line 48-49 of prompt-builder.js) contains the raw user answer as its entire content, separated by a Markdown header from everything above and below. The section header unambiguously identifies the block as user-supplied text. No other section contains this exact string.
### Previous Question Separation in Prompt
Status: **explicit**
Section `## Previous Selected Question` (line 45-46) contains only the previous question string, clearly separated by a Markdown header from both the graph above and the answer below.
### Graph/Context Separation in Prompt
Status: **explicit**
Section `## Current Situation Graph` (line 42-43) contains the full situation graph as formatted JSON, clearly separated by a Markdown header from all other content.
### Instruction Versus User-Content Separation
Status: **explicit**
All instruction blocks use Markdown headers (`## Proposal Rules`, `## Allowed Node Kinds`, `## Additional Guidance`, etc.). These headers create visual and structural boundaries between user-provided sections (graph, question, answer) and system instructions. The prompt does not interleave instructions within user-content blocks.
### Does Prompt Explicitly Identify User-Supplied Content
Status: **explicit**
Yes. The `## User Answer` header unambiguously marks which text block is the user's contribution. Additionally, Proposal Rule 9 states "Every new unknown must be directly traceable to the user's answer," and Rule 13a references "the relevant answer-derived decision or context node" — both rules reinforce that the answer section represents the authoritative user-supplied source.
### Does Prompt Explicitly Distinguish Supplied Meaning from Model Inference
Status: **implicit**
The prompt does not contain an explicit instruction telling the model to label or separate supplied meaning from inference in its output. However, implicit cues exist: Rule 9 requires traceability ("directly traceable to the user's answer"), Rule 9a requires a why-it-matters clause for new unknowns (implying the model must reason about what it derives versus what is given), and Rule 13a references "answer-derived" nodes. These create an expectation that the model should distinguish derived from supplied content, but there is no structural output mechanism to preserve that distinction in the JSON proposal.
### Does Requested Proposal Output Contain Provenance
Status: **absent**
The `graphUpdateSchema` (defined in `lib/graph/schema.js`, lines 155-163) has no provenance or source fields on any of its node or edge schemas. The `addedNodes` schema uses `situationNodeSchema` which contains only structural fields (id, label, description, kind, status, confidence, value, unit, evidenceIds, dependsOn, affects, parentId, childIds). No field exists to tag content as "user-supplied," "model-inferred," or any equivalent origin marker.
### Prompt-Level Source Identity Status
**explicit** — The model can clearly identify which input text came from the user (the `## User Answer` section) and which sections contain context/instructions (the graph JSON, previous question, rules, guidance).
### Proposal-Level Provenance Status
**absent** — The proposed output schema has no provenance fields. Even if the model understands which inputs were user-supplied, it has no mechanism to annotate its output nodes/edges with origin information.
### Trace from User Answer to Validated Proposal
| Stage | Source Identity | Supplied-vs-Inferred Meaning Explicit? |
|-------|----------------|----------------------------------------|
| 1. `answer` parameter in orchestrator.js:636 | explicit (parameter name) | N/A — raw string |
| 2. `## User Answer` section in prompt (prompt-builder.js:49) | explicit (named Markdown section) | implicit — the answer is given; no instruction distinguishes parts of it as supplied vs inferred |
| 3. LLM processes prompt and generates proposal | explicit (model can see which text is user answer) | implicit — rules require traceability but do not provide output mechanism for provenance |
| 4. `parsedProposal.proposal` after parseGraphUpdateProposal | absent (no source metadata on nodes/edges) | absent — graphUpdateSchema has no provenance fields |
### First Point Where Per-Node Provenance Becomes Unavailable
The proposed output schema (`graphUpdateSchema` in `lib/graph/schema.js`) defines the JSON contract returned by the LLM. Since none of its node or edge schemas include any origin/provenance field, per-node provenance is unavailable at the **output definition** stage — i.e., the prompt's own requested format cannot carry provenance even if the model understands it internally. This is upstream of parsing and validation; even before `parseGraphUpdateProposal` runs, the schema itself forbids provenance encoding.
### Could the Model Know Which Input Came from the User
**Yes.** The `## User Answer` section makes the user's contribution unmistakably identifiable. Rules 9 and 13a further reinforce the distinction between answer-derived content and model-generated additions.
### Could Downstream Deterministic Code Know Which Proposed Node Came from Supplied Meaning
**No.** The `graphUpdateSchema` has no provenance field on addedNodes, updatedNodes, or addedEdges. The validated proposal is a plain JSON object with no origin metadata. There is no deterministic mechanism to recover per-node provenance from the proposal alone.
### Experiment Conclusion
**Prompt clearly preserves user-source identity but proposal schema loses per-node provenance.**
The production update prompt (prompt-builder.js) already separates the user answer into a distinct named section (`## User Answer`) with clear visual and structural boundaries from graph context, instructions, and constraints. The model can unambiguously identify which text is user-supplied. Rules 9 and 13a reinforce traceability expectations.
However, the requested output schema (graphUpdateSchema in lib/graph/schema.js) has no provenance fields on nodes or edges. Even if the model internally distinguishes derived from supplied content, the JSON output contract cannot encode that distinction. Provenance is lost at the output-definition stage — before any parsing or validation occurs.
This means:
- Prompt-level source identity: explicit
- Proposal-level provenance: absent (structural limitation of schema)
- The gap is not a prompt-design problem; it is a schema-deficiency problem
### Limitations
- Source-inspection audit only; no live model call executed
- Inspected the production prompt-builder and its immediate caller only
- Did not inspect whether evidenceType in reconstruction can carry origin information for the answer field itself
- Did not evaluate whether a schema change would be sufficient or whether additional upstream markers are needed
- Conclusions apply to the current prompt version (v0.4); earlier versions may differ
### Status
**Pending Rob's review.** Source-inspection complete. No production code changed. Working tree clean before commit.
### Production Unchanged
- `lib/graph/orchestrator.js`: 0 lines changed
- `lib/graph/prompt-builder.js`: 0 lines changed
- `lib/graph/schema.js`: 0 lines changed
- No production files modified
- Working tree clean before commit
### Tests / Validation Run
No test run required; Experiment 54D is a prompt-source audit.