Thursday, August 27, 2026

Embabel DIF — fold intent into typed state before the agent writes code

Most coding agents reconstruct the architecture from scratch every run. They reread the repo, guess what must stay true, then edit files. Tomorrow the same prompt can invent a slightly different “must not change.” embabel-dif is a prototype that moves that knowledge out of the prompt and onto Embabel’s blackboard.

It is not Merly’s proprietary Deterministic Intent Folding algorithm. It is an experiment inspired by the public DIF idea: interpret with an LLM, then fold accepted facts into a stable semantic model that a deterministic planner can actually use.

LLM      = probabilistic interpreter / generator
DIF      = semantic intent substrate
Embabel  = deterministic planner / orchestrator
Verifier = deterministic acceptance boundary

The problem the loop keeps hiding

A typical agent loop is: request → LLM reads the repo → LLM infers intent → LLM edits code → tests. The semantic model is implicit. On the next run the model may reconstruct a different answer to:

  • why a component exists
  • which behavior is architectural, not incidental
  • which tests express real intent
  • which earlier decisions still constrain this change

The prototype splits a change into three layers and tries to keep the first two independent of whatever syntax the LLM eventually writes:

INTENT      What should be true?
            Refresh tokens rotate. Google login stays.

SEMANTICS   What properties follow?
            A used refresh token cannot authenticate again.
            sessionToken remains in the JWT.

SYNTAX      Which files implement that?
            TokenService.java, a migration, an IT.

What “folding” means here

Folding is not a novel math algorithm. After candidate facts are accepted, deterministic rules normalize, deduplicate, derive invariants, relate them to evidence, and refuse to plan if two required intents conflict.

Four observations about sessionToken — a test, a mobile reader, a commit, and the user’s “do not change login” — fold into one invariant: preserve the sessionToken claim. Unfold it later and you still have the explanation.

Design rule. Use stochastic reasoning to discover knowledge. Once a fact is accepted, fold it into typed intents, invariants, relations, and checks that Embabel can plan over.

Milestone 1 is already a GOAP path

The first milestone stops before anyone writes code. The question is: can probabilistically extracted intent become stable typed state that Embabel’s planner can reason over?

ChangeRequest
      │  interpret  (fixture, or LLM for unknown wording)
      ▼
CandidateIntent
      │  fold       (deterministic)
      ▼
SemanticModel
      │  analyze repo evidence
      ▼
RepositoryAnalysis
      │  derive missing obligations
      ▼
VerificationPlan

DifEmbabelAgent is a normal annotation agent. Interpret, fold, and analyze are @Action methods. Planning verification is the goal, but only when noBlockingIntentConflicts is true. GOAP picks the sequence from types, not from a hardcoded script.

The known refresh-token wording is handled by FixtureIntentInterpreter, so ./mvnw test and the shell fold command do not need an API key. Other requests go through LlmIntentInterpreter.

./mvnw test
./scripts/shell.sh

fold            # Embabel invocation → VerificationPlan
fold-local      # folder only, no planner
intent-diff     # semantic git-diff
x "Add refresh-token rotation without changing existing login behavior."

The first scenario: rotate, do not break login

The request is deliberately narrow:

Add refresh-token rotation without changing existing login behavior.

The fixture already knows Google and Apple OAuth exist, authorization-code flow is in use, JWTs carry sessionToken, login ITs exist, and refresh tokens are still reusable. The folder turns that into five intents and the matching invariants: consumed tokens cannot be reused; Google, Apple, and authorization-code clients still authenticate; sessionToken stays compatible.

Absence reasoning is the interesting extra. Rotation implies a token family id, consumed-token state, replay detection, and a rotation integration test. The fixture repo already has the first three. The deriver emits one MissingObligation: rotation integration test. That is a typed fact the planner can act on later — not a sentence buried in a prompt.

If someone also requires “existing clients must reuse the same refresh token indefinitely,” ConflictDetector marks the pair mutually exclusive. Embabel will not plan implementation while that conflict is unresolved.

A semantic diff is more useful than a file diff

Before vs desired for this scenario is a property snapshot, not a patch:

SEMANTIC DIFF
+ refresh-token.rotates=true
- refresh-token.reusable=true

UNCHANGED:
= jwt.claim.sessionToken=present
= provider.APPLE=present
= provider.GOOGLE=present

RESULT: PASS

The verifier’s job is not “LLM, does this look correct?” It asks whether required properties were preserved. Code and test checks are still stubbed; the semantic diff already works.

What is stubbed on purpose

Phase Status
1. Typed model + fold Working
2. Embabel GOAP → VerificationPlan Working (fixture path, no LLM)
3. LLM code generation Stub (LaterPhaseActions)
4. Deterministic verification Semantic diff works; code/test checks stubbed
5. Repair from VerificationFailure Types only
6. Persistent .dif/ memory Seed YAML + write-snapshot stub

Phase 3–5 stay off the GOAP graph on purpose so milestone 1 cannot accidentally wander into “generate a PR.” Seed files under .dif/intents, invariants, evidence, and relations are versioned with the repo — the start of architectural memory that an agent can inspect instead of reconstructing.

Why Embabel is the right control plane

Embabel does not duplicate the semantic layer. It consumes typed objects — ChangeRequest, CandidateIntent, SemanticModel, MissingObligation, later VerificationFailure — and decides the next legal action. That is the same type-is-wiring model as the rest of the framework: return a new object, flip a condition, replan.

The long-term bet is domain maturity. While the domain is unknown, an LLM explores. Once a fact is accepted, it should migrate into typed knowledge, deterministic planning, and deterministic verification. Git already stores what changed. A DIF-style layer stores why, what must remain true, and what depends on it.

Source: github.com/jmjava/embabel-dif
Spec: DIF_EMBABEL_PROTOTYPE.md
Related: Embabel Agent Framework · embabel-v1-learning · refresh-token-research

No comments: