Skip to content

Numerical Contracts

vibecodingisold carries 8 binding Numerical Contracts. These values must appear verbatim in every file where they're referenced. Changing one in one role file without propagating to all the others is a P0 contract violation, caught by the validate-numerical-contracts.py validator in CI.

The canonical source for all 8 contracts is core/_conflict-resolutions.md. When in doubt, that file wins.


The 8 contracts

# Concept Canonical value Files where it must align
1 Idempotency-Key TTL 24 hours minimum architect.md Item 8, backend.md Item 1
2 Liveness handler timeout <2s backend.md Item 18, devops.md Item 14, sre.md Item 26
3 terminationGracePeriodSeconds floor 30s minimum backend.md Item 17, devops.md Item 15
4 Webhook timestamp tolerance 300s (5 minutes) backend.md Item 12, security.md Item 29
5 Vuln-scan severity gate HIGH+CRITICAL with available fix BLOCKS merge devops.md Item 10, security.md Item 30, qa.md Item 25
6 Secret rotation cadence 90 days security.md Items 19+20, devops.md Item 24
7 Active-passive RPO/RTO floor 5min RPO / 30min RTO architect.md Item 32, devops.md Item 40
8 Active-active RPO/RTO floor <1min RPO / 5min RTO architect.md Item 32, devops.md Item 40

Why each contract exists

1. Idempotency-Key TTL — 24 hours minimum

Why this number: mobile clients retry over hours of background time. A 1-hour TTL is too short — a user closes the app, the OS suspends the process, the user reopens 4 hours later, the queued retry fires, and the original idempotency record has already expired. The duplicate charge happens.

24 hours is the minimum that survives realistic mobile-app and integration-partner retry windows. Stripe ships their own idempotency-key infra at this TTL. RFC drafts converge on the same number.

Sources:

  • Stripe Engineering: "Designing robust and predictable APIs with idempotency" (Brandur Leach, 2017).
  • IETF draft-ietf-httpapi-idempotency-key-header-06.

Companion shape: Key format = 8-128 chars, charset [A-Za-z0-9_-]. Body-hash = SHA-256. Conflict response = 409 + Problem Details urn:problem:idempotency-conflict. In-flight detection = 60-second reaper for entries with NULL response status.


2. Liveness handler timeout — <2s

Why this number: the liveness probe is a "process is alive" check, not a "all dependencies healthy" check. It must respond fast — <2s — so the orchestrator's slow-readiness threshold doesn't kill a pod that's actually fine. A liveness handler that takes 5s to respond because it's checking a downstream DB is wrong; that's a readiness check.

<2s is the handler-side ceiling. The probe configuration adds slack: timeoutSeconds: 3, periodSeconds: 10, failureThreshold: 3.

Sources:

  • Kubernetes Documentation: Configure Liveness, Readiness and Startup Probes.
  • Google SRE Workbook: Probes and health checks.

Companion contract: readiness handler timeout also <2s; startup probe failureThreshold × periodSeconds >= 60s for slow-boot apps.


3. terminationGracePeriodSeconds floor — 30s minimum

Why this number: graceful shutdown needs three time budgets stacked: (a) the load-balancer deregistration grace (5s preStop sleep), (b) the in-flight request drain (typically 15-20s), © connection-pool close + open-transaction commit (5-10s). Sum: 25-35s. 30s minimum is the floor that fits the typical stack.

The full contract is terminationGracePeriodSeconds >= app.shutdownTimeout + 10s. App default shutdownTimeout = 20s. Therefore terminationGracePeriodSeconds default = 30s.

Sources:

  • Kubernetes Documentation: Pod Lifecycle (terminationGracePeriodSeconds semantics).
  • AWS Builders Library: Implementing health checks.

Companion shape: preStop hook must include a sleep 5 for load-balancer deregistration grace.


4. Webhook timestamp tolerance — 300s (5 minutes)

Why this number: webhook receivers must reject replayed events to defend against an attacker who captures a webhook payload and replays it after the original processing. The defense is a timestamp check — the receiver compares the Webhook-Timestamp header against now() and rejects if the gap is too large.

300s is Stripe's standard tolerance and matches industry practice. Tighter (e.g., 60s) breaks under legitimate clock skew between sender and receiver. Looser (e.g., 1h) widens the replay window unnecessarily.

Sources:

  • Stripe Engineering: Webhook security and signature verification documentation.
  • IETF draft on Webhook signature standards.

Companion shape: HMAC verification is mandatory (security.md Item 29); timestamp tolerance is the second leg of replay defense.


5. Vuln-scan severity gate — HIGH+CRITICAL with available fix BLOCKS merge

Why this rule: image and dependency scans surface CVEs continuously. Without a clear policy, teams drift toward "we'll address it next sprint" — and the sprint never closes. The policy below is the minimum that prevents drift while staying achievable.

The full policy:

  • HIGH+CRITICAL with available fix: BLOCK merge.
  • CRITICAL without fix: BLOCK merge until exception ticket created with 14-day SLA.
  • MEDIUM with fix: open auto-PR (Renovate/Dependabot).
  • LOW: tracked but non-blocking.

Sources:

  • OWASP Dependency-Check documentation.
  • CIS Benchmarks: Vulnerability management.
  • GitHub Advisory Database SLAs.

Companion ownership: security.md Item 30 owns the policy; devops.md Item 10 owns the implementation (Trivy/Grype in pipeline); qa.md Item 25 owns the merge-gate enforcement; devops.md Item 31 owns automated update PRs (Renovate/Dependabot config).


6. Secret rotation cadence — 90 days

Why this number: 90-day rotation is the SOC 2 / ISO 27001 / industry-baseline cadence for secrets that don't have automated lifecycle management. Tighter (30 days) is achievable for fully-automated rotation but breaks legacy integrations. Looser (180 days) fails most compliance audits.

The full policy:

  • Database credentials: 90 days.
  • API keys (external services): 90 days.
  • Webhook signing secrets: 90 days, with grace-period dual-validation during rotation.
  • TLS certificates: automated via ACME, 2-week-before-expiry alarm.
  • OIDC tokens: short-lived (1h), no rotation needed.

Sources:

  • SOC 2 Trust Services Criteria.
  • NIST 800-57: Recommendation for Key Management.
  • AWS Builders Library: Secret management.

Companion ownership: security.md Items 19+20 own the what (no plaintext anywhere, rotation policy, scanning). devops.md Item 24 owns the delivery mechanism (IAM role / OIDC for fetch, boot-time injection, never bake into image).


7. Active-passive RPO/RTO floor — 5min RPO / 30min RTO

Why these numbers: active-passive means a primary region serves traffic and a passive region is warm-standby. RPO (Recovery Point Objective) = how much data loss is acceptable on failover. RTO (Recovery Time Objective) = how long until traffic is restored.

5min RPO is achievable with continuous WAL streaming + verified replica lag monitoring. Tighter requires synchronous replication, which has latency cost. Looser means the failover loses meaningful customer data.

30min RTO assumes manual failover decision (humans in the loop) + automated DNS update + read-only warmup of the passive replica. Automated failover (no humans) can hit ~5min RTO but adds the risk of false-positive failover. 30min is the floor for the manual-decision path.

Sources:

  • AWS Builders Library: Disaster recovery patterns.
  • Google SRE Workbook: DR planning.

Companion ownership: architect.md Item 32 owns the commitment (which RPO/RTO target the product is committing to). devops.md Item 40 owns the implementation (replication, failover runbook, DR drill cadence).


8. Active-active RPO/RTO floor — <1min RPO / 5min RTO

Why these numbers: active-active means multiple regions serve traffic simultaneously, with conflict-resolution for cross-region writes. RPO is <1min because a region failure loses only the in-flight requests for that region. RTO is 5min because traffic shifting is automated and DNS TTLs dominate.

These are floors, not targets. Real active-active topologies on the consumer-grade stack (CockroachDB, Spanner, multi-region Aurora) hit RPO=0 / RTO<1min. The 5min RTO floor accommodates DNS propagation lag.

Sources:

  • AWS Builders Library: Multi-region active-active patterns.
  • Cloudflare Engineering Blog: Anycast routing and failover.

Companion ownership: same as active-passive — architect.md Item 32 commitment, devops.md Item 40 implementation.


How the validator enforces alignment

The validator at .github/scripts/validate-numerical-contracts.py does these checks:

  1. Read the canonical table from core/_conflict-resolutions.md.
  2. For each contract, scan every named file for the value.
  3. Fail loudly if the value is missing in a file where it must appear, OR if the value differs from the canonical.

The validator runs on every PR that touches core/. It's intentionally fast (<10s) so PRs aren't blocked by slow checks.

You can run it locally:

python .github/scripts/validate-numerical-contracts.py

What you do if a contract needs to change

Numerical Contracts are binding. Do not change one in your fork without proposing the change upstream first.

The process for proposing a contract change:

  1. Open a GitHub issue titled Numerical Contract change proposal: <which one>.
  2. Include: the proposed new value, the failure-mode the change addresses, the citation chain (>=2 Tier-1 sources for the new value), and which other contracts the change interacts with.
  3. The maintainer (or the broader community on V2+) reviews and decides.
  4. If accepted: PR updates core/_conflict-resolutions.md AND every file the contract appears in, in a single atomic commit.

Local override pattern (for projects that genuinely need a different value): document the override as an ADR-style note inside your PLAN-REQUIREMENTS.md, not as a modification of the canonical contract. Example:

## Override: Idempotency-Key TTL

**Canonical value:** 24 hours (per vibecodingisold V1.0).
**Project value:** 7 days.
**Rationale:** integration partner X has SLA-documented retry windows up to 4 days; we add safety margin.
**Trigger to revisit:** if integration X SLA tightens, or if we move to a vendor with shorter retry SLAs.

This keeps your project consistent with itself, without breaking the upstream contract.