Lint rule: block new UPDATE tasks SET status outside advanceTaskPhase (B2 Phase 1 step 5/5)
This commit is contained in:
1 parent
ab4afba9c7
commit
411294afa6
4 files changed
+41
No files matched your search
@@ -76,5 +76,42 @@ export default tseslint.config(
|
||||
...globals.vitest,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// B2 Phase 1 step 5/5 — block new raw status-writes on tasks.
|
||||
//
|
||||
// During dual-write every `UPDATE tasks SET status = ...` must go
|
||||
// through `advanceTaskPhase` (or the four grandfathered sites in
|
||||
// database-base.ts / database/tasks-*.ts / database/runs.ts that
|
||||
// already dual-write and carry `eslint-disable-next-line
|
||||
// no-restricted-syntax` with a comment explaining the exemption).
|
||||
// The no-restricted-syntax rule below intercepts string literals
|
||||
// and template literals whose text starts with the offending SQL
|
||||
// prefix. New code that needs to mutate `tasks.status` must either
|
||||
// route through `advanceTaskPhase` (preferred) or add an
|
||||
// `eslint-disable-next-line` with a rationale comment.
|
||||
//
|
||||
// Migrations are deliberately excluded — schema changes operate on
|
||||
// the legacy column by design.
|
||||
files: ["src/main/**/*.ts"],
|
||||
ignores: ["src/main/database/migrations/**/*.ts"],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
// Plain string literals and normal template heads.
|
||||
selector:
|
||||
"Literal[value=/\\b[Uu][Pp][Dd][Aa][Tt][Ee]\\s+tasks\\s+[Ss][Ee][Tt]\\s+status\\b/]",
|
||||
message:
|
||||
"B2 Phase 1: write task.status through advanceTaskPhase (or tag with `// eslint-disable-next-line no-restricted-syntax` + dual-write rationale for grandfathered sites).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"TemplateElement[value.raw=/\\b[Uu][Pp][Dd][Aa][Tt][Ee]\\s+tasks\\s+[Ss][Ee][Tt]\\s+status\\b/]",
|
||||
message:
|
||||
"B2 Phase 1: write task.status through advanceTaskPhase (or tag with `// eslint-disable-next-line no-restricted-syntax` + dual-write rationale for grandfathered sites).",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -422,6 +422,7 @@ export class AppDatabaseBase {
|
||||
interruptedCount = Number(result.changes);
|
||||
if (interruptedCount > 0) {
|
||||
this.db.prepare(
|
||||
// eslint-disable-next-line no-restricted-syntax -- B2 Phase 1 grandfathered: crash-recovery sweep; trigger mirrors status→phase, step 3/5 already drops shadow leases below
|
||||
`update tasks set status = 'blocked', updated_at = ? where id in (select task_id from runs where status = 'interrupted' and finished_at = ?) and status in ('in_progress', 'todo')`,
|
||||
).run(ts, ts);
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ export function buildRunMethods(
|
||||
// NOT EXISTS clause is the canonical "no live owner" check.
|
||||
getDb()
|
||||
.prepare(
|
||||
// eslint-disable-next-line no-restricted-syntax -- B2 Phase 1 grandfathered: finishRun's "runNeverStarted" rollback; trigger mirrors status→phase, shadow lease already dropped above
|
||||
`UPDATE tasks
|
||||
SET status = 'todo',
|
||||
checkout_run_id = NULL,
|
||||
|
||||
@@ -377,6 +377,7 @@ export function buildTaskStatusMethods(
|
||||
): void {
|
||||
const ts = nowIso();
|
||||
getDb()
|
||||
// eslint-disable-next-line no-restricted-syntax -- B2 Phase 1 grandfathered: crash-recovery retry bounces task back to todo; trigger mirrors status→phase, no lease held at this point
|
||||
.prepare("update tasks set status = 'todo', updated_at = ? where id = ?")
|
||||
.run(ts, taskId);
|
||||
getDb()
|
||||
@@ -402,6 +403,7 @@ export function buildTaskStatusMethods(
|
||||
markInterruptedTaskFailed(taskId: string): void {
|
||||
const ts = nowIso();
|
||||
getDb()
|
||||
// eslint-disable-next-line no-restricted-syntax -- B2 Phase 1 grandfathered: crash-recovery terminal transition; trigger mirrors status→phase, lease was already cleared by interruptStaleRuns
|
||||
.prepare("update tasks set status = 'cancelled', updated_at = ? where id = ?")
|
||||
.run(ts, taskId);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user