The control you can edit is not the value the agent reads
Reported as "I updated it, but it is still using the old one." Both halves are true, and the reason
is that "which repo does this agent work on" is stored in two places, only one of which is
editable, and the editable one is read by nothing.
Measured on Chess coder (26f71cd8, surfaceOptions.coding.repos: "single")
GET /v1/instances/26f71cd8…/settings
settings.repo = ~/dev/stores/pas/platform/apps/chess-academy ← the owner edited THIS
field "repo" = { label: "Repository", type: "text",
description: "The repo this agent owns — a local path (~/dev/my-repo)…" }
GET /v1/instances/26f71cd8…/coding/repos
workdir = ~/dev/pas/platform/apps/chess-academy ← every tool reads THIS
cloneStatus = ready
updatedAt = 2026-08-07 23:23:15 ← untouched by the edit
grep across workers/api/src finds no reader of settings.repo at all. It is a
settingsSchema field seeded on the coder-repo agent (migrations/0063_seed_coder2_agents.sql),
stored faithfully, and consumed by nothing. The owner changed the one thing the console offers, the
platform saved it, and the agent kept reading a different column.
Both values happen to point at empty directories; the real checkout is
~/dev/stores/pas/apps/chess-academy (21,039 files). That is incidental — the bug is that no edit
to the visible field could ever have worked.
Why there is no way out from the UI
RepoSettingsModal.tsx:86 shows Folder through Detail — a read-only div, styled with the same
border/radius/font-mono language as every input in the app (bg-paper vs bg-panel, i.e.
#0a0a0a vs #141414 on a dark-only theme).
ReposList.tsx:192 hides the add-repo input entirely when singleRepo.
- The empty state (
:216) sends the user to "Settings → Agent settings" — the field that does
nothing.
So for a single-repo agent the repo path is: not editable in repo settings, not re-addable in the
repo list, and editable only through a setting no code reads. There is no supported way to correct
it. The only working route is POST /coding/repos by hand.
The design
1. One source of truth, and it is the setting
For repos: "single" the settings field is the right home: declarative, subscriber-owned, already
rendered, already documented in the empty state, and consistent with how every other per-instance
choice works (#41's typed settings). Make it authoritative:
PUT /v1/instances/:id/settings with a changed repo reconciles the coding_repos row —
updating workdir (local path) or the remote coordinates (owner/name), creating the row if
absent, and never silently diverging again.
- The row remains the internal representation. Nothing outside the reconciler needs to know.
For multi-repo agents the list stays the source of truth and the setting is absent — which is
already true, since only the coder-repo agent declares it.
2. Validate where the value is entered
Apply #405's check on the settings write, not only on POST /coding/repos: does the path exist, is
it non-empty, is it a git work tree — asked of the runner that will use it. A rejected value must
not be stored, and the reason must be shown next to the field. This is the moment the owner can
actually act on it.
3. Make the repo path editable in the repo settings modal too
PUT /coding/repos/:repoId currently accepts name | urls | mergePolicy only. Add workdir, with
the same validation. Two entry points are acceptable if they write the same place; that is the
whole lesson of this ticket.
4. Stop rendering read-only values as inputs
Detail is visually a text field. Give read-only rows their own treatment (no border, or an
explicit lock affordance) so a value that cannot be changed does not invite a change. This is
general — every Detail in the app has the problem — and belongs with the design-system primitives
that landed in #366.
5. Where this is going: a checkout is per MACHINE, not per repo
Recorded as direction, not proposed for this ticket. workdir is a property of (repo, machine),
not of a repo. This account has two connected machines; the same repository is at
~/dev/stores/pas/apps/chess-academy on one and may be anywhere or nowhere on the other. Today a
single workdir column means an agent bound to a second machine is silently wrong — the identical
failure this ticket describes, arriving invisibly. coding_sessions already carries runner_node;
coding_repos has no such dimension.
The end state: a repo row holds identity (name, provider, remote, merge policy, URLs,
instructions); a checkout row holds location per machine, validated on the machine it names, so
the platform can say "ready on Mac, missing on Mac-mini" — a sentence it cannot form today. Do this
when a second machine actually runs the same repo, not before.
Acceptance
Files: workers/api/src/routes/instances.ts (settings PUT), workers/api/src/lib/instance-settings.ts,
workers/api/src/routes/coding-repos.ts:146,500, workers/api/src/lib/coding-store.ts,
agents/coder/web/src/RepoSettingsModal.tsx:86,121, agents/coder/web/src/ReposList.tsx:192,216,
workers/api/migrations/0063_seed_coder2_agents.sql. Supersedes the framing of #410; extends
#405.
The control you can edit is not the value the agent reads
Reported as "I updated it, but it is still using the old one." Both halves are true, and the reason
is that "which repo does this agent work on" is stored in two places, only one of which is
editable, and the editable one is read by nothing.
Measured on Chess coder (
26f71cd8,surfaceOptions.coding.repos: "single")grepacrossworkers/api/srcfinds no reader ofsettings.repoat all. It is asettingsSchemafield seeded on thecoder-repoagent (migrations/0063_seed_coder2_agents.sql),stored faithfully, and consumed by nothing. The owner changed the one thing the console offers, the
platform saved it, and the agent kept reading a different column.
Both values happen to point at empty directories; the real checkout is
~/dev/stores/pas/apps/chess-academy(21,039 files). That is incidental — the bug is that no editto the visible field could ever have worked.
Why there is no way out from the UI
RepoSettingsModal.tsx:86shows Folder throughDetail— a read-only div, styled with the sameborder/radius/
font-monolanguage as every input in the app (bg-papervsbg-panel, i.e.#0a0a0a vs #141414 on a dark-only theme).
ReposList.tsx:192hides the add-repo input entirely whensingleRepo.:216) sends the user to "Settings → Agent settings" — the field that doesnothing.
So for a single-repo agent the repo path is: not editable in repo settings, not re-addable in the
repo list, and editable only through a setting no code reads. There is no supported way to correct
it. The only working route is
POST /coding/reposby hand.The design
1. One source of truth, and it is the setting
For
repos: "single"the settings field is the right home: declarative, subscriber-owned, alreadyrendered, already documented in the empty state, and consistent with how every other per-instance
choice works (#41's typed settings). Make it authoritative:
PUT /v1/instances/:id/settingswith a changedreporeconciles thecoding_reposrow —updating
workdir(local path) or the remote coordinates (owner/name), creating the row ifabsent, and never silently diverging again.
For multi-repo agents the list stays the source of truth and the setting is absent — which is
already true, since only the
coder-repoagent declares it.2. Validate where the value is entered
Apply #405's check on the settings write, not only on
POST /coding/repos: does the path exist, isit non-empty, is it a git work tree — asked of the runner that will use it. A rejected value must
not be stored, and the reason must be shown next to the field. This is the moment the owner can
actually act on it.
3. Make the repo path editable in the repo settings modal too
PUT /coding/repos/:repoIdcurrently acceptsname | urls | mergePolicyonly. Addworkdir, withthe same validation. Two entry points are acceptable if they write the same place; that is the
whole lesson of this ticket.
4. Stop rendering read-only values as inputs
Detailis visually a text field. Give read-only rows their own treatment (no border, or anexplicit lock affordance) so a value that cannot be changed does not invite a change. This is
general — every
Detailin the app has the problem — and belongs with the design-system primitivesthat landed in #366.
5. Where this is going: a checkout is per MACHINE, not per repo
Recorded as direction, not proposed for this ticket.
workdiris a property of (repo, machine),not of a repo. This account has two connected machines; the same repository is at
~/dev/stores/pas/apps/chess-academyon one and may be anywhere or nowhere on the other. Today asingle
workdircolumn means an agent bound to a second machine is silently wrong — the identicalfailure this ticket describes, arriving invisibly.
coding_sessionsalready carriesrunner_node;coding_reposhas no such dimension.The end state: a repo row holds identity (name, provider, remote, merge policy, URLs,
instructions); a checkout row holds location per machine, validated on the machine it names, so
the platform can say "ready on Mac, missing on Mac-mini" — a sentence it cannot form today. Do this
when a second machine actually runs the same repo, not before.
Acceptance
with the reason beside the field.
settings.repoandcoding_repos.workdircan no longer disagree — with a test that asserts it.Files:
workers/api/src/routes/instances.ts(settings PUT),workers/api/src/lib/instance-settings.ts,workers/api/src/routes/coding-repos.ts:146,500,workers/api/src/lib/coding-store.ts,agents/coder/web/src/RepoSettingsModal.tsx:86,121,agents/coder/web/src/ReposList.tsx:192,216,workers/api/migrations/0063_seed_coder2_agents.sql. Supersedes the framing of #410; extends#405.