Every canon "Do not" and every contract "Banned" line in one place, each
cited back to its source doc, so a builder who never opens the source doc
still meets the rule. Same idea as versable-builder/docs/DO-NOTS.md.
Generated by scripts/build-do-nots.py on 2026-08-23 from the Do-nots
section of every canon/*.md, patterns/*.md, v6/README.md, and the Banned
section of every contracts/*.md. Do not hand-edit; edit the source and
regenerate.
From canon/#
The runner and the payload#
- Do not put a runner behaviour (checkpoint, heartbeat, backoff, cancel, metering, finalization) inside a payload, however local it feels. Add it to the runner's verb set and call it. (speedway
partType.server.ts:114,normalize.server.ts:440,content.server.ts:628) (canon/01-runner-and-payload.md) - Do not answer the same lifecycle question twice inside the runner either. A second machine beside the first means the verb set is incomplete, and the cost is that "how does this get retried" has two answers. (walmart
jobs.py:636-771, besideorchestrator.py) (canon/01-runner-and-payload.md) - Do not let the runner name a domain field, even to avoid a payload-to- payload import. Give the payloads a shared package instead. (walmart
orchestrator.py:78,90) (canon/01-runner-and-payload.md) - Do not read the four divergent runner files in versable-runner as extension points. They are version skew, and the fork is not evidence about what varies between capabilities. (
../evidence/20260817-runner-four-file-diff.md) (canon/01-runner-and-payload.md)
Identity and tenancy#
- Do not issue one credential for all callers. (versable-runner
app/auth.py:32-45) (canon/02-identity-and-tenancy.md) - Do not treat a self-declared tag as identity or tenancy. Store it as attribution and label it claimed. (versable-runner
meta) (canon/02-identity-and-tenancy.md) - Do not put a static shared secret on an internal callback that is reachable from a public URL. (speedway
app/routes/tasks/module.tsx:4-9) (canon/02-identity-and-tenancy.md) - Do not reuse the caller's API key as the callback signing key. The issuer hands out a separate per-caller callback secret, so authentication and signature verification rotate independently and holding the API key does not let you forge a callback. (the extractor signs with
config.extractorApiKey,speedway/app/lib/extractor.server.ts:8-9,252) (canon/02-identity-and-tenancy.md) - Do not let a module verify users, sessions, or passwords. That is the app's job. (speedway's session auth is correct for an app and would be wrong in a module) (
canon/02-identity-and-tenancy.md) - Do not let one environment's identity be valid in another. (
runner-service-devruns asrunner-service@) (canon/02-identity-and-tenancy.md) - Do not create service-account keys to let a non-GCP caller in; use the issuer. (estate rule,
CLAUDE.md) (canon/02-identity-and-tenancy.md) - Do not skip tenant on any request, job, outcome, usage event, or log line. (
canon/02-identity-and-tenancy.md) - Do not accept a credential in a query string. It lands in access logs, browser history, and referrer headers. Header only. (the extractor webserver,
automation/router.py:35,request.query_params.get("key")) (canon/02-identity-and-tenancy.md)
Jobs and state#
- Do not report
runningfor a job that has not been expanded yet. Reportacceptedorexpandingand let the caller wait. (canon/03-jobs-and-state.md) - Do not leave a terminal state. Resolution and retry are new jobs with a
parent_job_id. (canon/03-jobs-and-state.md) - Do not write a retryable failure as an outcome. Only the terminal attempt is an
error. (canon/03-jobs-and-state.md) - Do not expose an unpaginated list. (
canon/03-jobs-and-state.md) - Do not require the caller to poll. Offer a callback and document the sweep. (extractor, per owner; speedway had to build around it) (
canon/03-jobs-and-state.md) - Do not accept a job without a tenant, and do not let a caller's
metaor attribution stand in for one. (versable-runner) (canon/03-jobs-and-state.md) - Do not let a job be deleted or mutated while it is running. (walmart-mvp
routes/jobs.py:215-216, the commented-out guard) (canon/03-jobs-and-state.md) - Do not put "stage" on the module surface. A stage is the app's composition over capabilities and child jobs; the module gives
capabilityandparent_job_idand the app reconstructs the stage. (vb-fable's shape check against both shipped jobs tables, 2026-08-18) (canon/03-jobs-and-state.md) - Do not let a read mutate. A
GETon outcomes is idempotent and safe to poll; "first pull wins" belongs on an explicit acknowledge call, never on a read. (the extractor's/jobs/{job_id}/resultsmarks the job consumed on first pull,speedway/app/lib/extractor.server.ts:11-12) (canon/03-jobs-and-state.md)
Dispatch and workers#
- Do not rely on the queue's retry for correctness. The handler owns the budget; the queue is a backstop. (both runner forks, documented) (
canon/04-dispatch-and-workers.md) - Do not batch inside one delivery without a heartbeat and a checkpoint. (walmart's current pipeline) (
canon/04-dispatch-and-workers.md) - Do not ship a job surface without cancel, or let a running job be deleted. (walmart
routes/jobs.py:215-216) (canon/04-dispatch-and-workers.md) - Do not put checkpoint, backoff, or poll machinery inside a payload. (speedway
partType.server.ts:114,normalize.server.ts:440,content.server.ts:628) (canon/04-dispatch-and-workers.md) - Do not build a second one inside the runner either. (walmart
jobs.py:636-771, besideorchestrator.py) (canon/04-dispatch-and-workers.md) - Do not enqueue N units at submit time. Enqueue one expansion. (versable-runner
jobs.py:93) (canon/04-dispatch-and-workers.md) - Do not write an outcome or charge a vendor without an idempotency guard. (
canon/04-dispatch-and-workers.md) - Do not let a per-unit timeout exceed the platform's. (
canon/04-dispatch-and-workers.md) - Do not drop an undeliverable event silently. Dead-letter it and count it. (
canon/04-dispatch-and-workers.md)
Storage and persistence#
- Do not key storage without the tenant. (versable-runner
jobs/{job_id}/…) (canon/05-storage-and-persistence.md) - Do not overwrite an outcome. Create-if-absent, once. (
canon/05-storage-and-persistence.md) - Do not open a live connection to another system's database for reference data. Snapshot it, version it, report the version. (
canon/05-storage-and-persistence.md) - Do not keep a per-item status column a worker must keep in sync with the outcome it just wrote. (
canon/05-storage-and-persistence.md) - Do not put a module's own artifacts on a different cloud from its own storage. (versable-runner image output to S3) (
canon/05-storage-and-persistence.md) - Do not make a bucket public unless the artifact is meant to be public and the bucket holds nothing else. (walmart's split is the allowed shape) (
canon/05-storage-and-persistence.md) - Do not ship without a retention rule. Buckets do not clean themselves. (
canon/05-storage-and-persistence.md)
Caching#
- Do not put a tenant's data in a cross-tenant cache key, and do not leave the tenant out of a key when the value carries tenant data. (
canon/06-caching.md) - Do not cache without the capability version and reference-data versions in the key. A stale hit is a wrong answer. (
canon/06-caching.md) - Do not serve a cached outcome without marking it cached. (
canon/06-caching.md) - Do not let a payload open a cache client. The runner provides the verb. (
canon/06-caching.md) - Do not treat Redis as durable storage. (
canon/06-caching.md) - Do not ship a cache without a TTL the store enforces. (
canon/06-caching.md) - Do not report a cache hit as a bare boolean. Carry the block, including on a miss. (
canon/06-caching.md) - Do not omit the key's version inputs from what the consumer sees. A hit nobody can explain is a hit nobody can trust. (
canon/06-caching.md)
Observability#
- Do not cap a per-item log at the last run. Append, chunk, and keep every attempt. (walmart
orchestrator.py:255-264) (canon/07-observability.md) - Do not require a shell to answer "why did item 4,312 fail". (all three instances today) (
canon/07-observability.md) - Do not let a payload write its own ids into log lines. The runner scopes the logger. (
canon/07-observability.md) - Do not name a Cloud Run health route
/healthz. (speedwayserver.js:14) (canon/07-observability.md) - Do not serve a health route that cannot go red. (
canon/07-observability.md) - Do not emit a stat, log, trace, or usage event without tenant and job on it. (
canon/07-observability.md)
Usage and credits#
- Do not record usage without an idempotency key. (walmart
usage.py, lossy by design) (canon/08-usage-and-credits.md) - Do not let a metering failure fail or revert the work. (speedway's rule, adopted) (
canon/08-usage-and-credits.md) - Do not drop a usage event on failure. Dead-letter it, replay it into the original period. (
canon/08-usage-and-credits.md) - Do not record usage without tenant, job, item, and attempt. (versable-runner
usage/{idx}.jsonhas no tenant) (canon/08-usage-and-credits.md) - Do not put plan logic (included bands, overage) in a module. Emit events; the app decides. (
canon/08-usage-and-credits.md) - Do not delete usage events. They are the billing record. (
canon/08-usage-and-credits.md) - Do not emit one opaque blob per item. One event per meter per attempt. (
canon/08-usage-and-credits.md)
Configuration and secrets#
- Do not read an environment variable outside the config object. (
canon/09-config-and-secrets.md) - Do not let an absent value silently change behaviour. Make the mode an explicit value. (versable-runner
RUNNER_API_PASSWORDempty) (canon/09-config-and-secrets.md) - Do not default a value that must differ per environment. (
canon/09-config-and-secrets.md) - Do not print a secret anywhere: log, echo, error message, transcript. (
canon/09-config-and-secrets.md) - Do not commit a
.envwith real values; commit.env.example. (canon/09-config-and-secrets.md) - Do not mix vendor-specific and vendor-neutral values in one flat namespace without grouping. (speedway
config.server.ts) (canon/09-config-and-secrets.md)
Human in the loop#
- Do not turn "I cannot verify this" into an error. Emit
needs_review. (versable-runnerRESEARCH_QUALITY_LOW) (canon/10-human-in-the-loop.md) - Do not hold, wait on, or gate a job in a module because an item needs a person. (
canon/10-human-in-the-loop.md) - Do not build a review queue or a threshold policy inside a module. (
canon/10-human-in-the-loop.md) - Do not mutate a job to record a resolution. New job,
parent_job_id. (canon/10-human-in-the-loop.md) - Do not emit
needs_reviewwithout a stable reason and evidence; a reasonless hold is a guess with extra steps. (canon/10-human-in-the-loop.md) - Do not implement review inside a capability, or as a modification of the item. It is its own declared step with its own verdict. (
canon/10-human-in-the-loop.md) - Do not build a review surface for one customer. The declaration is uniform or the consumer cannot render one queue. (
canon/10-human-in-the-loop.md) - Do not express "no review" by removing the step. Configure it to a no-op, so a consumer can tell a disabled gate from an unbuilt one. (
canon/10-human-in-the-loop.md) - Do not fail a row because it needs a person. Parking and failing are different outcomes and a consumer acts on them differently. (
canon/10-human-in-the-loop.md)
The capability manifest#
- Do not accept a capability, param, or setting the manifest does not list. (
canon/11-capability-manifest.md) - Do not emit an error code or review reason the manifest does not list. (
canon/11-capability-manifest.md) - Do not hand-write the prose guide; generate it from the manifest. (versable-runner generates
/usagefrom the registry, which is the right half of this) (canon/11-capability-manifest.md) - Do not change a schema without bumping
module_version, and do not remove a capability without a major bump and a period of running both. (canon/11-capability-manifest.md) - Do not require a caller to read source to learn what a module needs. (
canon/11-capability-manifest.md)
Deployment and environments#
- Do not share a service account, bucket, queue, or issuer client between environments. (
runner-service-dev) (canon/12-deployment-and-environments.md) - Do not ship a module that only knows one environment. (extractor) (
canon/12-deployment-and-environments.md) - Do not deploy without a
/build-infothat names the commit. (versable-runner) (canon/12-deployment-and-environments.md) - Do not provision by hand. Script it, idempotent, in the repo. (
canon/12-deployment-and-environments.md) - Do not let a config value from one env run under another's
env. (canon/12-deployment-and-environments.md) - Do not promote by rebuilding. A version is built once and an environment points at it; promoting is moving the pointer, not producing a second artifact from the same source. Two builds of one commit are two artifacts, and which one is live becomes unanswerable. (
evidence/20260820-internal-dashboard-reuse.md) (canon/12-deployment-and-environments.md) - Do not name a health route
/healthzon Cloud Run. (speedway found this the hard way,server.js:14) (canon/12-deployment-and-environments.md)
Local development and debugging#
- Do not require a bucket, a queue, a database, or a credential to run a module on a laptop. (
canon/13-local-dev-and-debugging.md) - Do not let the in-process mode take a different code path from prod. Same handler, different adapters. (
canon/13-local-dev-and-debugging.md) - Do not let the
localverifier start on a public URL. (canon/13-local-dev-and-debugging.md) - Do not make "debug one prod item" require shell access. (
canon/13-local-dev-and-debugging.md) - Do not point local at a shared cloud project by default. (speedway) (
canon/13-local-dev-and-debugging.md) - Do not answer
/health/deepdifferently in shape across environments. (canon/13-local-dev-and-debugging.md)
Graceful degradation, variants, and several versions at once#
- Do not ship a variant whose output schema differs from the capability's. That is a new capability. (
canon/14-graceful-degradation.md) - Do not fall back to a different variant without recording it on the outcome. (
canon/14-graceful-degradation.md) - Do not emit a
confidencethe module did not compute or receive. (canon/14-graceful-degradation.md) - Do not remove a capability or a route without a deprecation period the manifest announces. (
canon/14-graceful-degradation.md) - Do not hold per-tenant config inside a module. Take it per job from the app. (
canon/14-graceful-degradation.md) - Do not let a dependency outage hang a job. Complete with errors or review, and say why. (
canon/14-graceful-degradation.md) - Do not deregister, hide, or omit a capability because its provider is unconfigured. Register it, report it not ready, and name the missing piece. (
canon/14-graceful-degradation.md) - Do not report a module ready without running its preflight checks. (
canon/14-graceful-degradation.md) - Do not swallow a preflight warning inside the module. It travels to the consumer or it does not exist. (
canon/14-graceful-degradation.md) - Do not exempt a local or development module from provider configuration and the preflight checklist. (
canon/14-graceful-degradation.md)
Outputs and transforms#
- Do not add a consumer's row shape to an outcome. Declare a transform. (
canon/15-outputs-and-transforms.md) - Do not mutate an outcome to reflect a human edit. Write the edit in the app; submit a child job if the module must see it. (
canon/15-outputs-and-transforms.md) - Do not build an export that only works on a finished job. Read what is settled, say what is not. (
canon/15-outputs-and-transforms.md) - Do not hand-code M≠N inside a capability's
run. That is a transform, or arun_batchcapability with a stated output schema. (canon/15-outputs-and-transforms.md) - Do not treat a materialized export as a source; name it by its high-water mark and regenerate. (
canon/15-outputs-and-transforms.md) - Do not declare a fixed column list for output keys a person names at run time. It cannot be right, and it fails silently: the column is absent from the file rather than reported missing. (
canon/15-outputs-and-transforms.md)
From contracts/#
Contract: per-module caller keys (option C)#
- A key without an expiry. (
contracts/caller-keys.md) - One key shared by more than one caller. (
contracts/caller-keys.md) - A key valid in more than one environment. (
contracts/caller-keys.md) - A key stored in clear, or logged. (
contracts/caller-keys.md) - A key accepted anywhere except the
Authorizationheader. (contracts/caller-keys.md) - Reusing the key as the callback signing secret. (
contracts/caller-keys.md) - Sharing the key store itself with another service. Copy the pattern, keep the tables separate: a schema change in the other service's key table silently changes this module's auth surface, and the two stop being able to deploy independently. (
evidence/20260820-internal-dashboard-reuse.md) (contracts/caller-keys.md)
Contract: the capability manifest and the field registry#
- A capability id that is not
<domain>.<verb>[.<qualifier>]. (contracts/manifest.md) - A
providesorrequiresentry that is not a registry id. (contracts/manifest.md) - A provided field whose value sits anywhere but at its id's path in
output, or a required field read from anywhere but its id's path initems[].input. (contracts/manifest.md) - An
x.registry id inrequires. (contracts/manifest.md) - A
meterslist that repeats a standard meter. (contracts/manifest.md) - A
skippedoutcome whose reason is outsideduplicate,filtered,cancelled,superseded. (contracts/manifest.md) - An
error_typeorreview_reasonemitted at runtime and absent here. (contracts/manifest.md) - A
settingskey honoured at runtime and absent here. (contracts/manifest.md) - A manifest at 0.2 or later that omits
ceilings,cost_model, orretention; at 0.1 a capability may omitreview_reasons,confidence,ceilings,settings,cost_modelandlogand its instance doc says which, so no invented number enters a machine-readable file (review v2 J4). (contracts/manifest.md) - Two capabilities in one module with the same id and different schemas; that is a version bump or a new id. (
contracts/manifest.md)
Contract: the module surface#
- A shared credential across callers on any route. (
contracts/module-surface.md) - A job route that works without a tenant. (
contracts/module-surface.md) - An unpaginated list. (
contracts/module-surface.md) - A
runningstate before expansion. (contracts/module-surface.md) - A
/resultsshape that requires downloading every outcome to read one. (contracts/module-surface.md) - A callback without a signature or an event id. (
contracts/module-surface.md) - Secret values in
/health/deep's config echo. (contracts/module-surface.md)
Contract: the runner's verbs#
- Importing a queue, storage, cache, tracing, or secrets client. (
contracts/runner-verbs.md) - Reading environment variables. (
contracts/runner-verbs.md) - Writing a status, a job record, or a usage record directly. (
contracts/runner-verbs.md) - Sleeping in a loop to wait for a vendor; use
ctx.wait_for. (contracts/runner-verbs.md) - Keeping its own cursor or heartbeat; use
ctx.checkpoint/ctx.iterate. (contracts/runner-verbs.md) - Returning a
Resultwith aconfidenceit did not compute or receive. (contracts/runner-verbs.md)
Contract: the usage event#
- An event without
idem_key,tenant,job_id, ormeter. (contracts/usage-event.md) - A meter emitted at runtime that is neither a standard meter (above) nor in the capability's
cost_model.meters. (contracts/usage-event.md) - Deleting or rewriting an event. A correction is a new event carrying
corrects: <event_id>indetail. (contracts/usage-event.md) - Recording
cost_usdwithoutquantity. The quantity is what survives a price change. (contracts/usage-event.md)
From patterns/#
Building a module#
- Do not start from a payload file and grow a runner around it. (
patterns/00-building-a-module.md) - Do not write code before the manifest. (
patterns/00-building-a-module.md) - Do not open a live connection to App V5's Mongo or Pinecone; snapshot. (
patterns/00-building-a-module.md) - Do not deploy before local mode and the in-process conformance run are green. (
patterns/00-building-a-module.md) - Do not deploy dev under prod's identity. (
patterns/00-building-a-module.md) - Do not call it done without an instance breakdown. (
patterns/00-building-a-module.md)
Consuming a module from an app#
- Do not forward a user's credential to a module. Exchange the app's own. (
patterns/01-consuming-a-module.md) - Do not submit without a tenant, or with a tenant the app has not verified the user belongs to. (
patterns/01-consuming-a-module.md) - Do not invent a mapping table between app ids and module ids; pass the app's ids as
client_job_idanditem_id. (patterns/01-consuming-a-module.md) - Do not show
runningfor anacceptedjob. (patterns/01-consuming-a-module.md) - Do not treat a callback as the only signal; sweep. (
patterns/01-consuming-a-module.md) - Do not put the customer's bar or per-tenant config in the module. (
patterns/01-consuming-a-module.md)
Deploying a module#
- Do not share a service account, bucket, queue, or secrets namespace between environments. (
runner-service-devruns asrunner-service@) (patterns/02-deploying-a-module.md) - Do not let two environments push the same image tag. Tag by commit. (walmart's preview overwrote the artifact prod would reuse) (
patterns/02-deploying-a-module.md) - Do not deploy without a
/build-infothat names the running commit. (patterns/02-deploying-a-module.md) - Do not call a deploy done because the build went green. Assert the served commit equals the built one. (
patterns/02-deploying-a-module.md) - Do not provision by hand. If it is not an idempotent script in the repo, it will be a memory of clicks by the second environment. (
patterns/02-deploying-a-module.md) - Do not deploy a module that cannot run in-process with zero environment set. (
patterns/02-deploying-a-module.md) - Do not leave a preview sharing its base environment's queue. (
patterns/02-deploying-a-module.md) - Do not set
minScaleabove zero without saying what the cold start was costing. It bills continuously. (patterns/02-deploying-a-module.md) - Do not ship a workstation deploy that does not print the commit it deployed. (
patterns/02-deploying-a-module.md)
The identity recipe#
- Do not forward user credentials to a module. (
patterns/04-identity-recipe.md) - Do not put tenant only in a self-declared tag. (
patterns/04-identity-recipe.md) - Do not share a credential across callers or across environments. (
patterns/04-identity-recipe.md) - Do not verify users in a module. (
patterns/04-identity-recipe.md) - Do not use a static shared secret on an internal callback reachable from a public URL. (
patterns/04-identity-recipe.md) - Do not create service-account keys to let a non-GCP caller in. (
patterns/04-identity-recipe.md) - Do not answer 404 to an authorization failure. (
patterns/04-identity-recipe.md)
Authoring an agentic module#
- Do not import a search client or a tracing client in a payload; reach them through
Context. A model client may be imported, and every call through it is wrapped inctx.traceand, for a vendor with a limiter,ctx.guarded(../contracts/runner-verbs.md), which is what makes the call visible and budgeted. (patterns/05-authoring-an-agentic-module.md) - Do not return a confidence you did not compute or receive. (
patterns/05-authoring-an-agentic-module.md) - Do not guess when unsure;
needs_reviewwith a reason and evidence. (patterns/05-authoring-an-agentic-module.md) - Do not bake a tenant's template into a prompt; pass it in
params. (patterns/05-authoring-an-agentic-module.md) - Do not ship a prompt or model change without running the eval set. (
patterns/05-authoring-an-agentic-module.md) - Do not smooth a module's own recipe into another module's; name it, keep it, make its ingredients showable. (
patterns/05-authoring-an-agentic-module.md)
From v6/#
Workflow Console V6, mapped onto the contract#
- Do not put a customer's bar, Goals, or per-tenant config inside a module. (
v6/README.md) - Do not forward a user's session or JWT to a module. Exchange the console's own client credential for a token. (
v6/README.md) - Do not let the console be the only place a module can be called from. Every module the console uses is callable standalone with the same surface; that is the test that the boundary held. (
v6/README.md) - Do not build the composer before a second workflow shape is actually needed. Build the manifest so it is possible. (
v6/README.md)
180 lines gathered.