Skip to content

Commit 1f9a91c

Browse files
authored
Merge pull request #617 from PlanExeOrg/chore/rename-task-item-to-plans
chore: rename database table task_item -> plans
2 parents dffdd7a + 7da99a7 commit 1f9a91c

8 files changed

Lines changed: 197 additions & 96 deletions

File tree

‎database_api/model_planitem.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class PlanState(enum.Enum):
4141

4242

4343
class PlanItem(db.Model):
44-
__tablename__ = "task_item"
44+
__tablename__ = "plans"
4545

4646
# A unique identifier for the task.
4747
id = db.Column(UUIDType(binary=False), default=uuid.uuid4, primary_key=True)

‎docs/plan.md‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,6 @@ Verdict: Sub-Human. It provides the architecture for the math, but cannot execut
148148

149149
# Secondary issues
150150

151-
## Rename table name
152-
153-
rename PlanItem (task_items) to (plan_items)
154-
155-
156151
## Show commit id
157152

158153
use the git commit + branch in the generated report, so I can troubleshoot what version of PlanExe was used.

‎frontend_multi_user/src/admin_routes.py‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def _get_purge_activity_info() -> dict[str, Any]:
115115
"SELECT count(*), "
116116
"count(run_track_activity_jsonl), "
117117
"coalesce(sum(octet_length(run_track_activity_jsonl)), 0) "
118-
"FROM task_item"
118+
"FROM plans"
119119
)).fetchone()
120120
if row:
121121
info["total_rows"] = row[0]
@@ -125,10 +125,10 @@ def _get_purge_activity_info() -> dict[str, Any]:
125125
for keep_n in [10, 25, 50, 100, 250, 500]:
126126
result = conn.execute(text(
127127
"SELECT coalesce(sum(octet_length(run_track_activity_jsonl)), 0), count(*) "
128-
"FROM task_item "
128+
"FROM plans "
129129
"WHERE run_track_activity_jsonl IS NOT NULL "
130130
"AND id NOT IN ("
131-
" SELECT id FROM task_item "
131+
" SELECT id FROM plans "
132132
" ORDER BY timestamp_created DESC "
133133
" LIMIT :keep_n"
134134
")"
@@ -151,11 +151,11 @@ def _purge_activity_data(keep_n: int) -> dict[str, Any]:
151151
try:
152152
with db.engine.connect() as conn:
153153
row = conn.execute(text(
154-
"UPDATE task_item "
154+
"UPDATE plans "
155155
"SET run_track_activity_jsonl = NULL, run_track_activity_bytes = NULL "
156156
"WHERE run_track_activity_jsonl IS NOT NULL "
157157
"AND id NOT IN ("
158-
" SELECT id FROM task_item "
158+
" SELECT id FROM plans "
159159
" ORDER BY timestamp_created DESC "
160160
" LIMIT :keep_n"
161161
")"
@@ -168,15 +168,15 @@ def _purge_activity_data(keep_n: int) -> dict[str, Any]:
168168
return result
169169

170170

171-
def _vacuum_task_item() -> dict[str, Any]:
171+
def _vacuum_plans() -> dict[str, Any]:
172172
result: dict[str, Any] = {"error": None}
173173
try:
174174
with db.engine.connect() as conn:
175175
conn.execution_options(isolation_level="AUTOCOMMIT").execute(
176-
text("VACUUM FULL task_item")
176+
text("VACUUM FULL plans")
177177
)
178178
except Exception as e:
179-
logger.exception("Failed to vacuum task_item")
179+
logger.exception("Failed to vacuum plans")
180180
result["error"] = str(e)
181181
return result
182182

@@ -329,7 +329,7 @@ def admin_database():
329329
keep_n = 50
330330
purge_result = _purge_activity_data(keep_n)
331331
elif action == "vacuum":
332-
vacuum_result = _vacuum_task_item()
332+
vacuum_result = _vacuum_plans()
333333
size_info = _get_database_size_info()
334334
purge_info = _get_purge_activity_info()
335335
admin_ext = current_app.extensions.get("admin", [None])

‎frontend_multi_user/src/app.py‎

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -373,26 +373,59 @@ def __init__(self):
373373
self.db = db
374374
self.db.init_app(self.app)
375375

376+
def _ensure_plans_table_name() -> None:
377+
"""Rename legacy 'task_item' (and its indexes) to 'plans' (idempotent).
378+
379+
Serialized via a Postgres advisory lock so concurrent replicas do not
380+
race. Must run before db.create_all() so SQLAlchemy does not create
381+
a second empty 'plans' table alongside 'task_item'.
382+
"""
383+
sql = """
384+
DO $$
385+
BEGIN
386+
PERFORM pg_advisory_xact_lock(8462357421);
387+
IF EXISTS (SELECT 1 FROM information_schema.tables
388+
WHERE table_schema = current_schema() AND table_name = 'task_item')
389+
AND NOT EXISTS (SELECT 1 FROM information_schema.tables
390+
WHERE table_schema = current_schema() AND table_name = 'plans') THEN
391+
ALTER TABLE task_item RENAME TO plans;
392+
END IF;
393+
IF EXISTS (SELECT 1 FROM pg_indexes
394+
WHERE schemaname = current_schema() AND indexname = 'idx_task_item_user_id_timestamp_created') THEN
395+
ALTER INDEX idx_task_item_user_id_timestamp_created RENAME TO idx_plans_user_id_timestamp_created;
396+
END IF;
397+
IF EXISTS (SELECT 1 FROM pg_indexes
398+
WHERE schemaname = current_schema() AND indexname = 'idx_task_item_api_key_id') THEN
399+
ALTER INDEX idx_task_item_api_key_id RENAME TO idx_plans_api_key_id;
400+
END IF;
401+
END$$;
402+
"""
403+
try:
404+
with self.db.engine.begin() as conn:
405+
conn.execute(text(sql))
406+
except Exception as exc:
407+
logger.warning("Rename task_item -> plans failed: %s", exc, exc_info=True)
408+
376409
def _ensure_planitem_artifact_columns() -> None:
377410
insp = inspect(self.db.engine)
378-
columns = {col["name"] for col in insp.get_columns("task_item")}
411+
columns = {col["name"] for col in insp.get_columns("plans")}
379412
with self.db.engine.begin() as conn:
380413
if "generated_report_html" not in columns:
381-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS generated_report_html TEXT"))
414+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS generated_report_html TEXT"))
382415
if "run_zip_snapshot" not in columns:
383-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS run_zip_snapshot BYTEA"))
416+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS run_zip_snapshot BYTEA"))
384417
if "run_track_activity_jsonl" not in columns:
385-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS run_track_activity_jsonl TEXT"))
418+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS run_track_activity_jsonl TEXT"))
386419
if "run_track_activity_bytes" not in columns:
387-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS run_track_activity_bytes INTEGER"))
420+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS run_track_activity_bytes INTEGER"))
388421
if "run_activity_overview_json" not in columns:
389-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS run_activity_overview_json JSON"))
422+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS run_activity_overview_json JSON"))
390423
if "run_artifact_layout_version" not in columns:
391-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS run_artifact_layout_version INTEGER"))
424+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS run_artifact_layout_version INTEGER"))
392425
if "stop_requested" not in columns:
393-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS stop_requested BOOLEAN"))
426+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS stop_requested BOOLEAN"))
394427
if "stop_requested_timestamp" not in columns:
395-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS stop_requested_timestamp TIMESTAMP"))
428+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS stop_requested_timestamp TIMESTAMP"))
396429

397430
def _ensure_token_metrics_columns() -> None:
398431
insp = inspect(self.db.engine)
@@ -460,7 +493,7 @@ def _ensure_multi_api_key_columns() -> None:
460493
statements = (
461494
"ALTER TABLE user_api_key ADD COLUMN IF NOT EXISTS label VARCHAR(128)",
462495
"ALTER TABLE user_api_key ADD COLUMN IF NOT EXISTS key_plaintext VARCHAR(64)",
463-
"ALTER TABLE task_item ADD COLUMN IF NOT EXISTS api_key_id VARCHAR(36)",
496+
"ALTER TABLE plans ADD COLUMN IF NOT EXISTS api_key_id VARCHAR(36)",
464497
"ALTER TABLE credit_history ADD COLUMN IF NOT EXISTS api_key_id VARCHAR(36)",
465498
"ALTER TABLE token_metrics ADD COLUMN IF NOT EXISTS api_key_id VARCHAR(36)",
466499
)
@@ -473,38 +506,38 @@ def _ensure_multi_api_key_columns() -> None:
473506

474507
def _ensure_step_count_columns() -> None:
475508
insp = inspect(self.db.engine)
476-
columns = {col["name"] for col in insp.get_columns("task_item")}
509+
columns = {col["name"] for col in insp.get_columns("plans")}
477510
with self.db.engine.begin() as conn:
478511
if "steps_completed" not in columns:
479-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS steps_completed INTEGER"))
512+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS steps_completed INTEGER"))
480513
if "steps_total" not in columns:
481-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS steps_total INTEGER"))
514+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS steps_total INTEGER"))
482515
if "current_step" not in columns:
483-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS current_step VARCHAR(128)"))
516+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS current_step VARCHAR(128)"))
484517

485518
def _ensure_failure_diagnostic_columns() -> None:
486519
insp = inspect(self.db.engine)
487-
columns = {col["name"] for col in insp.get_columns("task_item")}
520+
columns = {col["name"] for col in insp.get_columns("plans")}
488521
with self.db.engine.begin() as conn:
489522
if "failure_reason" not in columns:
490-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS failure_reason VARCHAR(64)"))
523+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS failure_reason VARCHAR(64)"))
491524
if "failed_step" not in columns:
492-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS failed_step VARCHAR(128)"))
525+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS failed_step VARCHAR(128)"))
493526
if "recoverable" not in columns:
494-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS recoverable BOOLEAN"))
527+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS recoverable BOOLEAN"))
495528
# Rename last_error -> error_message in a separate transaction so a
496529
# failed RENAME (race with another container) doesn't poison the above.
497530
if "error_message" not in columns:
498531
if "last_error" in columns:
499532
try:
500533
with self.db.engine.begin() as conn:
501-
conn.execute(text("ALTER TABLE task_item RENAME COLUMN last_error TO error_message"))
534+
conn.execute(text("ALTER TABLE plans RENAME COLUMN last_error TO error_message"))
502535
except Exception:
503536
with self.db.engine.begin() as conn:
504-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS error_message VARCHAR(256)"))
537+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS error_message VARCHAR(256)"))
505538
else:
506539
with self.db.engine.begin() as conn:
507-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS error_message VARCHAR(256)"))
540+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS error_message VARCHAR(256)"))
508541

509542
def _ensure_stopped_state() -> None:
510543
"""Add 'stopped' value to the planstate/taskstate enum type (idempotent).
@@ -527,23 +560,23 @@ def _ensure_stopped_state() -> None:
527560

528561
def _ensure_last_progress_at_column() -> None:
529562
insp = inspect(self.db.engine)
530-
columns = {col["name"] for col in insp.get_columns("task_item")}
563+
columns = {col["name"] for col in insp.get_columns("plans")}
531564
if "last_progress_at" not in columns:
532565
with self.db.engine.begin() as conn:
533-
conn.execute(text("ALTER TABLE task_item ADD COLUMN IF NOT EXISTS last_progress_at TIMESTAMP"))
566+
conn.execute(text("ALTER TABLE plans ADD COLUMN IF NOT EXISTS last_progress_at TIMESTAMP"))
534567

535568
def _ensure_planitem_indexes() -> None:
536569
insp = inspect(self.db.engine)
537570
table_names = set(insp.get_table_names())
538571
statements: list[str] = []
539-
if "task_item" in table_names:
572+
if "plans" in table_names:
540573
statements.append(
541-
"CREATE INDEX IF NOT EXISTS idx_task_item_user_id_timestamp_created "
542-
"ON task_item (user_id, timestamp_created)"
574+
"CREATE INDEX IF NOT EXISTS idx_plans_user_id_timestamp_created "
575+
"ON plans (user_id, timestamp_created)"
543576
)
544577
statements.append(
545-
"CREATE INDEX IF NOT EXISTS idx_task_item_api_key_id "
546-
"ON task_item (api_key_id)"
578+
"CREATE INDEX IF NOT EXISTS idx_plans_api_key_id "
579+
"ON plans (api_key_id)"
547580
)
548581
if "credit_history" in table_names:
549582
statements.append(
@@ -593,6 +626,7 @@ def _create_tables_with_retry(attempts: int = 5, delay_seconds: float = 2.0) ->
593626
with self.db.engine.connect() as lock_conn:
594627
lock_conn.execute(text(f"SELECT pg_advisory_lock({_ADVISORY_LOCK_KEY})"))
595628
try:
629+
_ensure_plans_table_name()
596630
self.db.create_all()
597631
_ensure_planitem_artifact_columns()
598632
_ensure_token_metrics_columns()
@@ -657,7 +691,7 @@ def load_user(user_id):
657691
self.admin = Admin(self.app, name='PlanExe Admin', index_view=MyAdminIndexView())
658692

659693
# Add database tables to admin panel
660-
self.admin.add_view(PlanItemView(model=PlanItem, session=self.db.session, name="Task"))
694+
self.admin.add_view(PlanItemView(model=PlanItem, session=self.db.session, name="Plan"))
661695
self.admin.add_view(AdminOnlyModelView(model=EventItem, session=self.db.session, name="Event"))
662696
self.admin.add_view(WorkerItemView(model=WorkerItem, session=self.db.session, name="Worker"))
663697
self.admin.add_view(NonceItemView(model=NonceItem, session=self.db.session, name="Nonce"))

‎frontend_multi_user/src/plan_routes.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _load_prompt_preview_safe(task_id: Any, max_chars: int = 240) -> str:
113113
except DataError:
114114
db.session.rollback()
115115
logger.warning(
116-
"Detected invalid UTF-8 in task_item.prompt for task_id=%s; using placeholder preview.",
116+
"Detected invalid UTF-8 in plans.prompt for task_id=%s; using placeholder preview.",
117117
task_id,
118118
exc_info=True,
119119
)
@@ -935,7 +935,7 @@ def plan():
935935
)
936936
except DataError:
937937
db.session.rollback()
938-
logger.warning("Detected invalid UTF-8 in task_item.prompt for user_id=%s; falling back.", user_id, exc_info=True)
938+
logger.warning("Detected invalid UTF-8 in plans.prompt for user_id=%s; falling back.", user_id, exc_info=True)
939939
tasks = (
940940
db.session.query(PlanItem.id, PlanItem.timestamp_created, PlanItem.state, PlanItem.stop_requested)
941941
.filter(uid_filter)

‎frontend_multi_user/templates/admin/database.html‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,20 +245,20 @@ <h3>Purge run_track_activity_jsonl</h3>
245245
{% if vacuum_result.error %}
246246
<div class="error-banner">Vacuum failed: {{ vacuum_result.error }}</div>
247247
{% else %}
248-
<div class="success-banner">VACUUM FULL task_item completed. Disk space reclaimed.</div>
248+
<div class="success-banner">VACUUM FULL plans completed. Disk space reclaimed.</div>
249249
{% endif %}
250250
{% endif %}
251251

252252
<div class="purge-section">
253253
<h3>Vacuum</h3>
254254
<p class="purge-stats">
255-
Run <code>VACUUM FULL task_item</code> to rewrite the table and reclaim disk space.
255+
Run <code>VACUUM FULL plans</code> to rewrite the table and reclaim disk space.
256256
This locks the table briefly.
257257
</p>
258-
<form method="POST" onsubmit="return confirm('This will lock the task_item table while it rewrites it. Continue?');">
258+
<form method="POST" onsubmit="return confirm('This will lock the plans table while it rewrites it. Continue?');">
259259
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
260260
<input type="hidden" name="action" value="vacuum">
261-
<button type="submit" class="btn-purge" style="background: #2980b9;">Vacuum task_item</button>
261+
<button type="submit" class="btn-purge" style="background: #2980b9;">Vacuum plans</button>
262262
</form>
263263
</div>
264264

0 commit comments

Comments
 (0)