Debugging Intent
Part V β Debugging AI-Assisted Development and Research
The output is wrong β or the intent was never written
Chapter 24 routed defective work products by role. Most routes converge here: the builder built the wrong thing correctly because the intent was a sentence where a specification should have been. The debugging target shifts from the artifact to the artifact’s missing contract.
OBSERVATION: a prompt reading “add pagination to the user list” returns working code that the requester rejects β wrong page size, wrong sort, wrong error shape. The code runs; the intent was never specified. HYPOTHESIS H1 (underspecified intent): the prompt admits β₯3 compliant implementations, and the model picked one the requester did not want. H2 (model capability failure): no reasonable reading of the prompt produces the rejected behavior. INFERENCE: none yet β H1 and H2 are separable only by an intent artifact written before regeneration. Blaming the model before pinning intent is single-run inference.
This chapter’s question: when the output disappoints, is the defect in the generation β or in the intent it was generated from?
Why “write a better prompt” fails first
The obvious move β rewording the prompt until the output looks right β fails because it debugs the symptom (this output) instead of the cause (unpinned intent). Four defects hide behind prompt-tweaking:
- Unfalsifiable wishes. “Make it robust,” “handle edge cases,” “follow best practices” admit any implementation. No output can fail a criterion that was never written. Tamkin and colleagues framed this as task ambiguity: when a specification is consistent with several tasks, the model must infer which one you meant, and by default it picks one β not necessarily yours β with clarification or fine-tuning the main levers that help (Tamkin et al., 2023).
- Moving oracle. Each re-prompt teaches the requester what they wanted only after seeing what they got. The target moves per iteration; convergence is theater.
- Multi-variable edits. Rewording, adding examples, and changing the seed in one re-prompt confounds intent-clarity with sampling luck and context effects.
- Post-hoc acceptance. The final output is declared correct because fatigue set in, not because criteria were met. Nothing falsifiable survives for regression.
OPINION: a prompt is a lossy compression of intent. Debugging the decompression without fixing the compression is superstition with a text box.
The mental model: intent is a debuggable artifact with its own first divergence. The divergence is not between expected and observed output β it is between the requester’s intent and the written specification. Close that gap first; generation debugging starts only after intent is pinned.
The method: acceptance-criteria-first, then the intent-diff
Write the contract before regenerating anything. The Intent Contract Worksheet pins:
- Goal (one sentence, verifiable): what changes in the world when this is done.
- Acceptance criteria (checkable predicates): page size default 20 with
?per_pageoverride; sortcreated_at desc; error shape{"error": {"code", "message"}}; p99 budget named. - Non-goals and constraints: what the artifact must NOT do; load/latency/compat bounds it must respect.
- Examples (input β expected output pairs, β₯2 including one boundary).
Then run the intent-diff: hold the output fixed, diff it against the freshly written contract, and classify every mismatch as intent-gap (criterion missing from the original prompt) or generation-gap (criterion present in the original prompt but violated).
flowchart TD
REJ["output rejected"] --> C["write the intent contract: goal + >=4 checkable predicates + non-goals + >=2 examples"]
C --> FR["freeze the rejected output; evaluate each predicate against it"]
FR --> P{"per failing predicate: was it in the original prompt?"}
P -->|no| IG["intent-gap (H1): a prompt defect β the model chose one compliant reading"]
P -->|"yes, but violated"| GG["generation-gap (H2): a model defect"]
IG --> RG["regenerate once with the contract pasted verbatim, one variable, >=3 samples"]
GG --> RG
RG --> D{"all 3 pass the mechanical predicates?"}
D -->|yes| H1["H1 supported for this instance β pin predicates as regression assertions"]
D -->|"still disagree behaviourally"| AMB["contract still ambiguous β clarify, do not reword"]
INTENT-DIFF (output frozen; only the contract moves):
criterion | in original prompt? | output satisfies? | verdict
default per_page=20 | NO | NO | intent-gap (H1)
sort created_at desc | YES | NO | generation-gap (H2)
error shape {code,message} | NO | NO | intent-gap (H1)
p99 < 200ms | NO | UNKNOWN (unmeasured) | intent-gap + missing evidence
RULE: every intent-gap row is a prompt defect, not a model defect. Regenerate
only after the contract is pinned; change intent-clarity alone first.
OBSERVATION (constructed illustration, not a measured run): of six rejected behaviors in the pagination case, five traced to criteria absent from the original prompt (intent-gaps) and one to a violated stated criterion (sort order β generation-gap). UPDATED BELIEF: H1 supported as the dominant cause for this instance; H2 live for the single sort-order row only. Re-prompting without the contract would have relitigated five intent rows as model failures.
Example: the pagination prompt, debugged as intent
Original prompt (verbatim): “Add pagination to the user list endpoint. Make it standard.”
Intent contract, written after rejection but before regeneration:
# intent contract: acceptance predicates checked mechanically, not by reading
CONTRACT = {
"goal": "paginated GET /users with stable ordering under concurrent inserts",
"acceptance": [
"default per_page=20, override via ?per_page in 1..100",
"sort created_at desc, tie-break id desc (keyset-stable)",
"error shape {'error': {'code': str, 'message': str}} on 400/422",
"page beyond end returns [] with 200, not 404",
],
"non_goals": ["no cursor-token redesign this iteration"],
"constraints": ["p99 < 200ms at 100 rps against seed dataset D (hash ___)"],
}
# intent-diff procedure: freeze rejected output O1; evaluate each predicate on O1;
# label each failure intent-gap (predicate absent from original prompt) vs
# generation-gap (predicate present but violated). Regenerate once, changing only
# the prompt's criteria coverage; seed + model + context held fixed.
Running the diff on the frozen rejected output: default-size, error-shape, and empty-page rows are intent-gaps (the word “standard” specified none of them); only the sort row is a generation-gap. The intervention is therefore contract-first regeneration: re-issue the same task with the contract pasted verbatim, one variable changed, β₯3 sampled generations (sampling discipline) to separate intent-clarity from luck. If all three now pass the mechanical predicates, H1 is supported for this instance β not proved universally.
No confidence score, single approving re-read, or downstream “looks standard to me” substitutes for predicate evaluation. Agreement with the new output is not diagnosis; predicate passage across repeated trials is evidence.
Research lineage: underspecification makes many wrong answers look right
“Admits β₯3 compliant implementations” is underspecification, and it is a known trap. D’Amour and colleagues studied underspecification in machine-learning pipelines β the property that many distinct solutions score equally well on the stated objective yet behave very differently on things the objective did not pin down β and showed those solutions get treated as interchangeable when they are not (D’Amour et al., 2022). A vague prompt is an underspecified objective; the intent-diff is the step that surfaces which of the many compliant behaviors you actually wanted, before the model’s arbitrary choice is mistaken for its capability.
Executable acceptance predicates are the tested form of the contract. The idea of pairing generation with a test set that filters candidates β CodeT and related test-driven code-generation work β is the automated version of this chapter’s worksheet: write the predicates as runnable checks, generate several candidates, keep the ones that pass (Chen et al., 2022). The contract’s acceptance list should be executable wherever possible, not prose.
Clarification beats re-prompting. Tamkin and colleagues’ finding that clarification is a primary fix for task ambiguity is the research case against the prompt-tweaking loop: the productive move when H1 is live is to ask (or specify), not to reword and resample. Mu and colleagues’ ClarifyGPT is the measured, code-specific form of that move: it detects an ambiguous requirement by a code-consistency check β generate several candidate solutions, and if they disagree behaviourally, the requirement is ambiguous β then asks targeted clarifying questions before regenerating, lifting GPT-4’s pass@1 on the sanitized MBPP set from about 71% to about 81% (Mu et al., 2024). Note the tie to this chapter’s own procedure: the β₯3 contract-pasted regenerations already produce that candidate set, so if they still disagree behaviourally, the contract is still ambiguous β not merely sampled unluckily.
Lab 25: intent-diff with pre-written predictions (proposed)
PROPOSED, not executed: no author-measured results are reported. The evidence this chapter requires is the reader’s own diff table.
Setup. Take one rejected AI artifact with its original prompt preserved verbatim (or deliberately underspecify one: “make the export fast and clean”). Freeze the output bytes, the prompt bytes, model version, and seed. The contract is the independent variable; output, model, seed, and context are controlled.
Task.
- Before writing the contract, record H1/H2 with distinct FORECASTs: H1 (intent-gap dominant): “β₯50% of rejected behaviors map to criteria absent from the original prompt.” H2 (generation-gap dominant): “a majority map to stated criteria violated in the output.”
- Write the intent contract (goal + β₯4 acceptance predicates + non-goals + constraints + β₯2 inputβoutput examples with one boundary). Evaluate each rejected behavior against it in the intent-diff table.
- Regenerate once with the contract pasted verbatim (single-variable intervention; seed/model/context fixed), sampling β₯3 generations. Record OBSERVATION (predicate pass/fail per generation verbatim) and UPDATED BELIEF per hypothesis. Mixed patterns are UNKNOWN with the next probe named.
| Rejected behavior | In original prompt? | Contract predicate | FORECAST | OBSERVATION | UPDATED BELIEF |
|---|---|---|---|---|---|
| ___ | Y/N | ___ | H1: absent | ___ | H1 live/exonerated |
| ___ | Y/N | ___ | H2: violated | ___ | H2 live/exonerated |
| regen Γ3 | contract pasted | all predicates | pass //___ | ___ ___ ___ | intent convicted/suspended |
Success criterion. A completed intent-diff table plus one contract-first regeneration with per-generation predicate results. A reworded prompt without the contract and diff is explicitly not completion.
Companion tool: Intent Contract Worksheet
What it accepts: the original prompt bytes, the frozen rejected output, the written contract (goal, predicates, non-goals, constraints, examples), and the intent-diff rows plus the contract-first regeneration results. What it performs: it blocks regeneration until the contract holds β₯4 checkable predicates and β₯2 examples, enforces output-freezing during the diff, classifies each row as intent-gap vs. generation-gap vs. UNKNOWN, and requires multi-trial regeneration evidence before an intent conviction. What it can establish: whether the rejection traces to underspecified intent or violated specification β for the examined prompt/output pair only. What it cannot establish: model capability in general, correctness of the contract itself (a wrong contract precisely generated is precisely wrong), or future prompt reliability. It never treats fluency, confidence, single-run passage, or reviewer agreement as diagnosis. How its output changes your next action: intent-gap dominance routes to contract repair + regeneration; generation-gap dominance routes to generation debugging (Chapters 26/29); UNKNOWN routes to evidence collection (working set, context) before any verdict.
Paper form, sufficient for this chapter:
Original prompt (verbatim): ___ Output hash: ___ Model/seed: ___ / ___
Goal: ___ Non-goals: ___ Constraints: ___
P1 ___ [ ] P2 ___ [ ] P3 ___ [ ] P4 ___ [ ] Examples: ___ / ___
INTENT-DIFF: intent-gaps ___ | generation-gaps ___ | UNKNOWN ___
REGEN Γ3 (contract pasted, all else fixed): ___ ___ ___ CONVICTION: H1 / H2 / UNKNOWN
Where a software implementation does not yet exist in the reader’s stack, this record is the tool. Contract before regeneration, always.
Reusable procedure: intent first, generation second
- Freeze the rejection β original prompt verbatim, output bytes, hashes, model/seed.
- Write the contract β goal, predicates, non-goals, constraints, examples β without regenerating.
- Run the intent-diff β every mismatch labeled intent-gap vs. generation-gap.
- Regenerate once, contract-pasted β single variable, β₯3 samples, predicate-scored.
- Pin the contract β surviving predicates become regression assertions on future generations.
Failure modes
- Prompt-tweaking loops. Rewording without a contract. Each iteration tests the requester’s patience, not the model’s capability.
- Criterion smuggling. Adding new requirements mid-review and calling the old output wrong. The output failed an exam written after grading.
- Single-generation verdicts. One good contract-pasted run proves the method. Nondeterministic generation needs repeated trials.
- Contract vagueness. Predicates like “fast” and “clean” surviving into the worksheet. Uncheckable criteria are wishes with checkboxes.
- Gap misattribution. Treating every intent-gap as a model defect (or every generation-gap as a prompt defect). The diff’s labels are the diagnosis; respect them.
- Explanation acceptance. “The model says it understood the requirements” filed as compliance. Verbal assent is not predicate passage.
Limits, per contract: one worksheet convicts one prompt/output pair under one model/seed revision; it does not certify future prompts, does not validate the contract’s own wisdom, and does not diagnose generation internals. UNKNOWN where the original prompt is unrecoverable.
References
- Alex Tamkin, Kunal Handa, Avash Shrestha, and Noah D. Goodman. Task Ambiguity in Humans and Language Models. International Conference on Learning Representations (ICLR), 2023. https://arxiv.org/abs/2212.10711
- Alexander D’Amour, Katherine Heller, Dan Moldovan, et al. Underspecification Presents Challenges for Credibility in Modern Machine Learning. Journal of Machine Learning Research 23(226), 2022, pp. 1β61. https://www.jmlr.org/papers/v23/20-1335.html
- Bei Chen, Fengji Zhang, Anh Nguyen, Daoguang Zan, Zeqi Lin, Jian-Guang Lou, and Weizhu Chen. CodeT: Code Generation with Generated Tests. International Conference on Learning Representations (ICLR), 2023 (arXiv 2022). https://arxiv.org/abs/2207.10397
- Fangwen Mu, Lin Shi, Song Wang, Zhuohao Yu, Binquan Zhang, ChenXue Wang, Shichao Liu, and Qing Wang. ClarifyGPT: A Framework for Enhancing LLM-Based Code Generation via Requirements Clarification. Proceedings of the ACM on Software Engineering 1 (FSE), 2024. https://doi.org/10.1145/3660810
Debugging Checklist
- Original prompt preserved verbatim (not reconstructed from memory)?
- Rejected output frozen with hashes (model version + seed logged)?
- Contract written before any regeneration (goal + β₯4 predicates + examples)?
- Intent-diff table complete (every mismatch labeled gap-type or UNKNOWN)?
- Acceptance predicates written as executable checks where possible (not prose)?
- Single-variable regeneration (contract pasted, all else fixed, β₯3 samples)?
- When H1 is live: clarify/specify rather than reword-and-resample?
- H1/H2 FORECASTs pre-written with distinct predicted row patterns?
- Surviving predicates pinned as regression assertions?
What This Chapter Established
- Intent as a debuggable artifact: acceptance-criteria-first discipline with the intent-diff separating intent-gaps (prompt defects) from generation-gaps (model defects), demonstrated on the constructed pagination case.
- The contract-first regeneration intervention (one variable, multi-trial) with pre-written H1/H2 forecasts; no measured runs claimed.
- Lab 25 as a proposed diff record the reader executes; the Intent Contract Worksheet contract (accepts/performs/can-establish/cannot-establish/next-action).
- What was NOT proved: any general claim about prompt engineering efficacy, any certification that contracted prompts always succeed, or any account of generation internals. One pair convicted; nothing universal.
- Research grounding: an ambiguous spec makes the model infer a task and default (Tamkin et al.), and an underspecified objective admits many equivalent-looking solutions that differ where it matters (D’Amour et al.) β the intent-diff surfaces that hidden choice; acceptance predicates should be executable (CodeT-style), and clarification beats the re-prompt loop when H1 is live β with behavioural disagreement among candidate generations a measured, automatable ambiguity signal and clarify-then-regenerate a measured gain (ClarifyGPT, Mu et al.).
- Forward link: pinned intent exposes the next suspect β the agent saw the contract but not the files it governs. Context is Chapter 26’s jurisdiction.
Next
The contract is pinned and the regeneration still fails β the sort predicate passes in isolation but the agent edits the wrong file, imports a stale helper, misses the migration the contract names. Intent is now innocent; attention is guilty. Chapter 26, “Debugging Context for Coding Agents,” performs context forensics: what the agent saw, what it didn’t, and whether the failure is a missing file or a misread one.