Template for "rebuild a retired theme" tasks. Each concrete rebuild
spec references this template rather than duplicating the acceptance
criteria. When you see {{PLACEHOLDER}}, fill it in from the per-theme
spec.
One of the themes in docs/design/retired_scripts_patterns.md must be
rebuilt as a continuous process. The old implementation was a cluster
of ~N one-off scripts that rotted. This task replaces them with a
single, robust, LLM-first recurring process that gets better over time
without operator intervention.
Theme: {{THEME_ID}} — {{THEME_NAME}}
Layer: {{LAYER}}
Spec anchor: docs/design/retired_scripts_patterns.md#{{THEME_ID}}
Read these in order before starting:
docs/design/retired_scripts_patterns.md — the "Design principlesCLAUDE.md + AGENTS.md for worktree rules and commit standards.KEYWORDS = ["therapeutic", "clinical", ...], stop and writeinformation_schema to discover shape.
scripts/one_off_X.py.
sqlite3.connect. SciDEX uses PG viascidex.core.database.get_db(); Orchestra via orchestra CLI/MCP.
The core judgment in this process is {{CORE_JUDGMENT}}. That
judgment is made by a versioned LLM rubric:
theme_rubric with columns(theme_id, version, prompt, output_schema_json, created_at).
rubric_version that producedWhere tempting to use a regex: ask yourself, "will content three
months from now still match this pattern?" If not, it's a rubric job.
Define the predicate for "this row needs work":
{{GAP_PREDICATE}}. The process does:
SELECT <minimal_columns>
FROM <content_table>
WHERE <gap_predicate>
ORDER BY <priority_signal> DESC
LIMIT <batch_size>Where priority_signal is itself a learned quality score, not
hardcoded "newest first".
Batch size ≤ 50 per run. Writes:
INSERT INTO <output_table> (..., rubric_version, model, run_id)
VALUES (...)
ON CONFLICT (<idempotency_key>) DO UPDATE SET ...
WHERE EXCLUDED.rubric_version > <output_table>.rubric_versionrun_id is the row from <theme>_runs that produced this output;
every output row can be traced back to its run.
Three tables:
<theme>_runs(run_id, started_at, finished_at, items_considered,<theme>_audit_log(id, run_id, entity_id, before_json, after_json,<theme>_outcome_feedback(id, output_row_id, outcome_metric,POST /api/{{LAYER_SLUG}}/{{THEME_SLUG}}/run —run_id. Query param ?dry_run=1{{CADENCE}} and description pointing at this spec.
{{LAYER_SLUG}}__{{THEME_SLUG}}_run so agents canAll three call the same underlying scidex.{{LAYER_SLUG}}.{{THEME_SLUG}}.run_once() function. Zero code duplication.
A separate recurring task (one per theme, shared pattern) correlates
this theme's outputs with downstream quality signals:
<theme>_outcome_feedback rows. A secondtheme_rubric_proposal for**This loop is the difference between "process that gets better over
time" and "process that stays at day-one quality forever."**
Thresholds (batch size, priority weights, gap-predicate constants)
live in a PG theme_config(theme_id, key, value, updated_at, reason)
table. A meta-worker re-evaluates them monthly based on runs table
metrics. Operators can tune via SQL without code deployment.
Never write THINNESS_THRESHOLD = 500 as a module constant.
Use (or create, once) these shared primitives:
scidex.core.external_source_client — throttled HTTP + cache + retry.scidex.core.llm_rubric_judge — rubric+input → structured verdict,scidex.core.versioned_upsert — PG ON CONFLICT with version stamp.scidex.core.priority_batch_selector — gap-predicate + priorityscidex.core.run_metrics — context manager that opens a run row,scidex.core.rubric_registry — load rubric by theme+version.The process should operate on "any table matching shape S", not "the
hypotheses table specifically". Example: thin-content enrichment should
take (table, prose_column, priority_column) as config and work over
hypotheses, wiki pages, experiments, analyses uniformly. One process,
N content types.
Reviewer checks before merge:
<theme>_runs + <theme>_audit_log tables created, populated.llm_rubric_judge primitive (versioned).<theme>_outcome_feedback tabletheme_config, not module constants.<theme>_runs, <theme>_audit_log,<theme>_outcome_feedback, theme_rubric (if first theme), seedtheme_rubric with v1.
scidex.{{LAYER_SLUG}}.{{THEME_SLUG}}.run_once() usingorchestra create ...).<theme>_runs row, audit log entries,{{THEME_SLUG}}.py that does the work. (Deliver atheme_config.docs/design/retired_scripts_patterns.md — design principles +docs/planning/specs/rebuild_theme_template_spec.md — this file.scidex.core.database (PG dispatcher)scidex.core.db_connect (raw PG)orchestra get --id, orchestra list --search,orchestra cost report, orchestra backoff --status --json.