Skip to content

000 — Constitution ​

Non-negotiable principles for this project. Every spec, ADR, and pull request is checked against this document. Changing a principle requires an ADR that supersedes the relevant clause.

C1. No tenant data in version control ​

No production prompt, price list, course catalog, customer message, phone number, or credential may enter git history. Tenant configuration lives in config/ and .env, both gitignored; only *.example scaffolds are tracked.

Enforced by: .gitignore + CI secret scanning. Rationale: the repository is published. Scrubbing history later is unreliable; never committing is the only guarantee that holds.

C2. Provider access only through the registry ​

In each codebase, one registry module is the only one that imports a model provider's package or resolves a model from its provider:model string: src/agent/registry.ts in TypeScript and agent/registry.py in the Python service, so the rule holds in both while both run (ADR-0018). Everything else depends on the AgentRunner port. The active model is resolved at runtime from configuration.

Enforced by: a lint rule in each codebase + a unit test asserting a model swap changes no source. Rationale: model-agnosticism is a hard requirement, not an aspiration. A single direct import silently destroys it.

C3. Every outbound message is schema-validated ​

No string reaches a customer without passing the AgentReply schema and the active channel's capability constraints. Model output is untrusted input.

Enforced by: guardrails.ts + renderer unit tests.

C4. User text is untrusted data, never instruction ​

Inbound message text is fenced and labelled untrusted in the prompt. The catalog and system rules are injected server-side and never echoed back. No inbound field may alter tool availability, model selection, or system instructions.

C5. No PII in logs or traces ​

Phone numbers, names, emails, and full message bodies are redacted at the logger, not at the call site. A new log statement cannot opt out.

Enforced by: the logger's redaction config in each codebase + a test asserting known PII shapes are scrubbed.

C6. Fail closed, toward a human ​

When the agent is uncertain, over budget, rate-limited, or erroring, it escalates to a human. It never guesses at a price, a date, or a policy. A customer waiting for a person is an acceptable outcome; a customer quoted an invented price is not.

C7. The 10-second budget is a hard external constraint ​

ManyChat terminates external requests at 10s. Any code path on the inbound request may not exceed the budget in 002-channel-contract.md. Work that cannot finish in time moves to the outbox, never blocks the response.

C8. Tests assert against specs, not implementation ​

Behavioral tests cite the spec clause they enforce. A spec change that breaks a test is a real finding, not a test to update mechanically.

C9. English in the repository, no copy in source ​

Everything committed is in English. Beyond that, no customer-facing natural language lives in source at all - acknowledgements, escalation messages and persona text belong to the tenant, in gitignored configuration.

Enforced by: .gitignore for tenant config, review and a stopword check for the rest. Rationale: hardcoded copy limits the product to one linguistic market regardless of which language is hardcoded. See 005-language.md.