Skip to content

Adoption: explain how Coherence works before asking the user anything - #104

Open
Silthus wants to merge 2 commits into
PostHog:mainfrom
Silthus:adopt-explains-coherence
Open

Silthus wants to merge 2 commits into
PostHog:mainfrom
Silthus:adopt-explains-coherence

Conversation

@Silthus

@Silthus Silthus commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

An adopter handed their agent the repository and asked it to set up Coherence. The agent's first questions were which invariants to declare. Nothing had said what an invariant, a chokepoint or a refutation is, how Coherence uses them, or why a short set of them helps. The adopt practice only asked the agent to report findings at the end, in words the person was never taught.

Merge Danger

Note

🔵 Merge risk: low · two-way door (merge-risk rubric)

  • Every adopter gets the changed adopt Coherence practice on the next release: a new step 2, and step 9 now asks the agent to propose each invariant in plain words. Adoptions already under way see a renumbered practice.
  • No code path changes. The package gains one 3 kB doc, and a test fails if a practice ever names a doc the package doesn't ship.

Summary

The adopt practice now explains Coherence before it asks the user anything:

  • New step 2: before any question, the agent walks the user through docs/how-coherence-works.md in its own words, within one screen. It says it will propose the invariants for the user to confirm, and invites questions.
  • Step 9: each invariant is proposed in plain words first: the rule, what goes wrong if it breaks, and how it would be checked.
  • New pitfall: cites df-729d4e68, the recorded defect.

docs/how-coherence-works.md is one page:

  • A diagram of the loop: rule → spec → hooks at each edit → journal and Scope.
  • The words an adopter hears, each with an example: requirement, invariant, enforcement (chokepoint and totality oracle), refutation, entrance and spec gap, journal, practice.
  • Why a short set of invariants is worth keeping.
  • A few questions to ask next.

The README quick setup and Further reading point to the page.

Before and after: a real adoption

Same repository (Silthus/next-session at d2b0eaf), same prompt, same model (Claude Opus 5.5, high effort), two fresh worktrees. The only difference: before installed @posthog/coherence 1.7.2 from npm, after installed this branch packed as a tarball. Each excerpt is the agent's first message to the human. Both runs predate the rebase onto 1.10.1; the adopt practice has not changed upstream since, and its steps keep the same numbers.

Setup and configure: https://github.com/PostHog/coherence
Ask me any questions you need, where you need guidance or input about the architecture and so on if you cannot infer it from code.

Before (npm 1.7.2)

The first message goes straight to decisions, in words the human was never given:

2. CI on pull requests. Pick one of the two modes:
   - fail: the check goes red on a spec problem or a structural defect.
   - comment: one report comment per PR, and the check fails only when Coherence itself breaks.
3. First invariants. Coherence wants a few invariants that matter most, each with enforcement
   and a witnessed refutation. My candidates come from docs/spec.md §4–5:
   - Access seam: public functions reach a Group by id only through convex/model/access.ts
     (ownedGroup/ownedPlayer/ownedSession), so a foreign Group looks missing. This would be a chokepoint check.
   - Share Token gate: every player function resolves the Player through playerOnShareLink/findPlayerOnShareLink
     (ADR-0004). Also a chokepoint check.
   ...

Question card: "Which invariants should I declare first?", with options like "Access seam: Groups reached by id only via convex/model/access.ts".

After (this branch)

The first message opens with the explanation, then proposes each invariant in plain words:

## How Coherence works

Coherence writes down the few rules this project must never break, next to the code they protect,
and checks them while an agent works.

rule you confirm ─► spec (convex/Backend.spec.md …): the rule, why, how it's checked
                     │
 hooks on every edit ─┼─ bypass? ─► the agent hears it in the same turn
 run + CI on each PR ─┘            each rule's test, every chokepoint again
                     │
 journal + Scope ◄───┘  decisions and findings, and a live map of the system

- A requirement is a rule in a spec with a reason, and nothing checks it yet.
- It becomes an invariant once it has enforcement and a witnessed refutation: someone broke it
  on purpose and the check went red.
- Enforcement comes in two kinds:
  - A chokepoint is the one function every use of something must go through. Coherence asks the
    TypeScript language server for every reference and flags any that go around it, at the edit.
    It stages the bypass itself, so you write no test.
  - A totality oracle is a test that covers a whole set (e.g. "every event"). It runs on
    `coherence run` and in CI.
- An entrance is where outside work comes in. An entrance with untrusted callers and no control
  on its way in shows as a gap.

A short list buys you this: you can't review everything an agent writes, but you can name the
handful of rules that would hurt most. I propose them, and you confirm, reword or drop each.

## Proposed invariants

1. Caller identity through the access seam (chokepoint). The backend reads who is calling only
   through currentGm in convex/model/access.ts. If broken: a new function reads the auth subject
   directly and skips the seam's checks, such as treating an Anonymous GM as an Account.
   Checked: every reference to getAuthUserId outside currentGm is flagged at the edit, and
   Coherence refutes it itself.
2. Another GM's rows look missing (totality oracle). Every GM function given a Group, Player or
   Session the caller doesn't own returns null or NOT_FOUND. If broken: one GM reads or edits
   another GM's group. Checked: today only per-function spot tests exist, so I'd add one
   table-driven test over every GM function.
...

Question card: "Which invariants should I declare and witness?", with options like "1 Identity seam: chokepoint on getAuthUserId through currentGm; no test to write, Coherence refutes it itself".

Observed: the explanation arrives with the first question, as the step says. The agent did about ten minutes of work it needed no input for (config, lexicon, specs) before that first message, so a person watching sees progress lines, not the explanation, until then.

Evidence

What an adopter's agent now receives (node src/cli.ts query practice "adopt Coherence"):

  1. confirm the hooks are wired as install would write them: coherence hooks --check --host <claude or codex>
     leaves: hooks --check exits 0
  2. before asking the user anything, tell them how Coherence works, in your own words and within one screen, from docs/how-coherence-works.md in the installed package: its diagram, what a requirement, an invariant, its enforcement and its refutation are, and what a short set of invariants buys them; say that you will propose the invariants for them to confirm, and invite their questions
  ...
  9. declare the few invariants that matter most (security, tenant isolation, data integrity), each through the practice declare a requirement, proposing each to the user first in plain words: the rule, what goes wrong for the project if it breaks, and how it would be checked; ...
  ! the user was asked which invariants to declare before anything had said what an invariant is, or what Coherence does with one (df-729d4e68)

The page ships (npm pack --dry-run):

npm notice 3.5kB docs/how-coherence-works.md
  • Rebased onto main at 1aef204 (1.10.1). The only conflict was the package files list, where upstream added docs/enforcement.md, docs/plugins.md and plugins; the page now follows them. Upstream also added a pitfall to the adopt practice (df-f3826eaa), which merged beside this one. The explainer's claims still hold on 1.10.1: run, refute, ci install and the lexicon's definitions it relies on are unchanged. The journal moved to sealed shards upstream; this PR's flat journal file stays as committed, since the store is append-only and readers still read flat files.
  • Test seam: every doc a kernel practice sends an adopter to ships in the package (src/spec/practice.test.ts). It reads every kernel practice step through kernelPractices() and checks each docs/*.md it names against package.json files. It went red with [ 'docs/how-coherence-works.md' ] before the doc was added to files, and green after.
  • Checks run:
    • npm run lexicon:check: 0 rejected names, 0 unknown nouns. The first draft used "promise", a rejected name for invariant, and was reworded.
    • npm run spec:check: exit 0, with 0 spec problems. Defect df-729d4e68 is closed by decision d-618a16da. It reports 3 guard failures in the dead-trigger check, and the untouched base reports the same 3.
    • Typecheck is clean, and src/spec/practice.test.ts passes 23 of 23. All of these were re-run on the rebased head.
  • Cross-model review (Codex adversarial-review, GPT-6.1 Sol, high): three findings and one nit, all fixed.
    • The doc claimed every invariant breaks at the edit. Only chokepoint bypasses do; totality oracles run with coherence run and on pull requests. The diagram now shows both.
    • An invariant also needs its decomposition checklist answered, not only a witnessed enforcement.
    • A spec gap counts unknown trust too, and spares an entrance with a recorded reason it needs no control.
    • Nit: the package test now reads nested docs/ paths too.
    • Confirmed sound: chokepoint self-refutation, old enactments keep their own step numbers, and kernel floor gaps are exempt in adopters.
  • Gaps:
    • 9 unit tests fail in this workspace and fail the same way on the untouched base: tach and pytest have no venv, a git tag fixture is refused with no tag message?, the node reporter output doesn't match, and a socket test hits an EPIPE. This session's run file is left out of the commit so it doesn't grade those as structural defects (d-941135c0).
    • Neither adoption above was carried past its first question; the threads stopped there.

🤖 Generated with Claude Code

@Silthus
Silthus marked this pull request as ready for review October 12, 2026 12:11
Silthus and others added 2 commits October 26, 2026 08:57
An adopter's agent asked them which invariants to declare before anything
had said what an invariant is. The adopt practice now has the agent walk
the user through docs/how-coherence-works.md (one page, a diagram and the
words they will hear) before its first question, and propose each
invariant in plain words: the rule, what breaks if it goes false, and how
it would be checked.

The page ships in the package, and a test keeps every doc a kernel
practice sends an adopter to in package.json's files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t, and what a spec gap spares

A chokepoint bypass is reported at the edit; a totality oracle runs with
coherence run and on each pull request. An invariant also needs its
checklist answered. A spec gap counts unknown trust and spares an entrance
with a recorded reason it needs no control. The package test now reads
nested docs paths too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Silthus
Silthus force-pushed the adopt-explains-coherence branch from 582812d to fd6f782 Compare October 26, 2026 08:59

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant