You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Redesign how game systems and the Foundry module update: automatic, pinned, or approve-first #685
AutoPinOnInstall already reads each campaign's pin mode: only 'preserve' freezes a campaign at its previous version when a new module version installs, so the default empty mode and 'promote' both leave the campaign tracking the latest installed version — the 'Auto-update (track latest installed)' label is already true for every campaign. A writer (campaigns.SetFoundryModulePinMode) and a validator (foundry_vtt.IsValidPinMode) already exist for that mode, but no route or UI control calls the writer, so no campaign can actually be put into 'preserve' mode today. What's left is a small UI control (and a route, if one doesn't already reach the setter) so an owner can choose 'preserve', or closing this issue if that option isn't wanted.
Decided (2026-09-23): option B. The label promises "track latest", so the behaviour should do that; keep "preserve" as the second, explicitly named mode.
Cordinator/plans/BACKLOG.md line 761 — Open bug queue > C-UPDATER-CLUSTER item 21 — operator decision needed
Checked against main on 2026-09-23: internal/plugins/foundry_vtt/owner_tab.templ:161 still shows 'Auto-update (track latest installed)'; pin_mode.go stores preserve/promote/pinned (Chunk 1) but service.go:35-41 says AutoPinOnInstall still follows preserve-state regardless of pin_mode (Chunk 2 held since 2026-05-20).
Old tracking IDs (so a search for them lands here): C-FMC-6, C-FMC-CHUNK-2, C-UPDATER-CLUSTER
Checked against main on 2026-09-25 while cleaning up comments. The description above is out of date:
AutoPinOnInstall already reads the pin mode. Only "preserve" freezes a campaign at the previous version. An empty mode (the default) or "promote" does nothing, so the campaign keeps tracking the latest installed version. The "Auto-update (track latest installed)" label is therefore already true for every campaign.
What's missing is a way to choose "preserve". The mode has a writer, campaigns.SetFoundryModulePinMode (through foundryCampaignSettingsAdapter in internal/app/routes.go), and a validator, foundry_vtt.IsValidPinMode, but no route or control calls either. Nothing in the product can set it today.
So option B has shipped on the server side. What's left is a small UI control plus a route if the operator wants "preserve" to be selectable, or closing this issue if not.
Decided 2026-09-25 on the sign-off page. Redesign the whole update flow instead of adding a second dropdown to today's card. It's part of the admin redesign (#681, item A2).
The operator's requirements:
It covers game-system packages and the Foundry module alike.
Admins and campaign owners can each choose one of three ways:
follow the newest version automatically;
stay on a chosen version;
approve each update before it applies.
A complete overhaul is fine as long as no current function is lost.
changed the title [-]Make 'Auto-update' pinning actually move campaigns to new versions[/-][+]Redesign how game systems and the Foundry module update: automatic, pinned, or approve-first[/+]on Sep 25, 2026
Signed 2026-09-25 on the sign-off page (card "ui-685", version 3). Part of the admin redesign #681, item A2.
What was approved
One update setting for game-system packages and the Foundry module alike. Admins set it per package and campaign owners per campaign.
The three choices: automatic, pinned to a version, or approve first.
The admin view shows which campaigns are on which version and mode, with notify, force and approve actions. It also flags unused versions and handles long package lists.
Cleanup of unused versions stays game-systems-only. Foundry module versions are never removed, since a campaign may be pinned to an old one.
No current function is lost. The mockup lists every function of today's screens and where it goes.
Default confirmed: a package with auto-update off and no pin becomes "pinned to its current version".
Build, in order:#755 (server), #756 (admin views), #757 (campaign Settings → Updates). Each ships as its own PR built to this mockup. The mockup's HTML is below so the contract outlives the sign-off page.
Signed mockup (HTML: Today, Admin · package detail, Admin · package list at scale, Campaign owner)
<divclass="mk mk-685"><style>
.mk-685{font-family:inherit;color:var(--c-body);background:var(--c-page);padding:1rem;border-radius:.6rem}
.mk-685*{box-sizing:border-box}
.mk-685-row{display:flex;flex-wrap:wrap;gap:1rem}
.mk-685-panel{flex:11320px;max-width:440px;min-width:260px}
.mk-685-label{font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--c-muted);margin:00.35rem}
.mk-685-sub{font-size:.72rem;color:var(--c-muted);margin:00.6rem;line-height:1.4}
.mk-685-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:.6rem;padding:.8rem.9rem;margin-bottom:.6rem}
.mk-685-card:last-child{margin-bottom:0}
.mk-685-flat{background:transparent;border:none;padding:0.1rem}
.mk-685-head{display:flex;align-items:flex-start;gap:.5rem;margin-bottom:.45rem}
.mk-685-ic{width:1.55rem;height:1.55rem;border-radius:.4rem;background:var(--c-accent-soft);display:flex;align-items:center;justify-content:center;font-size:.75rem;flex:none}
.mk-685-nm{font-size:.8rem;font-weight:700;color:var(--c-text);display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;line-height:1.3}
.mk-685-meta{font-size:.68rem;color:var(--c-muted);margin-top:.15rem;line-height:1.4}
.mk-685-metab{color:var(--c-text)}
.mk-685-good{color:var(--c-success)}
.mk-685-chip{display:inline-flex;align-items:center;gap:.2rem;font-size:.58rem;font-weight:700;padding:.1rem.4rem;border-radius:999px;border:1px solid var(--c-border);color:var(--c-muted);white-space:nowrap}
.mk-685-chip-type{border-color:transparent;background:var(--c-accent-soft);color:var(--c-accent)}
.mk-685-field{display:flex;align-items:center;gap:.45rem;font-size:.7rem;margin-top:.4rem;flex-wrap:wrap}
.mk-685-fl{color:var(--c-muted);font-size:.62rem;text-transform:uppercase;letter-spacing:.03em;flex:none}
.mk-685-select{font:inherit;font-size:.7rem;border:1px solid var(--c-border);background:var(--c-page);color:var(--c-body);border-radius:.4rem;padding:.28rem.45rem}
.mk-685-modes{display:flex;gap:.2rem;background:var(--c-page);border:1px solid var(--c-border);border-radius:999px;padding:.2rem;margin-top:.5rem}
.mk-685-modesspan{flex:1;text-align:center;font-size:.64rem;font-weight:700;padding:.3rem.15rem;border-radius:999px;color:var(--c-muted)}
.mk-685-modes .on{background:var(--c-accent-soft);color:var(--c-accent)}
.mk-685-hint{font-size:.68rem;color:var(--c-muted);line-height:1.5;margin:.5rem00}
.mk-685-hintem{font-style:normal;font-weight:700;color:var(--c-text)}
.mk-685-queue{display:flex;align-items:center;gap:.5rem;background:var(--c-page);border:1px dashed var(--c-warn);border-radius:.5rem;padding:.4rem.55rem;margin-top:.5rem;font-size:.68rem;color:var(--c-text)}
.mk-685-queuespan:first-child{flex:1}
.mk-685-lk{font-size:.64rem;font-weight:700;color:var(--c-accent);white-space:nowrap}
.mk-685-lk.mut{color:var(--c-muted)}
.mk-685-crumb{font-size:.68rem;font-weight:700;color:var(--c-muted);margin:00.5rem}
.mk-685-crumbb{color:var(--c-text)}
.mk-685-go{color:var(--c-success);border-color:var(--c-success)}
.mk-685-at{color:var(--c-warn);border-color:var(--c-warn)}
.mk-685-ac{color:var(--c-accent);border-color:var(--c-accent);background:var(--c-accent-soft)}
.mk-685-dash{border-style:dashed}
.mk-685-dg{color:var(--c-danger)}
.mk-685-crow{display:flex;align-items:center;gap:.5rem;padding:.35rem0;border-top:1px solid var(--c-border);font-size:.72rem}
.mk-685-crow:first-child{border-top:none}
.mk-685-crowb{flex:1;min-width:0;color:var(--c-text);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mk-685-mono{font-family:ui-monospace,Consolas,monospace;color:var(--c-muted);font-size:.68rem;white-space:nowrap}
.mk-685-vchips{display:flex;gap:.3rem;flex-wrap:wrap;margin:.4rem0}
.mk-685-search{font:inherit;font-size:.7rem;border:1px solid var(--c-border);background:var(--c-page);color:var(--c-muted);border-radius:.4rem;padding:.32rem.5rem;width:100%;display:block;margin-bottom:.5rem}
.mk-685-lk:hover{color:var(--c-accent-hover)}
</style><divclass="mk-685-row"><divclass="mk-685-panel"><pclass="mk-685-label">Today</p><pclass="mk-685-sub">Three unrelated controls, one of them half-built (#685), and campaign owners get no say over their game system's version at all.</p><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🛡</span><div><divclass="mk-685-nm">Draw Steel <spanclass="mk-685-chip mk-685-chip-type">Game System</span></div><divclass="mk-685-meta">installed <b>0.13.10</b> · admin-only, one setting for every campaign</div></div></div><divclass="mk-685-field"><spanclass="mk-685-fl">Auto-update</span><selectclass="mk-685-select" disabled><option>off</option></select></div><divclass="mk-685-field"><spanclass="mk-685-fl">Pin</span><spanclass="mk-685-chip">none set · a separate control, inside Versions</span></div><pclass="mk-685-hint">Latest (<b>0.13.11</b>) already exists. Nothing on this card says so — finding out means opening Versions and comparing by eye.</p></div><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🎲</span><div><divclass="mk-685-nm">Chronicle Sync <spanclass="mk-685-chip mk-685-chip-type">Foundry Module</span></div><divclass="mk-685-meta">Campaign Settings › VTT Setup Guides › Foundry VTT</div></div></div><divclass="mk-685-field"><spanclass="mk-685-fl">Pin to Version</span><selectclass="mk-685-select" disabled><option>Auto-update (track latest installed)</option></select></div><pclass="mk-685-hint">One dropdown, two real states. A third — <em>keep serving my current version even after the admin installs a new one</em> — already exists in the database and has no control to turn it on.</p></div><divclass="mk-685-card mk-685-flat"><pclass="mk-685-hint"><em>Game system, campaign side:</em> Settings only offers <em>which</em> system — Draw Steel, D&D 5e, custom… Which version it runs is entirely the admin's call. No control exists here at all.</p></div></div><divclass="mk-685-panel"><pclass="mk-685-label">Proposed · Admin · one package in detail</p><pclass="mk-685-sub">Draw Steel opened up: every campaign’s mode and version, what’s waiting for an OK, and the actions that reach them.</p><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🛡</span><div><divclass="mk-685-nm">Draw Steel <spanclass="mk-685-chip mk-685-chip-type">Game System</span></div><divclass="mk-685-meta">installed <b>0.13.10</b> · latest <bclass="mk-685-good">0.13.12 ✓</b> · 5 campaigns</div></div></div><divclass="mk-685-crow"><b>The Sunken Crown</b><spanclass="mk-685-chip mk-685-go">Auto</span><spanclass="mk-685-mono">0.13.12</span></div><divclass="mk-685-crow"><b>The Ninth Bastion</b><spanclass="mk-685-chip mk-685-go">Auto</span><spanclass="mk-685-mono">0.13.12</span></div><divclass="mk-685-crow"><b>Vasloria by Night</b><spanclass="mk-685-chip mk-685-at">Pinned</span><spanclass="mk-685-mono">0.13.10</span></div><divclass="mk-685-crow"><b>Grim Harvest</b><spanclass="mk-685-chip mk-685-ac">Approve first</span><spanclass="mk-685-mono">0.13.9</span></div><divclass="mk-685-crow"><b>Old Campaign (archived-ish)</b><spanclass="mk-685-chip mk-685-at">Pinned</span><spanclass="mk-685-mono">0.13.9</span></div><divclass="mk-685-queue"><span><b>Grim Harvest</b> is waiting on <b>0.13.12</b></span><spanclass="mk-685-lk">Approve</span><spanclass="mk-685-lk mut">Skip</span></div><pclass="mk-685-hint"><em>Admin actions:</em> Notify owners still behind · Force a version, one campaign or every older one at once 🔒 · Approve on their behalf. Notify and Force-a-version are today’s Foundry-only actions, now working for a system too; Approve calls that same Force-pin — there’s no separate “approve” endpoint underneath it.</p></div></div><divclass="mk-685-panel"><pclass="mk-685-label">Proposed · Admin · package list at scale</p><pclass="mk-685-sub">Many packages, many versions — unused ones marked, and pruning still only ever touches game systems.</p><inputtype="text" class="mk-685-search" disabledplaceholder="Filter packages…"/><divclass="mk-685-vchips"><spanclass="mk-685-chip mk-685-ac">All · 3</span><spanclass="mk-685-chip">Systems · 2</span><spanclass="mk-685-chip">Foundry Module · 1</span><spanclass="mk-685-chip mk-685-at">Has unused · 2</span></div><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🛡</span><div><divclass="mk-685-nm">Draw Steel</div><divclass="mk-685-meta">5 versions on disk · 5 campaigns</div></div><spanclass="mk-685-chip mk-685-at">2 unused</span></div><divclass="mk-685-vchips"><spanclass="mk-685-chip mk-685-at mk-685-dash">0.13.8</span><spanclass="mk-685-chip mk-685-go">0.13.9 served</span><spanclass="mk-685-chip mk-685-go">0.13.10 served</span><spanclass="mk-685-chip mk-685-at mk-685-dash">0.13.11</span><spanclass="mk-685-chip mk-685-go">0.13.12 newest</span></div></div><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">📖</span><div><divclass="mk-685-nm">D&D 5e</div><divclass="mk-685-meta">3 versions, collapsed · 2.1.0 unused · 2 campaigns</div></div><spanclass="mk-685-chip mk-685-at">1 unused</span></div></div><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🎲</span><div><divclass="mk-685-nm">Chronicle Sync <spanclass="mk-685-chip mk-685-chip-type">Foundry Module</span></div><divclass="mk-685-meta">4 versions, collapsed · 5 campaigns</div></div><spanclass="mk-685-chip mk-685-go">kept, never pruned</span></div><pclass="mk-685-hint">Module folders are never reclaimed — a campaign can pin to any past release, so every version installed here stays on disk on purpose.</p></div><divclass="mk-685-queue"><span>Prune <b>3 unused versions</b>, reclaiming <b>~38 MB</b>? The newest, installed, and currently-served version of each system stays — nothing any campaign uses is touched.</span><spanclass="mk-685-lk mk-685-dg">Prune</span></div></div><divclass="mk-685-panel"><pclass="mk-685-label">Proposed · Campaign owner</p><pclass="mk-685-sub">The same three-way choice, now covering the game system too — including a waiting update, and a pin that’s starting to look neglected.</p><pclass="mk-685-crumb"><b>Grim Harvest</b> › Settings › Updates</p><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🛡</span><div><divclass="mk-685-nm">Game System</div><divclass="mk-685-meta">Draw Steel · serving <b>0.13.9</b></div></div></div><divclass="mk-685-modes"><span>Auto</span><span>Pinned</span><spanclass="on">Approve first</span></div><divclass="mk-685-queue"><span><b>0.13.12</b> is waiting for your OK</span><spanclass="mk-685-lk">Approve</span><spanclass="mk-685-lk mut">Skip</span></div></div><pclass="mk-685-crumb"><b>Old Campaign</b> › Settings › Updates</p><divclass="mk-685-card"><divclass="mk-685-head"><spanclass="mk-685-ic">🛡</span><div><divclass="mk-685-nm">Game System</div><divclass="mk-685-meta">Draw Steel · serving <b>0.13.9</b></div></div></div><divclass="mk-685-modes"><span>Auto</span><spanclass="on">Pinned</span><span>Approve first</span></div><divclass="mk-685-queue"><span>Once Grim Harvest takes its update, you’re the only campaign left on 0.13.9 — not the newest, so a future cleanup could reclaim it.</span></div><divclass="mk-685-head" style="margin-top:.55rem"><spanclass="mk-685-ic">🎲</span><div><divclass="mk-685-nm">Foundry Module</div><divclass="mk-685-meta">Chronicle Sync · serving <b>0.9.2</b></div></div></div><divclass="mk-685-modes"><span>Auto</span><spanclass="on">Pinned</span><span>Approve first</span></div><divclass="mk-685-queue mk-685-go"><span>Old module versions are never cleaned up automatically — 0.9.2 stays however long you’re pinned to it.</span></div></div></div></div></div>
Activity
keyxmakerx commentedon Sep 25, 2026
Checked against
mainon 2026-09-25 while cleaning up comments. The description above is out of date:AutoPinOnInstallalready reads the pin mode. Only"preserve"freezes a campaign at the previous version. An empty mode (the default) or"promote"does nothing, so the campaign keeps tracking the latest installed version. The "Auto-update (track latest installed)" label is therefore already true for every campaign.campaigns.SetFoundryModulePinMode(throughfoundryCampaignSettingsAdapterininternal/app/routes.go), and a validator,foundry_vtt.IsValidPinMode, but no route or control calls either. Nothing in the product can set it today.So option B has shipped on the server side. What's left is a small UI control plus a route if the operator wants "preserve" to be selectable, or closing this issue if not.
Generated by Claude Code
keyxmakerx commentedon Sep 25, 2026
Decided 2026-09-25 on the sign-off page. Redesign the whole update flow instead of adding a second dropdown to today's card. It's part of the admin redesign (#681, item A2).
The operator's requirements:
It needs a new mockup before building.
Generated by Claude Code
[-]Make 'Auto-update' pinning actually move campaigns to new versions[/-][+]Redesign how game systems and the Foundry module update: automatic, pinned, or approve-first[/+]keyxmakerx commentedon Sep 25, 2026
Signed 2026-09-25 on the sign-off page (card "ui-685", version 3). Part of the admin redesign #681, item A2.
What was approved
Build, in order: #755 (server), #756 (admin views), #757 (campaign Settings → Updates). Each ships as its own PR built to this mockup. The mockup's HTML is below so the contract outlives the sign-off page.
Signed mockup (HTML: Today, Admin · package detail, Admin · package list at scale, Campaign owner)
Generated by Claude Code