Read the vendor through a shared world
Keep a copy of the account in the team's world: refreshed on demand or on a schedule, read by every clone without a vendor call, and rebased when the vendor moves.
This page is executed as written by packages/cli/src/journeys/tutorials.test.ts; the recording
is made from the same run.

A root's log is the account's history as observed. A refresh asks the vendor what it holds
and appends to that log only what changed; the twin says how often the vendor can be asked, and
the world can say otherwise. Everything downstream reads the held copy: a clone answers from its
tree, so a rate-limited API is read as often as you like. When the vendor moves under a
changeset, rebase names the conflict by record and field.
Three worlds
GitHub itself is a world here, so the whole chain runs on one machine; the commands are the same
when the root is https://api.github.com.
{ "name": "acme-web", "private": true, "type": "module", "dependencies": { "@octokit/rest": "^21" } }
import { Octokit } from '@octokit/rest';
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data: issues } = await octokit.issues.listForRepo({ owner: 'acme', repo: 'web', state: 'all' });
for (const issue of issues) console.log(`#${issue.number} ${issue.title}`);
import { Octokit } from '@octokit/rest';
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
await octokit.issues.update({ owner: 'acme', repo: 'web', issue_number: 1, title: process.argv[2] });
console.log('retitled #1');
npm install
npm install -g @volter/world
npm install -D @volter/twin-github
mkdir ../reality && cd ../reality && volter world init --bare acme/reality --twins github
volter world serve --port 4400 &
mkdir ../team && cd ../team && volter world init --bare acme/team --twins github
volter world serve --port 4300 &
cd ../acme-web
Wait for both worlds to announce that they are serving before continuing.
serving acme/reality http://127.0.0.1:4400/acme/reality
serving acme/team http://127.0.0.1:4300/acme/team
for i in $(seq 1 60); do [ -f ../reality/.volter/token ] && [ -f ../team/.volter/token ] && break; sleep 1; done; sleep 3
curl -s -X POST http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues -H "authorization: Bearer $(cat ../reality/.volter/token)" -H 'content-type: application/json' -d '{"title":"Launch checklist"}' | grep -o '"number":[0-9]*'
"number":1
Set the root, and how often it may be asked
--deploy hold keeps landed entries waiting until someone deploys; --at-most 30s is this
world's word on how often the twin may be refreshed on demand (the twin has its own default).
cd ../team
volter twin github root http://127.0.0.1:4400/acme/reality/github --scope repos/acme/web --deploy hold --at-most 30s
cat ../reality/.volter/token | volter twin github credential
volter twin github refresh
volter twin github refresh
cd ../acme-web
github refreshed
github not refreshed: refreshed at ISO; the github twin refreshes at most every 30s (--force to refresh now)
A clone reads the copy
The app's world clones the team's, and its tree holds what the team observed. Stop the vendor, and the app still reads the issue: nothing here calls the vendor.
volter world init
volter world up --no-seed
volter world clone http://127.0.0.1:4300/acme/team --token "$(cat ../team/.volter/token)"
kill %1
volter world run -- node list-issues.mjs
#1 Launch checklist
When the vendor moves
Bring the vendor back and change the issue there. The team's world observes it on its next refresh. Meanwhile the app changed the same title, cut a changeset and pushes: the push refuses, because the team's world moved under it, and says what to do.
cd ../reality
volter world serve --port 4400 &
Wait for the restarted world to announce that it is serving:
serving acme/reality http://127.0.0.1:4400/acme/reality
cd ../acme-web
for i in $(seq 1 60); do curl -s http://127.0.0.1:4400/-/ping >/dev/null && break; sleep 1; done; sleep 2
curl -s -X PATCH http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues/1 -H "authorization: Bearer $(cat ../reality/.volter/token)" -H 'content-type: application/json' -d '{"title":"Launch checklist, final"}' | grep -o '"title":"[^"]*"'
cd ../team
volter twin github refresh --force
cd ../acme-web
volter world run -- node retitle.mjs "Launch checklist, draft"
volter world changeset -m "Retitle the checklist"
volter world push
"title":"Launch checklist, final"
github refreshed 1 changed
retitled #1
changeset retitle-the-checklist 1 change
origin moved: 1 change on github since your last fetch
fetch brings the moved base, and rebase replays the changeset over it, naming the record and
the field where the two disagree. The rebased changeset has a new hash; a reviewer sees the
conflict on it.
volter world fetch
volter world rebase retitle-the-checklist
volter world push
conflicts:
github issue:acme/web#issue:1 title set
pushed retitle-the-checklist 1 change → origin
The entry waits on the team's world: the root says hold. Someone deploys it, and the vendor
holds the app's title.
cd ../team
volter world deploy
volter world log --receipts
cd ../acme-web
curl -s http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues/1 -H "authorization: Bearer $(cat ../reality/.volter/token)" | grep -o '"title":"[^"]*"'
deployed github 1 change
"title":"Launch checklist, draft"
Clean up
volter world down
kill $(jobs -p)
Playback
Each command above, as the recording shows it.
npm install

npm install -g @volter/world

npm install -D @volter/twin-github

mkdir ../reality && cd ../reality && volter world init --bare acme/reality --twins github

volter world serve --port 4400 &

mkdir ../team && cd ../team && volter world init --bare acme/team --twins github

volter world serve --port 4300 &

cd ../acme-web

for i in $(seq 1 60); do [ -f ../reality/.volter/token ] && [ -f ../team/.volter/token ] && break; sleep 1; done; sleep 3

curl -s -X POST http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues -H "authorization: Bearer $(cat ../reality/.volter/token)" -H 'content-type: application/json' -d '{"title":"Launch checklist"}' | grep -o '"number":[0-9]*'

cd ../team

volter twin github root http://127.0.0.1:4400/acme/reality/github --scope repos/acme/web --deploy hold --at-most 30s

cat ../reality/.volter/token | volter twin github credential

volter twin github refresh

volter twin github refresh

cd ../acme-web

volter world init

volter world up --no-seed

volter world clone http://127.0.0.1:4300/acme/team --token "$(cat ../team/.volter/token)"

kill %1

volter world run -- node list-issues.mjs

cd ../reality

volter world serve --port 4400 &

cd ../acme-web

for i in $(seq 1 60); do curl -s http://127.0.0.1:4400/-/ping >/dev/null && break; sleep 1; done; sleep 2

curl -s -X PATCH http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues/1 -H "authorization: Bearer $(cat ../reality/.volter/token)" -H 'content-type: application/json' -d '{"title":"Launch checklist, final"}' | grep -o '"title":"[^"]*"'

cd ../team

volter twin github refresh --force

cd ../acme-web

volter world run -- node retitle.mjs "Launch checklist, draft"

volter world changeset -m "Retitle the checklist"

volter world push

volter world fetch

volter world rebase retitle-the-checklist

volter world push

cd ../team

volter world deploy

volter world log --receipts

cd ../acme-web

curl -s http://127.0.0.1:4400/acme/reality/github/repos/acme/web/issues/1 -H "authorization: Bearer $(cat ../reality/.volter/token)" | grep -o '"title":"[^"]*"'

volter world down

kill $(jobs -p)
