-
example of The Contract
The Shared Shape
Hook
both sides of a seam import one shared request-and-return shape
Thesis
Both sides of a seam import one seam-owned request-and-return shape, so the agreement is one declaration rather than two synchronized copies held together by a process promise.
Laws & fences
- Put the contract on the edge: when the sender owns one description and the receiver owns another, synchronization is an activity; when both point to one seam-owned artifact, synchronization is the structure.
- The shared contract carries how the receiver consumes the result as well as the shapes: complete means unanswered is empty, blocked means the caller surfaces the unanswered questions instead of filling them silently, and shape without consumption rules would specify bytes, not the handshake.
- The shared file does not make the crossing correct by itself; it makes the agreement singular and addressable, and enforcement is a separate role.
- The concrete syntax does not travel: a Python workflow can share a TypedDict, separate services can generate local types from one schema, and two agent steps can reference one Markdown or YAML contract; the invariant is the shared source, not the language.
When to reach
- Reach for it when each side of a seam keeps its own description of the crossing and a change that looked local altered the handshake for one participant and not the other.
- Do not reach for it to prove runtime validation, compatibility across deployed versions, or recovery from a bad return; those are different decisions, and a separate pattern begins where an assertion checks this agreement.
-
The Contract Keystone
-
The Contract
A write_brief step asks a collect_evidence step to research one question.
The caller sends a request; the research step sends findings back. They are
implemented in separate files and can change independently, but both live in
the same repository and can import a shared module.
The seam has two directions:
┌────────────────────────┐ ┌────────────────────────┐
│ caller: write_brief │ │ step: collect_evidence │
│ imports Request │──────▶│ imports Request │
│ imports Return │◀──────│ imports Return │
└───────────┬────────────┘ └────────────┬───────────┘
│ │
└──────── both import ────────────┘
│
contracts/research_handoff.py
┌──────────────────────┐
│ ResearchRequest │
│ ResearchReturn │
│ consumption rules │
└──────────────────────┘
The first implementation gives each side its own description of the crossing.
The caller expects sources; the research step has renamed that field
evidence:
# caller.py # research_step.py
class ResearchReturn(TypedDict): class ResearchReturn(TypedDict):
sources: list[Source] evidence: list[Evidence]
Neither file is malformed in isolation. The disagreement exists only at the seam: a change that looked local altered the handshake for one participant and not the other.
Make the agreement seam-owned. Move the request shape, return shape, and the
rules for consuming each status into
contracts/research_handoff.py. Both sides
import those names instead of restating them.
The nearest alternative is synchronized copies: keep one type beside the caller and another beside the research step, then remember to edit both. That is two declarations with a process promise between them. The shared module is one declaration.
The baseline has two sources of truth:
caller/ResearchReturn ≈ research_step/ResearchReturn
The ≈ is the problem. Agreement depends on two files remaining coincidentally
equivalent, and review must reconstruct the seam by comparing them.
The single decision is relocating both private descriptions into one shared contract file. The standing agreement carries the two shapes:
class ResearchRequest(TypedDict):
question: str
allowed_sources: list[str]
as_of: str
class ResearchReturn(TypedDict):
status: Literal["complete", "blocked"]
findings: list[Finding]
unanswered: list[str]
It also carries how the receiver consumes the result: complete means
unanswered is empty; blocked means the caller surfaces the unanswered
questions instead of filling them silently. Shape without consumption rules
would specify bytes, not the handshake.
Both participants now point at the same artifact:
# caller.py
from contracts.research_handoff import ResearchRequest, ResearchReturn
# research_step.py
from contracts.research_handoff import ResearchRequest, ResearchReturn
The request and return values are the per-crossing instances. Their as_of
and evidence fields carry the freshness and provenance required for this
particular handoff; the shared module is the standing agreement that requires
those fields on every handoff.
The after-state has one equality, not an approximation:
caller ──imports──▶ research_handoff ◀──imports── research_step
Renaming findings now changes the seam once. Both implementations encounter
the same new shape through the same import, and a reviewer can inspect the
whole handshake without diffing participant-owned copies.
The shared file does not make the crossing correct by itself. It makes the agreement singular and addressable; enforcement is a separate role.
The supporting sketch is executable:
python3 demo.py
It proves the property this example claims:
caller.pyimports both crossing types from the contract;research_step.pyimports the same two types;- neither participant declares a local request or return shape; and
- one request crosses into the step and one return crosses back.
The sketch does not prove runtime validation, compatibility across deployed versions, or recovery from a bad return. Those are different decisions. The The Contract Keystone begins where an assertion checks this agreement.
Put the contract on the edge. When the sender owns one description and the receiver owns another, synchronization is an activity. When both point to one seam-owned artifact, synchronization is the structure.
The concrete syntax does not travel. A Python workflow can share a
TypedDict; separate services can generate their local types from one schema;
two agent steps can both reference one Markdown or YAML contract. The invariant
is the shared source, not the language that expresses it.
The relationships ledger
Evidence-bearing references
Relationships
Every connection keeps the section where it was found. The map above orients; this ledger carries the evidence.
Outbound references 1
-
in-slice · occurrence 1
The Contract Keystone
contract ↔ verification ↔ re-dispatch
Evidence: Verification · occurrence 1
Inbound references 1
-
in-slice · occurrence 1
The Contract
both sides of a seam import one shared request-and-return shape
Evidence: Examples · occurrence 1
↑ back to the top ← the survey
Node shared-shape · corpus 31de4cb · Catalog revision 35263c4c415da742953d0462804fb14424e2244dae4c63efd27e468988de70ab