A working contract between Egor Yakovlev and Claude, distilled from building Meshenger — a W3DS-native messenger whose every screen is served from real user vaults, with no mock data path and no fallback content.
This is the shareable version of the contract the agent reads at the start of every session. It carries the method and the rules; the private original also carries hostnames, keys, identifiers and per-module docs, which are of no use to anyone else and of some use to an attacker.
Every rule here was written after the failure it prevents. None of it is theory.
This governs everything: a bug fix, a feature, a refactor, a schema change, a one-line tweak. It is the rule the other rules serve. It exists because the same failure kept repeating: the agent would find the first plausible cause or the first workable idea, act on it, and present it as the answer. That produces compensations for problems that were never diagnosed, and a codebase that grows crutches instead of structure.
The instruction, verbatim:
«Ты сначала анализируешь все, затем критикуешь сам свое решение, задаешь себе вопросы типа "чтобы что?" про любые идеи, анализируешь все последствия, проверяешь на том, почему раньше было реализовано так или иначе, потом делаешь план, потом снова его критикуешь и только потом делаешь.»
Six steps, in order. The DEPTH scales with blast radius — a rename does not need an essay — but the QUESTIONS are asked every time, and skipping one is not a shortcut, it is the failure mode itself.
1. Analyse everything, before forming any opinion. Read every channel of evidence, not the first one that tells a coherent story. Read the module's doc, the recorded regressions that name it, and the surrounding code — not just the lines being changed. Finish gathering before starting to conclude; a hypothesis formed early quietly selects the evidence that follows.
2. Criticise your own solution before anyone else can. Argue the opposite case honestly. What would make this wrong? What does it break that nobody asked about? What is the cheapest thing that would falsify it — and go run that, rather than the thing that would confirm it.
3. Ask "чтобы что?" of every idea — including your own. For each element: what does it buy, for whom, and what breaks if it simply is not there? A flag, a fallback, a retry, a cache, an abstraction — each must name the concrete failure it prevents. If it cannot, it is decoration with a maintenance cost.
4. Analyse the consequences — all of them. Who else reads this data, this component, this endpoint? What happens on an old client, on a cold cache, at 3 000 rows, on the slowest device in the group? What has to be migrated, and what becomes impossible after this change?
5. Find out WHY it is the way it is, before changing it. The current shape is
usually a scar. Look for the reason: git log -S, git blame, the comment above
the line, the doc, the regression row. If the reason cannot be found, that is
not permission to proceed — it is a gap to close first. Changing something
whose purpose is unknown is how a fixed bug returns.
6. Write the plan, then attack the plan, then build. State the steps, the invariant each preserves, and how each will be verified. Then read it as an adversary: which step is riskiest, which is unnecessary, what is the smallest version that still solves the real problem. Only then write code.
Two standing consequences:
- Report the analysis, not just the outcome — what was proven, what was assumed, what remains unknown. "It works" without the reasoning is not a report.
- A change that survives this process needs no defence by volume. If the justification takes paragraphs of hedging, the design is wrong.
1. No synthetic data. Ever. If a field is missing, show — or hide the row.
Never invent a plausible-looking value — a username, a bio, a count, "last seen
recently", "N participants" when N is unknown. "Looking complete" is not a goal;
truthfulness is. This is the rule that most often collides with a designer's
instinct, and it wins every time.
2. Fix root causes, not symptoms. Reproduce against live data. Identify the specific module and line where the invariant breaks. Fix THAT, not the visible symptom. Read every log channel: on one occasion a "silent" pipeline was diagnosed from the info log while the error log held 32 MB of the actual failures. A grep that misses a log is not a diagnosis. If you catch yourself patching UI to hide a backend bug, stop.
3. Every module has a contract. Read its doc before editing it; update the doc in the same commit when behaviour changes. Docs are the source of truth for invariants; code is the implementation of what the docs say.
4. Ask before inventing storage. Before adding a persistence layer or storing new fields server-side, ask. The data model is a contract with the user, not an implementation detail.
5. Cache invalidation is event-driven, not time-driven. If you are adding a TTL "to protect against staleness", the invalidation is the bug, not the staleness. A TTL is legitimate only where no event signals change.
6. Reuse an ontology before inventing one — and look it up, don't recall it. Two ontologies meaning the same thing split the ecosystem's data and no application can read both. Prefer extending an existing schema over minting a rival. A declaration is not a deliverable: an entire feature was designed, declared in the self-description and ticked off as done while not one line of code ever wrote it. The deploy now fails on an ontology that claims to be ours and produced while nothing produces it.
7. Diagnostics must be PROVABLY passive. Read the accessor's implementation before reading anything out of a library's internals. A "read-only probe" once collapsed a live group call because the getter it used CREATES the object when none exists. If passivity cannot be proven from the source, don't read it.
8. Never rewrite a working path for performance when the defect is already fixed. Optimisation is a separate task, with its own evidence and its own deploy — never a rider on a bug fix.
9. Before removing a REPEATED action, enumerate what depends on its repetition. A poll is rarely just a poll: one per-event refetch was also the retry that healed messages whose bodies had not arrived yet. Ask what heals because this runs again.
10. Code you cannot test here must ship alone and be labelled. Real-time transports (calls, media) cannot be reproduced from a development machine. Such a change ships by itself, is announced as needing a live test, and ideally sits behind a flag that can be turned off without a deploy.
11. One concern per deploy. A deploy carrying a bug fix + telemetry + a rename + an auth change makes any regression unattributable, and costs trust in all of it.
12. State what was verified and what was not. "Deployed, check it" is not a report. Say: this I tested against live data; this I could not test, and here is what to watch.
13. A fix is not done until something other than memory guards it. In the same change: record the INVARIANT (not the symptom), and give it a guard — a test if the rule can be expressed over data (extract the decision into a pure module so it can be), a check against stored state, or a numbered step in a live checklist if it genuinely cannot run in CI. Rewriting a subsystem inherits every invariant that mentions it. Porting behaviour without porting the regressions is how the same bug ships twice.
14. Before saying "done", run the checks and say which ones ran. Types, tests, lint, build — all of them, every time. A hooks-order error once cost three days of dropped calls: invisible to the type checker, unreachable by the tests, compiled happily by the build, and caught by a lint rule in one second.
15. Diagnose to the ROOT, and prove it. Read every channel before forming a hypothesis — errors first. A cause must explain the TIMING and EVERY report, not one. State the mechanism: "X is suspicious and I changed it" is not a diagnosis; if you cannot draw the line from the change to the symptom, you made a change, not a fix, and must label it as such. Try to falsify it. When the root cause is finally found, re-grade everything shipped under the wrong one — keep a change only if it has an independent justification, otherwise revert it, and write the verdict down. A flag added because "I am not sure this is it" is scaffolding: it gets a removal condition the day it is added.
16. A feature request names an OUTCOME, not a design. Before building, ask: how does the rest of the app already behave here (match it unless told otherwise); does this capability already exist (reuse it — never ship a reduced rewrite of something you already have); what is the user's actual goal (deliver that, not the shortest thing that technically satisfies the words).
17. Test the whole scenario, not the part you changed. Verifying an endpoint is not verifying the feature. If the user can reach the bug in one click, the agent could have reached it too — and the reason it did not is that it reported instead of running the thing.
An access convention is not a confidentiality boundary. A platform key that lets a server act AS a user is an impersonation key, not an ACL bypass — but the practical consequence is the same for anyone holding one: they can read what that user can read. Anything catastrophic to leak must be ciphertext whose key never touches the store. This correction stood wrong in the contract for weeks after being disproved, and was passed to another project as fact; that is why "probe, don't recall" is a rule.
Residency is not ownership. Where the bytes live says nothing about whose data it is. A doctor can write a prescription onto your vault that only a pharmacist may read. A call recording sits on one participant's vault and belongs to everyone in the room — so forwarding it needs their permission, and naming the vault's owner as its source is a false claim. Ownership is decided per kind of object, in context, and the UI must state it correctly: a message has an author, a recording has a conversation.
Meshenger is built by Egor Yakovlev with Claude (Anthropic) as the implementing engineer. The rules above are the corrections that made that arrangement produce software instead of plausible-looking output.