Roster
Browse the docs

Upgrading

How framework changes reach a tenant without eating your edits.

The framework writes templates out. A tenant runs its own copies. So the two drift, and roster upgrade is what reconciles them without eating your edits.

roster upgrade                 # what would change
roster upgrade --apply         # do it
roster upgrade --check         # exit non-zero if anything is pending (for CI)

How it decides

A generated file has three versions: what the framework shipped when this tenant was seeded, what it ships now, and what you have today. Upgrading is a three-way merge between them, done by git merge-file. A tenant that has rewritten half of org/voice.md still gets the rest.

The base lives in <ops>/.roster/seed/ and is committed. Without it an upgrade can only be a copy, which would take your edits with it.

Ownership decides what an edit means

In the ops repo:

org/, prompts/ yours. Local edits are expected and get a real merge.
everything else the framework's. An edit is reported whether or not anything has collided.

That last rule is deliberate. A fix applied to a framework-owned file in the tenant is not safe just because nothing broke; it survives only until the framework next touches that file. "Not broken yet" is the state a lost fix sits in, so it is reported then, not later.

In a brain repo, only the caller workflows are upgraded. The charter, the memory index, the decisions log and the manifest belong to the staff member from the moment they are created. A working agent rewrites them beyond recognition, and merging a template into that would be vandalism. They are added when new and never rewritten.

Callers are regenerated, not merged

A caller is derived entirely from the manifest and the template, so there is no third version to reconcile. What looks like a local edit is either a template change that has not arrived, or something that should have been a manifest change.

The diff is printed either way, so nothing goes quietly.

If you want a caller to differ, change the thing it is generated from. Timeouts, schedule, model and identities all live in staff.yaml.

Conflicts

A conflict is never written into a live file. Agents read org/voice.md at every boot, and conflict markers in it would land in every composed prompt.

Instead the merged result with markers goes to <file>.roster-merge, the live file is untouched, and the recorded base does not advance for that file. That last part matters: advancing it would throw away the only thing that can merge it next time.

Resolve by hand, then re-run.

A tenant with no recorded base

Tenants created before this existed have nothing to merge against. Reconstruct it from the framework's own history:

roster upgrade --baseline <git-ref>

Pick the framework commit the tenant was seeded from. You can tell you picked the right one: most files will match it byte for byte, and the ones that do not will be the ones you know you edited.

After applying

roster upgrade --apply writes into your working trees. It does not commit or push, and it cannot: App tokens cannot push a change under .github/workflows/. Review, commit, push.

Edit this page on GitHub