# Your first output contract: shape is not truth

An assistant extracts a machine's condition from a note. Another program
needs to know which source was used, which version it was, what was observed
and what supports the observation. A paragraph may be readable to a person
without being a stable interface for that program.

This original 40-minute exercise teaches that interface using a small JSON
response, an actual JSON Schema validator and one fictional source. There is
no model call, external action or claim of measured model accuracy.

## 1. Give the answer a usable shape

**Structured output** is data arranged to an agreed contract. **Prompting**
describes a task and context to a model. Asking for a shape and validating
the returned value are different jobs.

Think of a typed function result. A caller expects particular fields, but
an annotation in your code cannot prove that incoming text from an external
service has those fields. A runtime validator inspects the actual value.

**JSON**, JavaScript Object Notation, is a text data format used across
languages. **JSON Schema** describes rules for JSON values. Neither is an
AI model, a database or a guarantee that a statement is true.

Here is the complete fictional source:

```text
NOTE-7 · revision 3
Pump P7 is running. Temperature is 41 C. No shutdown is recorded.
```

Here is the supported response in this exercise. Save it as `answer.json`
when you reach the local setup:

```json
{
  "source_id": "NOTE-7",
  "source_revision": 3,
  "condition": "running",
  "evidence": "Pump P7 is running."
}
```

The four fields answer four questions: which note, which version, what
condition, and what evidence? This is a draft finding. It does not operate
a machine.

## 2. Run the local contract

Download these files into one folder:

- [First output contract · Python](/templates/fde-first-schema.py)
- [Pinned JSON Schema environment](/templates/fde-first-schema-requirements.txt)

Use Python 3.11 or later. On macOS or Linux:

```shell
python3 -m venv .venv-schema
. .venv-schema/bin/activate
python -m pip install -r fde-first-schema-requirements.txt
python fde-first-schema.py test
python fde-first-schema.py compare
```

On Windows, use Command Prompt:

```shell
py -3 -m venv .venv-schema
.venv-schema\Scripts\activate.bat
python -m pip install -r fde-first-schema-requirements.txt
python fde-first-schema.py test
python fde-first-schema.py compare
```

Installation needs internet access. The exercise commands block socket
connections and DNS lookup. The actual `jsonschema` 4.26.0 validator uses
a local Draft 2020-12 schema with no remote references. It needs no provider
account, key, collector or model.

Eight checks cover stage order, downstream skips, different failure reasons,
type handling, source mismatch, parser limits, diagnostic privacy and local
repeatability. The report is deterministic for the pinned environment.

## 3. Read before validating

The first boundary is a parser:

```text
Response text
      |
      v
Read JSON ---- unreadable or ambiguous ----> hold
      |
      v
Check shape -- violates the contract ------> hold
      |
      v
Compare note - unsupported or old source --> hold
      |
      v
Supported draft within this fixture
```

A parser turns text into values. JSON object property names are quoted
strings; the values may be strings, numbers, booleans, null, arrays or
objects. The number `3` and the string `"3"` are different.

The supplied parser refuses a trailing comma, duplicate property names
and nonstandard `NaN` or `Infinity` values. It does not silently choose
one of two conflicting duplicate fields. Inputs are limited to 4 KiB.

Run:

```shell
python fde-first-schema.py run --case syntax
```

Read `first_stop: "parse"`. The schema and source stages say `not-run`.
That is neither a pass nor a second diagnosis of the response.

## 4. Make the field rules explicit

The schema's `properties` entry describes the fields:

| Field | Shape rule | What that rule cannot establish |
| --- | --- | --- |
| `source_id` | Nonempty string, at most 40 characters | Whether the note exists or is accessible |
| `source_revision` | Integer, at least 1 | Whether this is the current version |
| `condition` | String: `running`, `stopped` or `unknown` | Whether the source supports that value |
| `evidence` | Nonempty string, at most 180 characters | Whether the quote supports the claim |

All four fields also appear in `required`. Merely describing a property
does not make it mandatory. `additionalProperties` is false here, so an
unexpected `execute` property is refused.

Run these examples and predict the rule keyword first:

```shell
python fde-first-schema.py run --case wrong-type
python fde-first-schema.py run --case missing
python fde-first-schema.py run --case null
python fde-first-schema.py run --case extra
python fde-first-schema.py run --case enum
```

| Change | First stop | Rule keyword |
| --- | --- | --- |
| Revision is `"3"` | Schema | `type` |
| Evidence is omitted | Schema | `required` |
| Evidence is `null` | Schema | `type` |
| Extra `execute` field | Schema | `additionalProperties` |
| Condition is `idle` | Schema | `enum` |

Null is a value, not an omitted property. An enum is a permitted-value list,
not a truth test. Validation does not fill missing knowledge or coerce a
quoted number into an integer. The path `/evidence` identifies that field;
`/` identifies a rule on the object itself.

## 5. Challenge a perfectly shaped response

```shell
python fde-first-schema.py run --case unsupported
python fde-first-schema.py run --case stale
```

The first response says `stopped`. That word is in the enum, so the schema
passes. The note says `running`, so the separate source comparison holds it.

The second names revision `2`. It is a valid positive integer, so the schema
passes. The supplied source is revision `3`, so the source check holds it.

This comparison is intentionally narrow: it checks the identifier, revision,
condition and quote against explicit values for one fixture. It is not a
general semantic evaluator, and it cannot determine whether arbitrary prose
is true. In a real system, a quote may occur in a document without supporting
the conclusion. The receiving workflow needs appropriate evidence and review.

The accepted outcome is `supported-draft`, not an executed action. Schema
validity does not authenticate a caller, authorize a tool or prove that an
external operation completed.

## 6. Change one value and explain the boundary

Save the example from section 1 as `answer.json`, then run:

```shell
python fde-first-schema.py check --file answer.json
```

Change one thing at a time:

1. Change `3` to `"3"`. Predict a type failure.
2. Restore the number, then change it to `2`. Predict a source revision failure.
3. Restore revision 3, then omit evidence. Predict a required-field failure.
4. Restore evidence, then use `null`. Predict a type failure.
5. Restore the quote, then add an `execute` field. Predict an additional-field failure.

The `check` command returns bounded diagnostics without echoing the submitted
text. Its exit code indicates whether the command ran. Inspect `first_stop`
and `outcome` for the response decision; a successful process exit is not
acceptance of the response.

Keep the contract and source unchanged during this comparison. Changing the
schema to make a failing response green changes what you are claiming to test.

## 7. Connect the exercise to a real model integration

A provider's structured-output feature may constrain generation to a supported
shape. That differs from a prompt that simply says “return JSON.” This lab tests
the local receiving checks with supplied responses; it does not exercise
provider generation or measure a model.

When integrating a provider, verify its current schema support and completion
states. Explicitly handle refusal and incomplete generation. A successful HTTP
response does not alone establish complete usable content. Keep a bounded
repair policy instead of retrying indefinitely.

Final structured responses and structured tool arguments have different
consumers. The latter proposes a function's arguments; it grants no permission
to run that function. Current access, source support and action policy remain
application responsibilities.

## 8. Keep a receiving-engineer note

Keep:

- The schema version and meaning of each field.
- A response that stops at parsing, one at shape validation, and one at source comparison.
- A supported draft and an explanation of what it still cannot authorize.
- A proposed negative test for changed sources or a changed consumer contract.

The next lesson develops source acceptance and human review:
[Open structured-results practice](/app/academy?course=architecture-foundations&lesson=structured-results&view=focus).
The advanced [structured agent output lab](/training/guides/agent-engineering#2h-validate-structured-agent-output-before-source-review)
uses actual LangChain ToolStrategy validation and repair.

## Primary references

Checked 20 October 2026. These references support the concepts; the fictional
note, exercise and teaching sequence are original LockedIn Labs material.

- [JSON Schema: object properties, required fields and additional properties](https://json-schema.org/understanding-json-schema/reference/object)
- [python-jsonschema: schema validation](https://python-jsonschema.readthedocs.io/en/stable/validate/)
- [Claude: structured outputs and completion limits](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)
