iyi

Agent experience (AX)

Your agent reads the interface, not the repository.

When it's wrong and the compiler knows the fix, the verdict carries it. iyi holds this by language rule: a module compiles against what its dependencies declare, so a model can read an interface without the repository.

same task, both armsmeasured on webapp.iyi, calc.iyi
Reading the sourcesevery body the edit depends on
100%of the sources' size
With iyiiyi mod context: declarations only
52–59%of the sources' size

Same fix. 31–43% fewer prompt tokens, for no fewer rounds.The recorded session ↓

Same four verbs in both lanes. Watch what each one has to read.

A replay of the measured result (method below). Code shown is illustrative; the recorded session follows.

recorded with iyi 0.17.0 · fe3db8354

The loop, recorded.

4 files, one mistake, the compiler's own verbs. The same verbs answer over MCP, byte for byte.

01 · Ground the edit

records/agent/app.iyi · 11 lines
1# The file being edited, in the state an agent meets it: one call misspelled.
2# The compiler knows the name it meant, so `iyi check -f json` carries a
3# `suggested_edit` with the exact replacement span and `iyi fix` applies it.
4module app
5
6import app/greeter::*
7
8pub def main : Int32
9 puts polit("Türkiye")
10 0
11end
$ iyi mod context --json app.iyithe grounding packstdoutexit 0
importapp/greeter · interface f2f9576b
defpolite(name : String) : String
defshout(name : String) : String
traitGreet · abstract def greet : String
{"file":"agent/app.iyi","imports":[{"import":"app/greeter","api":{"module":"app/greeter","interface_hash":"f2f9576ba7e3996e204d5c93ccf5f42d","functions":[{"name":"polite","parameters":["name : String"],"returns":"String","doc":"The exported surface carries its doc comment, so the grounding pack an agent\nreads says what the function is for and not only what it takes.","rendered":"def polite(name : String) : String"},{"name":"shout","parameters":["name : String"],"returns":"String","rendered":"def shout(name : String) : String"}],"types":[{"name":"Greet","kind":"trait","visibility":"pub","methods":[{"name":"greet","parameters":[],"returns":"String","required":true,"rendered":"abstract def greet : String"}]}],"impls":[]}}]}

02 · Ask for the verdict

records/agent/app.iyi · 11 lines
1# The file being edited, in the state an agent meets it: one call misspelled.
2# The compiler knows the name it meant, so `iyi check -f json` carries a
3# `suggested_edit` with the exact replacement span and `iyi fix` applies it.
4module app
5
6import app/greeter::*
7
8pub def main : Int32
9 puts polit("Türkiye")
10 0
11end
$ iyi check -f json app.iyithe compiler's verdictstderrexit 1
8:1instantiating 'App.main()'
9:8 errorundefined method 'polit' for App:Module
fixreplace 5 characters at 9:8 with 'polite'
[{"file":"agent/app.iyi","line":8,"column":1,"size":4,"message":"instantiating 'App.main()'"},{"file":"agent/app.iyi","line":9,"column":8,"size":5,"message":"undefined method 'polit' for App:Module\nDid you mean 'polite'?","suggested_edit":{"file":"agent/app.iyi","line":9,"column":8,"size":5,"replacement":"polite"}}]

03 · Apply it, and confirm

records/agent/app.iyi · 11 lines
1# The file being edited, in the state an agent meets it: one call misspelled.
2# The compiler knows the name it meant, so `iyi check -f json` carries a
3# `suggested_edit` with the exact replacement span and `iyi fix` applies it.
4module app
5
6import app/greeter::*
7
8pub def main : Int32
9 puts polite("Türkiye")
10 0
11end
$ iyi fix app.iyithe repairstdoutexit 0
fixedfixed app.iyi:9:8: `polit` -> `polite`
fixed app.iyi:9:8: `polit` -> `polite`
$ iyi check app.iyigreen, and silent about itstdoutexit 0
exit 0No output. The verdict is the exit code.
(no output)

04 · Re-run only what the change reaches

records/agent/app.iyi · 11 lines
1# The file being edited, in the state an agent meets it: one call misspelled.
2# The compiler knows the name it meant, so `iyi check -f json` carries a
3# `suggested_edit` with the exact replacement span and `iyi fix` applies it.
4module app
5
6import app/greeter::*
7
8pub def main : Int32
9 puts polite("Türkiye")
10 0
11end
$ iyi test --affected app/greeter.iyia module the test importsstdoutexit 0
ran1 passed, 0 failed
1 passed, 0 failed
$ iyi test --affected other.iyia module nothing importsstdoutexit 0
skipped0 to run, 1 skipped: no test's imports reach the change
0 to run, 1 skipped: no test's imports reach the change
Read as
rendered for you

The servers are in the binary.

There is nothing to install and no second implementation to keep in step. iyi lsp speaks the Language Server Protocol to an editor and iyi mcp serves check, fix, context, test and doc as Model Context Protocol tools to a harness, both over stdio, and both are shells around the same front end a build uses. So there is no agent mode and no forked behavior: same verb, same file, two doors.

measured · iyi check -f json in a shell, against the check tool over MCP stdio

Byte for byte the same answer through both doors.

Compared on every recording, and the record is refused when they differ.

identical: true · bytes: 318

Does the edit reach anyone?

Pick an edit and see who has to rebuild.

Change a body

$ iyi mod diff before/app/greeter.iyimod after-body/app/greeter.iyimod
module          app/greeter
interface       unchanged  what a consumer type-checks against
implementation  unchanged  the bodies a consumer compiles: macros, generics, the initialiser
source          changed    the file
dependencies    unchanged  what this module was compiled against
imports app/greeter
appup to date
app/app_testup to date
Consumers do not have to be rebuilt: what they compile against is the same.iyi mod diff · interface unchanged

Change a declaration

$ iyi mod diff before/app/greeter.iyimod after-api/app/greeter.iyimod
module          app/greeter
interface       changed    what a consumer type-checks against
implementation  unchanged  the bodies a consumer compiles: macros, generics, the initialiser
source          changed    the file
dependencies    unchanged  what this module was compiled against

  gone   def shout(name : String) : String
  new    def shout(name : String, times : Int32) : String
imports app/greeter
apprebuild
app/app_testrebuild
Consumers have to be rebuilt: what they compile against moved.iyi mod diff · interface changed

How the numbers were measured.

On four modules a pack is no smaller than the sources it stands in for. So the gate that runs both arms measures it on two of the repository's samples whose imports carry bodies: webapp.iyi, against kemal/dsl and kemal/router, and calc.iyi, against calc/lexer, calc/parser, and calc/ast. The prompt tokens are from the webapp.iyi task, written once against the pack and once against the sources.

The project is records/agent/ in this site's repository. Install the compiler and the frames on this page are what you will see.

bench/site_facts.pya146f81de
samples
webapp.iyi · calc.iyi
token task
webapp.iyi
runs
twenty-six
arm A
sources
arm B
iyi mod context
pack size
52–59% of sources
rounds to green (the model's)
tied three · lost three
prompt tokens−31 to −43%