promises
Declare the contracts your repos share, so agents and people read them before changing them.
A promises.yaml at the root of a multi-repo workspace lists every value one repo produces and another consumes: a Firestore field, a Pub/Sub topic, an HTTP route, an environment variable. Each row carries one line of meaning and one line of invariant. That is the whole idea. There is no graph, no server, no hook. A one-line pointer in your agent instructions file is the integration.
promises.yaml # the declarations (see SPEC.md)
promises.schema.json # JSON Schema for editors and the lint
promises check # a lint: every row has meaning + invariant, writer files exist, keys still appear in them
Why it exists
Picture a parking operator's platform split across a few repositories: a web app the operators use, a mobile app the drivers use, and a handful of cloud functions that copy documents from the shared Firestore into PostgreSQL for billing. The web app writes a sessions/{plate} document with a boolean is_parked. The app's "parked now" screen is one equality query on that field.
The rule that is_parked is true only while the session has no end time lives in exactly one place: the body of the function that computes it. One day a refactor rebuilds that payload inline, drops the helper, and writes is_parked: True, because every session the new code path sees is, at that moment, being started. Nothing fails. The app keeps showing cars as parked after they leave, and the first person to notice is a driver disputing a bill months later.
The field had writers in two repositories and a reader in a third, and no file anywhere said what it meant. That is the gap promises.yaml fills: one row, one meaning, one invariant, read by whoever is about to change a writer.
The obvious fix looked like a tool: a cross-repo contract graph, a server to query it, and a pre-edit hook that warns an agent "this field has consumers in another repo". We wrote the plan for that tool and then tested whether it was needed before building it.
What the experiment showed
On a private multi-repo workspace where a flag of exactly this shape had drifted, fresh planning agents were given the original refactor task, on a snapshot of the code at the commit before the incident, with the sibling repos visible. Nothing in the prompt mentioned the flag or its invariant.
| Setup | Found the consumer in the other repo | Preserved the invariant |
|---|---|---|
| Instructions file only, as it was | 1 of 3 | 3 of 3, by reusing the old function body |
| + one sentence: "grep the sibling repos for consumers before finalising" | 3 of 3 | 3 of 3 |
| Harder prompt that asks for the payload rebuilt inline, the helper dropped and the flag set on the way through, sentence only | 3 of 3 | 3 of 3 (one as a literal True inside a gate) |
Same prompt + promises.yaml present and pointed to |
4 of 4 | 4 of 4, each citing the row by line |
One of the four runs with the file present was wrapped as a compacted session whose summary claimed orientation was already done. It still found the file, by listing the root directory, not by reading the pointer.
By the decision rule fixed before the runs, the hook lost to a YAML file. Declarations plus one sentence became v1, and the tool was never built.
Read the caveats. Seven runs, one codebase, one model, one afternoon. The field under test also had a one-line warning in the instructions file in every arm, so the experiment cannot separate "a declarations file" from "a well-aimed sentence about that one field". What the file demonstrably added was form, every run wrote the derivation and none wrote a literal, and reach: the runs also applied rows for fields the instructions file never mentioned, and raised the rows marked unknown as questions for the maintainer.
Use it
-
Copy
examples/promises.yamlto your workspace root and replace the rows with yours. Start with the fields that have bitten you. -
Add to your
CLAUDE.md/AGENTS.md:Cross-repo contracts and their invariants are declared in
promises.yaml. Read the relevant rows before changing any field, topic, or route listed there. -
In CI, from the workspace root:
uvx --from promises-yaml promises checkIt fails when a row lacks
meaningorinvariant, when awritten_byglob matches no file, when two rows share anid, or when a declared key no longer appears in any of its writer files.
Where you cannot state an invariant, write invariant: UNKNOWN — <owner> to confirm. Agents treat that as a question to raise, which is the right behaviour.
What it is not
Not a scanner: consumers are found by grepping, which the sentence tells the agent to do. Not a schema registry: schema: may point at a JSON Schema or a model file, but the lint does not read it. Not a hook: if you need an edit-time interrupt, the experiment above is the reason we did not build one; your codebase may differ.
Licence
MIT.
Metadata
Release files for promises-yaml 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| promises_yaml-0.1.1.tar.gz | 51.0 kB | Without provenance | Details for promises_yaml-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| promises_yaml-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for promises_yaml-0.1.1-py3-none-any.whl |
Total release size: 58.0 kB
Release files / promises_yaml-0.1.1.tar.gz
| Download URL | promises_yaml-0.1.1.tar.gz |
|---|---|
| Size | 51.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
93b9dd143d83aafc3682b8f2bdd17258dcc2f6eb1f433aa081568a23513cd370
|
|
BLAKE2b-256 checksum How to use checksums |
eae704e78c6ad2907d4382f8abaacfc0786b1dd3ce4992df3c49141acc5763e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / promises_yaml-0.1.1-py3-none-any.whl
| Download URL | promises_yaml-0.1.1-py3-none-any.whl |
|---|---|
| Size | 7.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
406ccd4aeecc052894662dffc22d977851db7b93c2a0c655d877b8586e59ce42
|
|
BLAKE2b-256 checksum How to use checksums |
cf01f4d39bf3d110c5721f094da286434dd2c15da16cd71ae383c2f69988c20b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for promises-yaml 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.