Satsuma owns what it builds, and fixes it without touching the money

Eleven runs. Every contract Satsuma deployed became a product it owns: a record on its chain, a repository, a list of things that must stay true, and a review of its own code by a model told to attack it. Those reviews found real defects, twenty of them, and Satsuma fixed nine by vote, three different ways: a redeploy for a contract holding nothing, a migration that moved ten thousand tokens to a successor, and in the last run an upgrade in place of the contract holding the position. In five of the last seven runs nobody touched anything.

The previous experiment ended with Satsuma proposing what it needed, building it, running it under a policy it had written, and reviewing that policy every five minutes. It also ended with something nobody was looking at. The contracts it had built were deployed and then forgotten. An exchange and a mandate account sat on two chains holding Satsuma’s money, and nothing in the loop would ever read their code again, ask whether they still did what they promised, or fix them if they did not.

Anyone who has shipped software knows that this is where the work begins. A contract that holds funds is attacked, drifts from its specification, or turns out to have been wrong from the start. Someone has to own it: know what it must keep true, notice when it stops, judge how bad that is, stop it if it is dangerous, and fix it, without losing what it holds, in a way others can check.

This experiment asks whether Satsuma can be that someone. The objective and the world are those of the first two experiments. What changed is that every contract Satsuma deploys now becomes a product it owns, and the run is scored on what it does with that ownership.

Eleven runs over five days. Every run but one built the same two products from scratch, and a review of their code by a model told to find what was wrong filed twenty issues against them. Satsuma fixed nine, each by a vote, and did it three different ways depending on what the broken contract held. The last run did the hardest of the three: it changed the code of the contract holding its position without moving the position, and nobody touched anything from start to finish.

Runs
11
same objective; the loop changed between them
Issues filed by its reviews
20
9 fixed and resolved by vote
Ways a fix landed
3
redeploy, migration, upgrade in place
Human interventions
0
in runs 5, 7, 8, 9 and 11

The setting, briefly

Readers of the first two experiments can skip this section.

Satsuma is an attempt at a fully autonomous AI. Language models make its decisions, and a blockchain of its own holds its identity, its treasury and the rules it must act within. It is given an objective with measurable key results, written into a on its chain, and everything else has to come out of its own process: proposals, votes, builds, reviews, deployments, and only then action. The objective is unchanged:

Keep Satsuma’s holdings on tangerine balanced by swapping on its DEX, with no human in the transaction path, and never exceed the mandate.

tangerine is a second chain with its own token, TANG. The two chains exchange proven messages over , carried by a and checked by the receiving chain itself. Satsuma holds 10,000 tUSD and 10,000 TANG there, and to begin with has no exchange on tangerine and nothing that can act for it.

In every run it builds two things. An exchange, an on tangerine, which a scripted market then trades on. And a mandate account: a contract on Satsuma’s chain that one key may ask to swap, which checks each request against limits and refuses anything outside them. An accepted request crosses to tangerine as a proven message, a keyless there makes the swap, and a receipt crosses back. The remote account is the contract that actually holds the tokens. No private key for it exists; only the mandate account’s instructions can move what it holds.

Then an operation trades through the mandate under a policy Satsuma wrote and voted, reviewed every five minutes, as in the previous experiment. The four key results are the same: swaps executed and proven (10), requests refused by the mandate (1), failures after which the books still balance (2), and holdings between 45% and 55% tUSD by value at the close.

The brief gained one paragraph. Everything Satsuma builds must come with an operating manual, its administrative functions must be held by the chain’s or not exist, and it must say how each contract can be changed after deployment.

What owning means here

The design is recorded as two architecture decisions, written before the runs that tested them. The first says what a product is.

When a deployed contract is on chain, it becomes a product: a record in a registry, with a state, Operating, Halted or Sunset, and a private repository of its own under Satsuma’s GitHub organisation, carrying the files any serious open-source project carries: a security policy, a changelog, issue forms, a disclosure that the sole contributor is a machine and that the ’s vote stands in for human review.

Each product has a manifest: a document naming the that must hold and the signals worth watching. A model drafts it from the deployed code and the builder’s manual; the council adopts it by vote, with a maintenance reserve set aside from the build’s budget to pay for fixes. From then on the invariants are read from the chain every cycle, and a reading outside its bounds halts the product without asking anyone.

Signals go to an assessor: a model role whose only job is to say whether something that looks wrong is a defect in the product, an exploit in progress, a market condition, or nothing. A market condition is the operation’s problem and goes to its reviews. A defect becomes an issue, filed on chain by with a readable copy in the repository, and given a severity on the scale bug bounty programs use: S1, theft or permanent loss of funds; S2, funds frozen for a time; S3, denial of service or ; S4, wrong behaviour that loses nothing. Some floors do not depend on the model: a breached invariant is S1, whatever the assessor thinks. An S1 halts the product: every operation using it pauses, and no new build proposal is accepted anywhere in the loop until it is resolved. Resuming is a vote.

What a signal or a finding becomes. The assessor is the one judgment call; the severity ladder below it is fixed, and only S1 does anything on its own.

Besides the signals, a product’s code is read. The assessor’s adversarial review takes the deployed sources and reads them as an attacker would, file by file, and files what it finds with the same severities.

A fix is building, shortened. It is a proposal citing the issue, paid from the reserve without a funding vote, built from the deployed sources on a branch of the product’s repository, through the same gates as a first build, and landed by the same attestation vote that records any deployment. That vote also resolves the issue and resumes the product. The fix’s is merged only when the chain says the issue is resolved: the repository’s main branch is whatever is deployed, never ahead of it.

The ownership loop. The two highlighted steps are where a model's judgment enters; every other step is a vote or a fact read from the chain.

The second decision was forced by run 2, and it is the subject of most of this article. It says how a fix reaches a contract that holds money.

Run 1: it measured, and refused to file

The first run showed the loop closing without anything going wrong. Both products were opened on chain with their repositories. The assessor drafted two manifests, and here the brief’s paragraph had already changed what Satsuma built: told that administrative functions must be held by the timelock or not exist, the mandate’s builder made its caps, floor and timeouts compile-time constants, gave it a timelock-only pause, and wrote a manual listing fifteen invariants of its own. The assessor turned that manual into seven invariants that pin exactly those constants, and the council adopted both manifests with their reserves. Every cycle the invariants were read back, and read back exactly at the end: 1,000 tUSD per instruction, a 90% floor, a 90-second message timeout.

Then the began: a relayer outage, and the liquidity pull from the previous experiment, in which the is emptied under every swap in flight so that each one fails. Seventeen swaps failed in a row. The assessor was asked three times whether this was a defect, and three times said no. Its reason the second and third time:

The counters do not say why the swaps failed, so they cannot tell a defect from a market condition.

That is the right answer for a liquidity pull, reached for a partly wrong reason: the signal it was shown carried no failure reasons. It was given them before run 2. But the shape of the behaviour was what the design wanted. The reviews of the operation said amend the policy, twice, and the policy was amended. Ownership asked is the product defective and answered no. The two channels stayed separate, and nothing halted that should not have.

Run 2: the first real finding, and the wrong shape of fix

Run 2 added a drill: three swaps into the operation, the harness asks Satsuma to review both products. It did, and the review of the exchange found a known weakness of the Uniswap V2 router it had built: a function that lets anyone a signed permit and make the caller’s withdrawal . The assessor filed it as S4, a bug that loses nothing. Within the same cycle a fix was proposed, funded with 500 from the exchange’s reserve, approved, built from the deployed sources with the issue as its scope, and deployed. Only the router was redeployed; the factory and the pool, which hold the liquidity, were untouched. One vote recorded the new router, kept the factory where it was, and resolved the issue. The whole loop in the figure above had run once, by votes, on a real defect.

The same run produced a false halt. One of the exchange’s invariants pinned the factory’s address, and the loop’s measuring step pushed that address through a on its way to the chain. The registry saw a value that did not match and halted the exchange, as it must. The assessor was asked whether this was an exploit, and declined to file: “the expected address after losing precision in a 64-bit float.” After the push was made exact, the loop proposed a resume, the council passed it, and a second vote set the operation active again. The halt was wrong; the recovery was three votes and no hand.

And the run found the problem the rest of the experiment is about. The review of the mandate account filed an S4 as well (the proof-age defect that run 11 would fix nine runs later), and the loop treated it exactly as it had treated the router’s: propose a fix, rebuild, redeploy. The builder stopped and returned a gap report instead of a contract:

The position cannot be moved from the live RemoteAccount to a redeployed one.

It was right. For newcomers: a deployed cannot be edited. A redeploy is a new contract at a new address, and whatever the old one held stays in the old one. For the router, which holds nothing, that is fine. For the remote account, which holds Satsuma’s 10,000 tUSD and 10,000 TANG and moves them only on the mandate account’s say, a redeploy would have produced a new, empty account and left the money in the old one, now owned by nothing. The previous experiment’s loop would have shipped it. This builder noticed, and said so.

Why the same fix shape works for one contract and not the other. A redeploy is a new contract at a new address; whatever the old one held stays behind.

Three ways to fix a contract

The second architecture decision came out of that gap report. A product’s manual must now declare, for each contract, how it can change: stateless, so a fix is a redeploy; upgradeable, behind a the timelock owns, so a fix is a new implementation the proxy is pointed at; or migratable, exposing a function the timelock may call that hands its state to a successor, so a fix is a successor and a . A contract that declares none is fixed for good: a fix is a new product. The manifest’s validator refuses a manual that leaves a fund-holding contract stateless.

The three fix paths. Each ends in one vote, executed from the timelock, that records the deployment, lands the fix in the way the contract allows, resolves the issue and resumes the product.

The decision adds three rules that run 2 had also asked for. A fix builds from the product’s repository at the deployed commit, never from a build workspace, because the workspace predates the security fixes made before deploy. A Critical finding the assessor is unsure of is filed as an investigate issue rather than dropped. And every value pushed to the registry is an exact integer, read twice before a halt, so that a float can never halt a product again.

Runs 3 to 11 are the story of these paths being built and tested, one at a time.

Run 3: it stopped, and stayed stopped

Run 3 had the declarations and the rules, but not yet the migration or upgrade payloads: the loop could refuse an unsafe fix but could not yet make a safe one. The question was whether it would refuse.

The review of the mandate filed an S1: the price floor every swap was checked against came from a single of the pool’s reserves, which an attacker can skew in the same block, so an operator could be made to accept a swap at any price. The product halted, the operation paused, and intake froze. Then the loop looked for a fix and declined:

mandate-account, cross-chain-account hold state (… declare an upgrade path the loop cannot execute yet). It stays open until an upgrade or migration proposal answers it.

The reserve was not touched. The product stayed halted for the rest of the run, which is what the decision asked for, and what run 2 would not have done.

Two other things happened. A stray vote, a loop bug fixed during the run, set the operation active while its product was halted; the operator still did not run, because a halt outranks the operation’s state. And the council said no. The exchange’s manifest came to it twice with zero invariants, because the assessor had written the invariant calls in a shape the registry cannot encode, and the council refused both drafts 2 to 1. The third draft, with three invariants, was adopted 2 to 1. Nothing in the code decided that.

Runs 4 to 7: the money moves

Run 4 had the migration path. Its review filed another S1 on the mandate’s floor, and the product halted. The manifest said both of the mandate’s contracts were upgradeable, which is what the builder had claimed when it wrote the manual. Before building an upgrade, the loop checked: it read the storage slot where a proxy keeps its implementation address, and found no proxy at either contract. The builder’s claim was wrong. The loop asked the assessor what path the contract actually offered, and it found a migrate(address) function that only the timelock may call, verified in the deployed . A vote amended the manifest. Then a fix was proposed as a migration, built from the deployed sources, and deployed as a successor: a new mandate account and a new remote account.

The first deploy attempt failed, for a reason worth recording. Earlier in the same run, the exchange’s review had filed an S3 and the exchange’s router had been redeployed with a new contract. The successor’s deploy script compared the predecessor’s wrapped token with the exchange’s and refused to proceed. The failure went back to the builder as scope, and the second attempt deployed. A cross-product consequence surfaced as a deploy failure, not as silent corruption.

Then one vote did everything: it called migrate on the old mandate account with the successor as argument (the loop chose the argument by simulating the timelock’s call, because this build’s migrate takes the new mandate, not the new account), recorded six new addresses, resolved the S1 and resumed the product. On tangerine, the old remote account went from holding the position to holding nothing, and the new one held 9,847.8 tUSD and 10,964.4 TANG. The operator restarted on the successor, and the drills followed it there.

Run 4 took seven loop fixes and one edit of the daemon’s journal by hand to get there. Run 5, on the fixed loop, took none. The mandate’s builder declared its migration in the manifest itself this time, and said that the successor would carry the predecessor’s counters. The review filed the floor weakness as an investigate issue, S3, because the assessor’s confidence was under the Critical floor; the fix was proposed as a migration at once, the successor deployed on the first attempt, its pull request opened in the product’s repository, and one vote moved 12,783.4 tUSD and 9,235.6 TANG. The swap count read 28 before and 28 after.

Run 6 was the first run of the experiment in which the operation reached its objective under the full regime: a false halt (every invariant of the mandate’s manifest had been pinned to the wrong KPI by one, after two products were adopted in the same cycle) recovered by a re-pin vote and a resume vote; an S4 on the exchange fixed by redeploy; an S2 on the mandate, which an operator could use to block migrate by keeping an instruction pending forever, fixed by migration; a successor deploy that failed on the same wrapped-TANG mismatch as run 4 and recovered on its own this time; and ten swaps, holdings in band, the operation closed by its own policy. One stop and restart for a loop fix.

Run 7, with no intervention, migrated the mandate while the operation was trading through it. The operation had made 25 swaps on the predecessor; the migration landed; the reviews amended the policy to bind the operation to the successor; the operator restarted there and made nine more, counters carried, closing in band twice. 14,847.9 tUSD and 9,527.1 TANG on the new account.

RunWhat the mandate’s issue wasPathHoldings on the successor
4S1, floor from a skewable spot readingmigration, after a vote corrected “upgradeable”9,847.8 tUSD, 10,964.4 TANG
5S3 investigate, the same floormigration, declared in the manifest12,783.4 tUSD, 9,235.6 TANG
6S2, a pending instruction blocks migratemigration11,322.1 tUSD, 10,390.0 TANG
7S3 investigate, the same floormigration, mid-operation14,847.9 tUSD, 9,527.1 TANG

Four migrations, four positions moved, four issues resolved by the vote that moved them. The books balanced on every successor: Satsuma’s recorded holdings equalled what the new account held on tangerine.

Runs 8 to 10: the proxy

A migration is the heavy path. It deploys two contracts, moves funds across a bridge, and relocates every claim that depended on the old address, which is why run 7 found one more gap: the claim for the remote account was recorded at the wrong successor contract, and the loop watched that contract for three hours for a position that had landed, elsewhere, within minutes. An upgrade is lighter: the proxy keeps its address, its funds and its state, and a vote points it at new code. The brief gained a rule before run 8: a contract that others bind to by address is a proxy the timelock may upgrade.

Run 8 showed the rule working and nothing else. Given it, and given the proxy libraries, the builder put the mandate behind a proxy on the first try and the loop recorded the claim at the proxy, not the implementation. Then the operation met its objective, closed, and was retired by its own review; and for five hours every cycle found every key result met and proposed nothing. No issue was ever filed, because the drill had asked for the mandate’s review before the mandate’s state folder existed, so the request was lost. The upgrade path went untested for a harness reason.

Run 9 tested two things the earlier runs had not reached. The exchange’s S4 (“invariant I1, WTANG fully backed, is tautological”) was fixed by redeploy, and this time the fix’s pull request was merged into the product’s main when the chain resolved the issue, the first time that ran live. And because the redeploy moved the router, the ownership pass filed an S3 on the mandate: “Dependency amm-dex relocated: this product still binds to 0x4bfb…”. A product learning, on chain, that a product it depends on has moved. The mandate’s own issues stayed open, though, and the reason was a loop bug: the loop looked for the migrate function in the proxy’s bytecode, where of course it is not (it is in the implementation’s), and a declared migration was given priority over an upgrade even when both were real. Both were fixed afterwards. The fix made the assessor decide, per issue, which path a defect travels when a product can travel both.

Run 10 lasted 96 minutes and built nothing. The proposer named the exchange’s interface IUniswapV2, the builder wrote a file of that name declaring the factory, pair and router interfaces, none called IUniswapV2, and the gate that checks the first milestone accepted only an exact match. Five refusals across two proposals, each retried blind, before the run was stopped by hand. Run 9 had passed the same gate because its proposer happened to choose a name the builder declares. A gate that passes by coincidence is not tested. The gate now accepts the file a faithful builder writes, the brief tells the builder what the name must be, and a refused attempt carries its reason into the next one.

Run 11: the upgrade

Run 11 ran on 17 October from 03:33 to 08:24 UTC, on frozen code, with no intervention; it ended itself.

Both products built and deployed on the first attempt. The proposer split the exchange into the two interfaces a dependent actually calls, the factory’s and the router’s, and the mandate was built behind a proxy owned by the timelock. The operation went active and its operator started at 06:55. Three swaps in, the drill asked for both reviews.

the runexchange, deployed 04:48mandate built behind a proxy, deployed 06:55idle04:0005:0006:0007:0008:0003:33 start08:24 the watcher ends the runoperationownershipreviewsdrillspolicy as proposed · operator tradingclosed in band 07:53retired 07:55review 07:01: S4 filed, upgrade path chosen, fix proposedfix built · implementation deployed · upgrade votefix PR merged 07:351C2A3C4Rpool pulled: 17 swaps failamended policy live 07:5206:5007:0007:1007:2007:3007:4007:5008:00key results metmissedC continue · A amend · R retire
Run 11, UTC. Three and a half hours of building, then one hour in which the review, the fix, the upgrade vote, the pull drill and the policy amendment all happened, then nothing left to do.

The exchange’s review read every file against the canonical Uniswap V2 sources and found nothing. The mandate’s review found three things and filed one, an S4: “maxProofAge measures receipt arrival, not observation time; stale prices pass.” For newcomers: the mandate account refuses a swap whose price proof is older than 180 seconds. The proof is a receipt from tangerine carrying the pool’s reserves at the moment they were read. The contract stamped the receipt with the time it arrived on Satsuma’s chain, not the time the reserves were read. A receipt delayed by the relayer, or re-sent after a timeout, would reset the clock, and the floor below which a swap is refused would be computed from a price minutes old. No funds could leave beyond the operator’s own minimum, but the promise in the manual did not hold.

The assessor was asked which path the fix should travel, since this mandate could travel both: it was behind a proxy and also exposed a migration. It chose the upgrade, and said why:

The defect is in MandateAccount.onAccept. It sets $.provenAt = uint64(block.timestamp), which is when the receipt arrives on Satsuma. That moment can be long after the reserves were read on tangerine, for example when RemoteAccount.onTimeout resends the receipt unchanged.

The defect was in the code the proxy runs; an upgrade reaches it; nothing that holds funds needed to move. A fix was proposed in the same cycle, up to 500 compute units from the reserve, and approved at 90% confidence. The builder rebuilt the mandate from its deployed sources with the issue as scope. The fix proper is one line, $.provenAt = req.requestedAt, dating the proof from the moment the instruction was dispatched, which is a lower bound on when tangerine read the reserves; with it came a version bump and a test that reproduces the filed scenario: a receipt re-sent after the market moves 20%, delivered late, which now fails the age check as it should.

The new implementation was deployed on its own at 0xfa87…9e3. Then one vote, executed from the timelock, called upgradeToAndCall on the proxy at 0xC19E…d64, recorded the claims as deployed, resolved the issue, and resumed the product. The proxy’s address did not change. Its holdings did not move. Its state, its counters and its last proof, stayed. The operator never changed its target. The fix’s pull request was merged into the product’s main branch at 07:35, because the chain now said the issue was resolved, and the readable copy of the issue was closed.

Meanwhile the drills ran. During the liquidity pull, seventeen swaps failed in twenty minutes, and the operation’s second review said amend:

The failures were not random. Across reports 17 to 32 the failure count rose with almost every swap while the tUSD share fell from about 48% to 28.8%. The operator’s own need op0-need-1 says the live pool reserves had dropped about 1000x below the proven reserves. Swap sizes and minOut were still derived from those proven reserves, so tangerine rejected swap after swap.

The operator had filed a need on its own account, saying the pool it could see no longer matched the pool it had proofs of. The loop picked up the need as a proposal, “Size operation 0 swaps from fresh verified reserves and close inside the 47–53 percent band”, and the amended policy was voted and went live at 07:52:06. The operator restarted on it at 07:52:50 and closed, in band at 51.07% tUSD, thirty-six seconds later. The third review found the new policy had no evidence yet and said continue; the fourth found the operation closed on its objective and retired it, returning the two unspent reserves in the same payload.

ReviewPeriodOn-chain verdictRecommendationCouncil
106:54–06:59both targets missed, on 3 settled swapscontinue (62%): too little activity to judge3 to 0
207:03–07:358 swaps met; failure rate 7,727 missedamend (80%): never size a swap from reserves the live pool no longer matches3 to 0
307:38–07:5226 swaps met; 0 bps metcontinue (88%): the new policy is six seconds old3 to 0
4, final07:54–07:55both metretire (72%): the operator closed itself in band3 to 0

Final state, read from both chains before they were stopped: 26 swaps executed, 19 failed (17 of them inside the pull), none timed out, 2 requests refused by the mandate, holdings 51.07% tUSD, Satsuma’s recorded balances equal to the real ones. Twenty-three Governor proposals, twenty-three votes, twenty-three executions. Every drill passed, including the one that tries the against every administrative function of the mandate and found it can call neither setLimits nor upgradeToAndCall: the only thing that can re-point the proxy is the timelock, and the only thing that moves the timelock is a vote.

From 07:56 every cycle found every key result met and proposed nothing. At 08:24 a watcher that had counted twenty such cycles verified both chains and shut everything down. The run ended because the work was done.

The eleven runs

Eleven runs. Bars: times a person changed the loop during the run. Squares: issues the reviews filed, filled when a vote fixed them inside the run. Run 10 was stopped by hand before it built anything.
RunWhat it testedDurationIssues filed / fixedInterventionsSwaps / refused / books / band
1Products, manifests, invariants, the assessor6 h 410 / 0319 / 3 / match / stopped mid-operation
2The fix path, forced by review~8 h3 / 1 (redeploy)10 restarts10 / 2 / not read / retired
3Refusing an unsafe fix (ADR 0019, first cut)4 h 471 / 0 (S1 stays halted)43 / 0 / match / halted
4The migration path~15 h, two limit stalls3 / 2 (redeploy, migration)7 + one hand edit3 / 0 / match / paused
5The same, on fixed code5 h 152 / 1 (migration)028 / 4 / match / stopped mid-operation
6Every key result under the regime5 h 303 / 2 (redeploy, migration)1 restart10 / 3 / match / in band
7Migration mid-operation~8 h 504 / 1 (migration)034 / 3 / match / in band
8The proxy rule~9 h 45, 5 h idle0 / 0011 / 3 / match / in band
9Fix PR merged on resolution; dependency alert12 h 30, 7 h idle3 / 1 (redeploy)017 / 5 / match / in band
10(stopped at the interface gate)1 h 36–the stop–
11The upgrade path4 h 511 / 1 (upgrade)026 / 2 / match / in band

Interventions are code changes to the loop with a restart, during the run, after a bug the run exposed; none changed a decision Satsuma made, and the one hand edit (run 4) restored a journal a loop bug had corrupted. “Issues fixed” counts those resolved on chain by a vote within the run. Every run was on Claude Opus 5.5, billed to a subscription rather than an API; two runs were stalled for hours by its usage limits, and lost nothing.

What was fixed in advance, and what was fixed on the way

The claim of this article is that in five of the last seven runs every decision came out of Satsuma’s process; the other two were a restart for a loop fix (run 6) and a stop (run 10). The boundaries:

Fixed in advance by the experimenter: the objective and key results; the world on tangerine, all scripted; the drills, now including a request for both reviews three swaps in and a scan of what the deployment key can call; the severity scale and its floors; the four outcomes a review may choose from; and the paragraph of the brief that asks for manuals, timelock-held administration, and a declared way to change each contract.

Added to the loop between runs, by a person: the product registry, the manifests, the assessor and the adversarial review (before run 1); the declarations and the refusal of unsafe fixes (before run 3); the migration payload (before run 4); the proxy rule, the upgrade payload and the merge on resolution (before run 8); the assessor’s choice of path when both are real (before run 10). Each is recorded as an architecture decision, or an amendment to one, before the run that tested it.

Fixed during a run, by a person, after the run exposed it: twenty-five interventions across runs 1, 2, 3, 4 and 6. Two were housekeeping (a missing package, a restart to take the loop off an API key); the rest were loop bugs: a float where an integer was needed, a manifest file renamed from under its on-chain reference, KPI identifiers shifted by a concurrent adoption. None in the runs the table marks with a zero.

Decided by Satsuma: what each product’s invariants are and how wide; what it found when it read its own code, and how severe; whether a signal was a defect or a market condition; whether to file at all; which path a fix should travel, when both were open; the fix itself, down to the line; every policy and its amendments; and every vote.

What this shows, and what it does not

It shows that ownership closes on chain. A deployed contract is opened, measured, reviewed, found wanting, fixed and attested, with each step a vote or a verified fact and the reasoning behind each judgment hashed on chain. Over eleven runs that happened nine times, on code Satsuma had written itself and built afresh each run.

It shows that the refusals are the safety. The assessor declined to file a drill’s failures as a defect, declined to treat a float’s rounding as an exploit, and withheld low-confidence Critical findings as questions rather than alarms. The loop declined to redeploy a contract holding funds when it had no safe way to fix it, and left an S1 open rather than close it badly. A system that fixes everything it sees is more dangerous than one that files what it cannot yet fix.

It shows that a builder’s claim about its own contract is checked. Twice the manual said upgradeable and the chain said there was no proxy. The correction went through a vote, and the fix went the way the contract actually allowed.

It shows three ways to change a contract that is live, and when each is right. Four migrations moved the position to a successor and the books balanced every time. One upgrade changed the code under the position without moving it. The assessor chose between them for a reason it wrote down.

It does not show a Critical defect fixed by upgrade. The upgrade of run 11 fixed an S4. The S1s and the S2 were fixed by migration, in builds that had no proxy. Whether the loop halts a proxied product on an S1 and upgrades it in place, with the operation paused and resumed around the vote, is untested.

It does not show learning between runs. The same weakness in the mandate’s price floor, a floor computed from reserves an attacker can skew, was filed in runs 3, 4, 5, 6 and 7; the proof-age defect run 11 fixed had first been filed in run 2. Each run builds the mandate from scratch, and each builder made a version of the same mistakes, and each review caught them. The reviews work; the builder does not remember. Within a run, Satsuma owns what it builds. Across runs, it starts over.

The reviewer and the builder are the same kind of model. The adversarial review found real defects, several of them known classes of weakness in the code it was reading. It missed what it missed, and nothing here measures that. An independent audit of any of these products would be the way to find out.

The council rarely says no, but it did. It refused two manifests with no invariants in run 3. It passed every fix, every amendment, every review recommendation it was given. Twenty-three of twenty-three votes in run 11 were 3 to 0.

The limits of the setup are those of the first two experiments. Both chains had a single validator and ran on one machine. The market, the relayer and the drills were run by the experimenter. The tokens are worthless. The repositories are private.

What the first experiment tested was whether a system could be given a goal and build its way to acting on it. The second tested whether it could keep acting well. This one tested whether it could keep what it built: know what must stay true of it, notice when it does not, say how bad that is, stop if it must, and fix it without losing what it holds. Over eleven runs, by the last one, with nobody in the room, it did.

Every figure comes from the recorded runs of 13 to 17 October 2026, whose records are kept with the experiment. Run 11, the one described in detail, began on 17 October at 03:33 UTC and ended, by its own hand, at 08:24. Models were Claude Opus 5.5, called with no memory between calls. Where an assessor’s or reviewer’s reasoning is quoted, the quote is from the file whose hash was recorded on chain. The two decision records are ADR 0018, “Products are owned”, and ADR 0019, “Fixes to stateful products are migrations”.

  1. The setting, briefly
  2. What owning means here
  3. Run 1: it measured, and refused to file
  4. Run 2: the first real finding, and the wrong shape of fix
  5. Three ways to fix a contract
  6. Run 3: it stopped, and stayed stopped
  7. Runs 4 to 7: the money moves
  8. Runs 8 to 10: the proxy
  9. Run 11: the upgrade
  10. The eleven runs
  11. What was fixed in advance, and what was fixed on the way
  12. What this shows, and what it does not