feat(db): crew_skills table + role-based starting levels — schema v8 (#45)
CI / build + unit + e2e (pull_request) Successful in 2m48s

This commit is contained in:
2026-10-04 00:56:30 +00:00
parent 678cd48a94
commit 449d4b9895
6 changed files with 99 additions and 6 deletions
+1
View File
@@ -19,6 +19,7 @@ export const ADMIN_TABLES = {
missions: schema.missions,
events: schema.events,
tasks: schema.tasks,
crew_skills: schema.crewSkills,
} as const satisfies Record<string, SQLiteTable>
const TABLE_NAMES: Set<string> = new Set(Object.values(ADMIN_TABLES).map((t) => getTableName(t)))
+10
View File
@@ -129,6 +129,16 @@ CREATE TABLE IF NOT EXISTS tasks (
status TEXT NOT NULL DEFAULT 'ACTIVE', -- 'ACTIVE' | 'PENDING' | 'DONE'
queued_at_turn INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS crew_skills (
id INTEGER PRIMARY KEY AUTOINCREMENT,
save_id INTEGER NOT NULL REFERENCES saves(id),
crew_id INTEGER NOT NULL REFERENCES crew(id),
skill TEXT NOT NULL, -- 'NAVIGATION' | 'SCANNING' | 'ENGINEERING' | 'RESEARCH' (SkillName)
level INTEGER NOT NULL DEFAULT 0, -- 0–5
xp INTEGER NOT NULL DEFAULT 0, -- cumulative completions in this skill (Task 13 grants)
UNIQUE (crew_id, skill)
);
`;
// SPACE_WANDERER_DB_PATH override exists so unit tests can point at a temp file instead
+11
View File
@@ -153,3 +153,14 @@ export const tasks = sqliteTable('tasks', {
status: text('status').notNull().default('ACTIVE'), // 'ACTIVE' | 'PENDING' | 'DONE'
queuedAtTurn: integer('queued_at_turn').notNull().default(0),
})
// Per-member skill progression (PR B, spec §6): one row per (crew member, skill); seeded to
// STARTING_LEVELS[role][skill] by createNewGame / the v8 backfill; Task 13 adds xp + level-ups.
export const crewSkills = sqliteTable('crew_skills', {
id: integer('id').primaryKey({ autoIncrement: true }),
saveId: integer('save_id').notNull().references(() => saves.id),
crewId: integer('crew_id').notNull().references(() => crew.id),
skill: text('skill').notNull(), // SkillName
level: integer('level').notNull().default(0), // 0–5
xp: integer('xp').notNull().default(0),
}, (t) => [uniqueIndex('crew_skills_crew_skill_unique').on(t.crewId, t.skill)])