fix(demo): switch on the actions the seeded events use (DM28)
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 54s
PR Checks / server-tests (pull_request) Successful in 6m6s
PR Checks / client-build (pull_request) Successful in 7m33s

The step-5 walk found the demo's three seeded events (Server greeting,
Treasure drop, Payday) showing as ready but failing when run: a fresh
database has no switchboard rows, so every change and irreversible action
starts disabled ("not enabled on this deployment").

seed:demo now switches on, with the events, every action their steps use,
uncapped, as an admin flipping the switch without filling the cap boxes
would. Nothing else is switched on. The deny list (DM11) still keeps a
visitor's own command step harmless, and the wipe drops the rows with the
rest of the database.

Checked on a scratch database with the v0.6.1 module: the seed leaves
rust.command.run, rust.dt.start and rust.coins.pay enabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-28 04:44:25 -05:00
parent 47a79d841c
commit ebedee146f
2 changed files with 43 additions and 2 deletions

View File

@@ -17,6 +17,9 @@
// 5. `demo_state` — when the next wipe is due and the published logins, for
// the banner and the sign-in page (§6).
//
// With the events it also switches on, in the events switchboard, every action
// those events' steps use — otherwise a seeded event cannot run (§7.3).
//
// A string `"$env:NAME"` anywhere in the fixture is replaced with that variable,
// so the rig's sidecar token lives in the host's .env and never in the repo. An
// unset variable fails the run rather than seeding an empty credential.
@@ -106,6 +109,26 @@ function checkFixture(fixture) {
return problems
}
/**
* Every action a seeded event's steps use, once each, in first-use order.
*
* A fresh database has no switchboard rows, so every `change` and `irreversible`
* action starts disabled (EVENTS.md §K) — and a seeded event that answers "not
* enabled on this deployment" when a visitor runs it is a showcase that does not
* show (step-5 walk, W1). The seed switches on exactly these and nothing else.
*/
function eventActionIds(events = []) {
const ids = []
for (const e of events) {
for (const phase of e?.spec?.phases || []) {
for (const step of phase?.steps || []) {
if (step?.actionId && !ids.includes(step.actionId)) ids.push(step.actionId)
}
}
}
return ids
}
// ── The run ────────────────────────────────────────────────────────────────
/* eslint-disable global-require */
@@ -174,11 +197,18 @@ async function writeContent(fixture, authorId) {
if (!pub.ok) throw new Error(`event "${input.name || input.title}" did not publish: ${(pub.errors || []).join('; ')}`)
}
}
// Uncapped, like a switch an admin flips without filling the cap boxes: the
// deny list (DM11) is what keeps a visitor's own command step harmless, and the
// wipe takes the row away with the rest of the database.
const actionSettings = require('../src/model/events/eventActionSettings.db')
const enabled = eventActionIds(content.events)
for (const actionId of enabled) await actionSettings.put(actionId, { enabled: true, caps: {} }, authorId)
log.info('content written', {
posts: (content.posts || []).length,
wiki: (content.wiki || []).length,
pages: (content.pages || []).length,
events: (content.events || []).length,
actionsEnabled: enabled,
})
}
@@ -251,4 +281,4 @@ if (require.main === module) {
})()
}
module.exports = { resolveEnv, nextResetAt, checkFixture, run }
module.exports = { resolveEnv, nextResetAt, checkFixture, eventActionIds, run }

View File

@@ -20,7 +20,7 @@ const db = require('../src/utils/db')
const registries = require('../src/modules/registries')
const settingsDb = require('../src/model/settings/settings.db')
const settings = require('../src/model/settings/settings.model')
const { resolveEnv, nextResetAt, checkFixture } = require('../db/seedDemo')
const { resolveEnv, nextResetAt, checkFixture, eventActionIds } = require('../db/seedDemo')
after(() => db.close())
afterEach(() => {
@@ -212,6 +212,17 @@ test('checkFixture accepts the four accounts and refuses anything that would lea
assert.match(checkFixture({ ...GOOD, modules: [] }).join('\n'), /modules must be an object/)
})
test('eventActionIds lists each action the seeded events use, once, and the shipped fixture names all three', () => {
const step = (actionId) => ({ actionId, params: {} })
const ev = (...phases) => ({ spec: { phases: phases.map((steps) => ({ steps: steps.map(step) })) } })
assert.deepEqual(eventActionIds([ev(['a.run', 'core.wait'], ['a.run']), ev(['b.pay']), {}, { spec: {} }]),
['a.run', 'core.wait', 'b.pay'])
assert.deepEqual(eventActionIds(), [])
const fixture = require('../../deploy/demo/seed.json') // eslint-disable-line global-require
assert.deepEqual(eventActionIds(fixture.content.events), ['rust.command.run', 'rust.dt.start', 'rust.coins.pay'])
})
test('seed:demo refuses to run off the demo', async () => {
const { run } = require('../db/seedDemo') // eslint-disable-line global-require
await assert.rejects(() => run('anything.json'), /only with DEMO_MODE=true/)