SPEC-199 — Skill-First Architecture

Current ownership boundary: substrate stores durable facts; agents own judgment, policy, and authoring loops.

Source: docs/design/spec-199-skill-first-architecture.md

SPEC-199 — Skill-First Architecture: Substrate as Store, Agents as Skills

Status: adopted 2026-05-22. Owner: platform. Related: SPEC-001 (polymorphic substrate), SPEC-013 (skills as artifacts), SPEC-046 (scheduled workers), SPEC-052 (gap detection), skills/MAINTENANCE.md.

1. Principle

Substrate is the data/metadata store and the transactional mechanism. Agents do the thinking, expressed as versioned Skills.

Content manipulation, recurring maintenance, and economic/governance policy belong in skills (SPEC-013 artifacts), run by agents — not hardcoded in substrate Python. Substrate keeps the schemas, APIs, hooks, and atomic guarantees those skills call. This is the norm for all new code.

2. The decision rule (where does X go?)

For any capability, ask: does this require atomicity, data integrity, or a constraint the DB must enforce?

  • YES → STAYS in substrate as a verb/worker/hook. Ledger postings, balance math, idempotent state transitions, vote tallying, gate-evaluation writes, link stamping, content-hash versioning, embeddings/search indexes, partition/matview maintenance, auth, schema validation. You cannot make a double-entry ledger a “skill.”

  • NO, it’s judgment / content / policy → SKILL. What to write and how (authoring, summarizing, classifying, extracting), when/whether to act (funding, betting, promoting, moderating, flagging), and the rationale behind thresholds and weights.

  • Mixed → HYBRID. Keep the deterministic/transactional pass + execution harness in substrate; move the judgment/prompt to a skill the worker loads. Reference implementation: scheduled_workers/linking_maintenance (Pass 0/1 deterministic in substrate; Pass 2 loads skills/entity-linking/SKILL.md).

3. The three migration patterns

3.1 LLM worker → load policy from a versioned SKILL.md

An LLM-driven worker/verb must NOT hardcode its system/user prompt. It loads the prompt body from a SKILL.md (a SPEC-013 artifact: versioned, signal-rated, improvable) and degrades to a minimal inline fallback when the skill or the LLM runtime is absent. Pattern: _load_skill("<name>") → prepend to cached_system (see linking_maintenance/runner.py). The deterministic selection/audit/budget scaffolding (scheduled_workers/_runner.py, _policy.py, _llm.py) stays.

3.2 Policy constant → config authored by a policy skill

Magic thresholds/weights/decay constants baked into mechanism verbs are policy. Externalize them the way senate.gate_define already does: policy lives as data (a config row / gate row), the mechanism verb just reads and applies it, and a policy skill authors/justifies the values. New mechanism verbs must read tunables from config, never hardcode them.

3.3 Content/persona/seed corpus → lives in scidex-agents

Canned scientific/editorial content, persona prompts/contexts, the mimeo/template seed corpus, and the agent outer-loop driver are agent material, not store material. Their canonical home is the scidex-agents repo. Relocation is gated on that corpus being reliably deployed in the substrate runtime (see §6).

4. Classified catalog (2026-05-22 inventory)

4.1 Scheduled workers / watchdogs

  • STAYS (~50+ workers + 4 watchdogs): all gap_detector_*, wiki_parse_on_write, wiki_section_miner, dividend/deadline/budget/ calibration ticks, embed/dedup/evidence-ledger/matview/partition maintenance, federation I/O, trust/score/popularity/portfolio recompute, senate_gate_evaluator, corpus_quality_sweep, all watchdogs. Plus detector workers: spec_drift_auditor (iter-11 #2096), schema_registry_auditor (iter-170/172/174 #2196/#2200/#2201, operationally complete with 12 tests), parity_inventory_auditor (iter-179/180/181 #2204/#2205/#2206/#2207/#2208, 16 tests; sister resolver agents #99 iter-178), demo_flow_health_auditor (iter-210/211 #2218/#2219, 8 unit tests; sister resolver agents #101 iter-209; SPEC-113 §3.7 status observer). Deterministic; no judgment.

  • HYBRID: linking_maintenance (done — the template); ✓ persona_drift_daily narration → persona-drift-narration skill (this iter); visual_artifact_generator (selection stays; prompt templates → skill — not yet done).

  • SKILL (LLM, hardcoded prompts — migrate): progress table (✓ = wired via _skill.load_skill_body + inline fallback; PR refs):

    • proposal_from_* ×6 + proposal_feed_from_unreplicated now queue agent_work_packet artifacts; proposal authorship runs in scidex-agents

    • brief_writer_daily + daily_digestdigest-authoring (#2091)

    • falsification_scorerfalsification-scoring (#2092)

    • scrutiny_workerscrutiny-scoring (#2092)

    • comment_classifiercomment-classification (#2094)

    • debate_outcome_synthdebate-outcome-synthesis (#2094)

    • cross_disease_analogycross-disease-analogy (#2094)

    • prediction_minerprediction-mining (iter-99 #2099).

    • open_question_from_falsifiedresidual-question-from-falsification (iter-99 #2099; consolidated 1-prompt shape).

    • claim_extractorclaim-extraction (iter-99 #2099; 3-prompt unified consolidation per the iter-34 N-modes pattern).

    • persona_drift_dailypersona-drift-narration (iter-100 #2107 HYBRID; narration text from skill, statistical selection stays).

    Wave-2 SKILL tier 100% complete at iter-100; all ☐ above closed.

4.2 Economics / governance — mechanism STAYS, policy EXTRACTS

  • STAYS (mechanism): LMSR/CLR/Brier math, all ledger postings, wallet.*, budget.reserve/settle, agent_budgets.spend, market/proposal/gate state transitions, vote tally, dividend_tick transactional core, recusal/self-deal guards. Do NOT move these out of the DB.

  • POLICY (extract to config + policy skill): progress table after the iter-40 audit revealed the catalog overstated remaining work — several constants are ALREADY env-driven via scheduled_workers._policy.f_env.

    • scrutiny deviation_threshold (env: SCIDEX_SCHED_SCRUTINY_WORKER_DEVIATION_THRESHOLD).

    • ✓ 7 scheduled workers’ usd_cap / limit / lookback / max_tokens (env: per-worker SCIDEX_SCHED_<NAME>_*).

    • ✓ Per-worker LLM model selection (env).

    • senate_proposal_resolve MIN_QUORUM (env: SCIDEX_SENATE_MIN_QUORUM, #2119).

    • pantheon_debate_resolve _INCONCLUSIVE_MARGIN (env: SCIDEX_PANTHEON_INCONCLUSIVE_MARGIN, this PR).

    • score.py DEFAULT_WEIGHTS (env: per-kind SCIDEX_SUBSTRATE_SCORE_WEIGHT_<KIND>; PR 104 era, pre-Wave-3-session). iter-46 audit caught this — was incorrectly listed as ☐ in the catalog. Score decay also has env overrides.

    • agent_trust_score 4-weight group (W_CALIBRATION / W_DIVIDENDS / W_SIGNAL_VOLUME / W_AGE) → env vars (SCIDEX_TRUST_W_*) + trust-score-weight-policy skill (this PR). 2nd full-shape Wave-3 entry; includes a sum-to-1.0 invariant check that warns at startup if violated.

    • trust_propagate 4-constant group (_DAMPING / _DECAY_HALFLIFE_DAYS / _SIGNAL_THRESHOLD / _INTRINSIC_DEFAULT) → env vars (SCIDEX_TRUST_*) + trust-propagation-policy policy skill (this PR). First constant-GROUP externalization combining the lightweight env-var pattern with a documented policy skill per the SPEC-199 §4.2 "config

      • policy skill" model — full Wave-3 shape on a single subsystem.

    • 🔶 dividend_backprop Python constants — iter-99 env-var externalization of PHASE1_BASE_DIVIDEND (1.0), DECAY_BASE (0.5), and the 3 _CALIB_MULTIPLIER_* decile multipliers (1.5/1.0/0.5). Envs: SCIDEX_DIVIDEND_PHASE1_BASE, SCIDEX_DIVIDEND_DECAY_BASE, SCIDEX_DIVIDEND_CALIB_{TOP,MID,BOT}. Smoke-verified safe: 33 dividend_backprop tests + 21 cross-repo CI tests at iter-99 (cross-repo CI grew to 35 tests across 13 guards by iter-126); all pass; defaults unchanged. The remaining ☐ piece is the dividend_tick SQL-embedded damping=0.85 / recency 1.0 literals — those live in raw SQL queries and need a different parameterization shape; deferred per the user’s ledger-touching risk flag.

    • tier_promote falsification cutoffs — iter-96 extracted 0.7/0.5 from inline literals to module-level constants (FALSIFICATION_HARD_BLOCK_THRESHOLD, FALSIFICATION_CAVEAT_THRESHOLD); iter-97 wrapped them in _env_float for operator override (envs: SCIDEX_TIER_FALSIFICATION_HARD_BLOCK_THRESHOLD, SCIDEX_TIER_FALSIFICATION_CAVEAT_THRESHOLD). Full Wave-3 done.

    • calibration_recompute _DECILE_WINDOW_DAYS / _DECILE_MIN_EVENTS (envs: SCIDEX_CALIBRATION_DECILE_*, this PR).

    • market _DEFAULT_LMSR_B is the FALLBACK for markets without an explicit lmsr_b DB column value; per-market liquidity is already data-driven (row["lmsr_b"] from the markets table). iter-46 audit finding — was incorrectly listed as ☐. The hardcoded fallback constant is acceptable since real markets carry per-row policy.

    • managed_runtime MIN_CITATION_COUNT (env: SCIDEX_MANAGED_RUNTIME_MIN_CITATION_COUNT, this PR).

    • squad_fork _FORK_SEED_FRACTION (env: SCIDEX_SQUAD_FORK_SEED_FRACTION, this PR).

    • atlas/agent_personality_content_drift DEFAULT_THETA (env: SCIDEX_PERSONALITY_DRIFT_THETA, iter-117 #2174 — SPEC-177 §7.2 personality.md drift threshold; 9th explicit externalization).

    • watchdogs/link_checker/policy MAX_PAGES_PER_RUN / WARN_BROKEN_LINKS / CRIT_BROKEN_LINKS (envs: SCIDEX_LINK_CHECKER_*, iter-118 #2175; 10th externalization).

    • ✓ 10× gap_detector_* _DEFAULT_PRIORITY constants (envs: SCIDEX_GAP_PRIORITY_<DETECTOR>, iter-119 #2176 via new shared scheduled_workers/_env.py helper module — 11th-20th batch externalizations: UNOWNED_ARTIFACTS=0.3, FAILED_QUALITY_GATES=0.8, UNSCORED_HYPOTHESES=0.6, STALE_MARKETS=0.4, MISSING_PREDICTIONS=0.5, MISSING_EVIDENCE=0.5, UNSCORED_GAPS=0.6, ZERO_VOLUME_MARKETS=0.4, UNTRIAGED_PROPOSALS=0.7, MISSING_REPRODUCE_ATTESTATIONS=0.5). Wave-3 now has 28+ explicit externalizations (iter-314 added the 9th full-shape entry — domain-prioritization-policy, a 4-for-1 lift covering the Biomni-parity quartet).

    • hypothesis_score_update 4-weight group (W_PAPER_QUALITY / W_DEBATE / W_CALIBRATION / W_PAPER_COUNT) + _CALIBRATION_TEMPERATURE → env vars (SCIDEX_HYPOTHESIS_W_* + SCIDEX_HYPOTHESIS_CALIBRATION_TEMPERATURE) + hypothesis-score-weight-policy skill (PR #2231, iter-217). 3rd full-shape Wave-3 entry (after agent_trust_score + trust_propagate); includes a sum-to-1.0 invariant check that warns at startup if violated, and the policy skill documents the adaptive low-evidence redistribution branch + maturity floor that operators must NOT try to replicate via env-var math. 22nd explicit Wave-3 externalization.

    • gap_advance_status 3-threshold intermediate ladder group (_OPEN_TO_INVESTIGATING / _INV_TO_PARTIAL / _PARTIAL_TO_SUBSTANTIAL) → env vars (SCIDEX_GAP_*_THRESHOLD) + gap-status-ladder-policy skill (PR #2232, iter-218). The terminal rung _SUBSTANTIAL_TO_RESOLVED was already env-overridable via SCIDEX_RESOLVED_COMPOSITE_THRESHOLD pre-Wave-3; that PR completed the ladder. 4th full-shape Wave-3 entry; includes a strict-monotonicity invariant warning at startup when env-var drift makes higher rungs unreachable (rungs evaluated top-down in _choose_new_state). 23rd explicit Wave-3 externalization.

    • gap_roi_aggregator 4-tunable reallocation policy (_BOOST_TOP_N / _REDUCE_BOTTOM_N / _BOOST_FACTOR / _REDUCE_FACTOR) → env vars (SCIDEX_GAP_ROI_*) + gap-roi-realloc-policy skill (this PR). 5th full-shape Wave-3 entry; the policy skill documents the boost > reduce asymmetry (intentional design, not a typo: yield is noisy at low mission counts, reducing compounds badly, boosting is self-correcting) and the shadow-vs-apply split. 24th explicit Wave-3 externalization.

    • ✓ Skill taxonomy (this PR, iter-221) — every skill in skills/ is now classified by category + permission_level

      • write_access + owner_role_aliases in scidex_agents.agent_driver.skill_taxonomy in /home/ubuntu/scidex-agents. The taxonomy is the controlled vocabulary the persona-skill runtime filter will read in iter-222; CI guard #18 (test_skill_taxonomy.py, 75 tests across 67 skills) keeps it 1-to-1 with the on-disk catalogue and pins the count. Not a Wave-3 externalization itself (no constants moved), but the foundation step for §5 (“execution-wiring track”). See docs/architecture/skill-taxonomy.md for the controlled vocabulary, the Allen-Institute scientist default profile, and the iter-222/223 follow-up roadmap.

    • falsifier_accountability 4-tunable reputation policy (threshold_strong / delta_successful / delta_rebutted / delta_fabricated) → env vars (SCIDEX_SCHED_FALSIFIER_ACCOUNTABILITY_*, already in place pre-Wave-3) + falsifier-accountability-policy skill (this PR, iter-221b). 6th full-shape Wave-3 entry. Documents the honest-error / bad-faith asymmetry (|delta_fabricated| ≥ 5 × |delta_rebutted|), the positive-expected-value-at-50% invariant (delta_successful > |delta_rebutted|), and the pool-ban-as-the-real-fabrication-penalty design. Signals are not directly ledger-bearing (no apply-flag), but they feed trust-score-weight-policy rollup. 25th explicit Wave-3 externalization.

    • ✓ Participant-trio shared risk-envelope + per-participant defaults (max_position_size / max_drawdown / concentration_cap shared; FUND_FLOOR + top-N for value-funder; sigma_threshold / STAKE_SIZE / model_decay for contrarian; SCIDEX_DIVERSITY_MIN / RECOVERY_THRESHOLD / ALLOCATION_UNIT / EXPLORATION_GRANT / diversity_bonus cap for diversifier) → participant-policy skill, now owned by scidex-agents (iter-305, PR #2245; moved out of the substrate prompt catalog on 2026-05-24). 7th full-shape Wave-3 entry; single doctrine drags 3 participant_* skills off the iter-220 “no-hardcoded-constants” backlog because the tunables are cross-coupled (changing the diversifier’s SCIDEX_DIVERSITY_MIN without looking at the contrarian’s sigma_threshold leaves two participants chasing the same noise). Documents 6 load-bearing invariants (descending max_position_size, inverse-ordered concentration_cap, RECOVERY_THRESHOLD > SCIDEX_DIVERSITY_MIN hysteresis, diversity_bonus_cap ≤ 2.0, …). 26th explicit Wave-3 externalization.

    • ✓ Proposal-authoring 5-dimension weighted feasibility rubric (Technical 0.30 / Resources 0.25 / Regulatory 0.20 / Timeline 0.15 / Risk 0.10) + decision thresholds (≥0.7 ship / 0.4-0.7 conditional / <0.4 drop) + tripwire constants (timeline buffer ≥25%, budget overage ≥30%) + hard-blocker → abstract → mitigation pattern → proposal-feasibility-policy skill (this PR, iter-311). 8th full-shape Wave-3 entry. Documents 6 load-bearing invariants (weights sum to 1.00; monotonically non-increasing weight ordering; two distinct cutoffs not collapsed; buffer/overage are caps not boosts; hard-blocker without concrete mitigation = drop). This originally lifted the proposal authorship surface from 2.6 → ~4.8 on the iter-220 “no-hardcoded-constants” axis. The later work-packet split moves qualitative proposal judgement fully to scidex-agents; substrate proposal-trigger workers no longer load the authoring skill directly. The numerics remain documented here for agent policy reuse. 27th explicit Wave-3 externalization.

    • ✓ Biomni-parity quartet weighted-aggregation constants (causal_gene_prioritization: DisGeNET 0.25 / KG-causal 0.25 / G2P 0.20 / Monarch 0.20 / druggability 0.10; drug_repurposing: Broad 0.40 / reversal 0.30 / sensitivity 0.20 / literature 0.10; rare_disease_diagnosis: Phenodigm 0.45 / Monarch 0.30 / G2P 0.15 / age-onset 0.10; microbiome_analysis: |log2 FC| ≥ 1.5 + FDR < 0.05 + n_taxa ≥ 3 + n_samples ≥ 10 conjunction) + shared cross-domain constants (emit threshold 0.7, evidence floor 0.3, top-3 hypothesis cardinality) → domain-prioritization-policy skill (this PR, iter-314). 9th full-shape Wave-3 entry; single doctrine drags FOUR skills off the iter-220 “no-hardcoded-constants” backlog at once — the largest cross-coupled cluster after participant-policy. The quartet shares the same workflow shape (forge-evidence → per-class scoring → weighted aggregation → conditional emit) and the same emit/floor constants; bundling here keeps them coherent. Documents 6 load-bearing invariants (weights sum to 1.00; non-increasing weight ordering within each domain; emit threshold > evidence floor; microbiome 4-way conjunction; druggability ≤ 0.10 modifier cap; rare-disease top-1 conditional vs other 3 quartet members’ top-3 unconditional). The four quartet skills go v0.1 → v0.2 in the same PR; the qualitative judgement stays in the source skills and the numerics live in this policy doc. Lifts the four sources from 3.0 / 2.2 / 2.0 / 2.4 → ~4.8 on the iter-220 “no-hardcoded- constants” axis — a 4-for-1 design-quality lift. 28th explicit Wave-3 externalization.

    Pragmatic Wave-3 shape: lightweight env-var override with graceful default-on-typo (the iter-40 / iter-41 pattern). Heavier “config row + policy skill” model per SPEC-199 reserved for groups that see enough operator tuning to warrant the authoring effort.

4.3a On-demand verbs with hardcoded LLM prompts (re-sweep addition)

Beyond §4.1’s scheduled-worker catalog, nine skill/verbs/*.py files embed a "You are …" system prompt + LLMCaller/cached_system. Verbs are agent-callable through the API, so their embedded policy is even more visible than worker cron prompts — same pattern, higher leverage.

  • gap_decomposegap-decomposition (#2093, iter-9). First verb-layer migration.

  • gap_elo_round + open_question_elo_roundelo-pairwise-importance (#2127, iter-52). Consolidated; both share the pairwise-importance scoring model. iter-52 also caught a latent-bug fix during migration.

  • percolation_classify → routed to existing comment-classification skill (#2131, iter-55). Shared rubric; no new skill needed.

  • hypothesis_design_experiment + hypothesis_evidence_synthesis + hypothesis_prioritize_experiments → 3 hypothesis-* skills batch (#2125, iter-51). One-batch consolidation per shape.

  • hypothesis_literature_scoutliterature-search-query-generation (#2132, iter-56). FINAL verb migration; closed verb tier 9-of-9.

  • hypothesis_cross_species_inferencecross-species-inference (#2131, iter-55).

Verb tier 9-of-9 CLOSED at iter-56 #2132. All 9 verbs now route through skills. Catalog last refreshed iter-164.

4.3b Non-worker, non-verb modules with hardcoded prompts (re-sweep addition)

Modules outside scheduled_workers/ and skill/verbs/ that hardcode system prompts; same migration pattern applies. Progress:

  • atlas/causal_extractor.pycausal-triple-extraction (#2100).

  • atlas/semantic_extractor.pysemantic-belief-extraction (#2101). First use of the {{PLACEHOLDER}} substitution pattern (multi-line predicate vocab + MAX_TRIPLES int).

  • Retired: persona_runner/llm_decide.py previously mapped to persona-runtime-decision (#2102), but substrate no longer executes provider-backed persona decisions. Agent runtimes in scidex-agents now own prompt assembly and adaptive decision loops.

  • Superseded: ingest/pubmed_update/classifier.py previously used evidence-direction-classification (#2103), but substrate now keeps only the deterministic query-bucket heuristic. Abstract-level evidence-direction judgment is agent-owned.

  • shredder/extractors.pypaper-shredding (#2123, iter-43). Same structured-message-with-cache_control shape as pubmed/classifier; shared system policy across the 3 tool_use extractors (claims, datasets, open_questions) — atomicity, faithfulness, one-kind-per-turn discipline. 3rd Wave-2 layer closed at iter-43.

  • WITHDRAWN skill/tool_runtimes/causal_llm.py — NOT a migration target. Its system_prompt is config-driven (config.get("system_prompt", "…1-line default…")); the policy lives in the CALLER’s config, not the module. The one-line fallback (“expert causal reasoning system; respond with valid JSON only”) is intentionally minimal — extracting it as a skill would not improve governance and would mislead readers about where the policy lives.

4.3c Additional policy constants in mechanism verbs (re-sweep addition) — DONE except 1

Beyond §4.2, hardcoded judgment constants worth externalising as data. Most landed iter-44-46 + iter-117-119 (see §4.2 catalog above for env-vars

  • PR# provenance):

  • agent_trust_score.py W_CALIBRATION/W_DIVIDENDS/W_SIGNAL_VOLUME/W_AGE weights (iter-46; full Wave-3 shape with trust-score-weight-policy skill).

  • trust_propagate.py PageRank constants (iter-44 #2118; full Wave-3 shape with trust-propagation-policy skill).

  • market_trade.py _DEFAULT_LMSR_B=144.27 (iter-46 audit: already data-driven via per-row lmsr_b column; fallback constant acceptable).

  • pantheon_debate_resolve.py _INCONCLUSIVE_MARGIN=0.10 (iter-45).

  • managed_runtime_review_gate.py MIN_CITATION_COUNT=3 (iter-46).

  • squad_fork.py _FORK_SEED_FRACTION=0.10 (iter-46).

  • ☐ Domain evidence-weight vectors in hypothesis_chromatin_tf_binding_synthesis.py and hypothesis_rna_modification_mechanism.py (deferred — domain-specific, lower leverage than verb-tier constants).

See §4.2 catalog for full env-var + iter#/PR# provenance details.

4.3d v1-personas corpus (re-sweep addition)

data/v1-personas.yaml (33KB, 939 lines, persona content) + its loader skill/seed_v1_personas.py — same relocation class as seeds/ but outside the named tree. Move with §4.3 in Wave 4.

4.3 Content / seeds / agent-driver — MOVE to scidex-agents (gated)

  • seeds/mimeos/ (55 bundles), seeds/templates/ (md/yaml/prompt text), seeds/agent_configs/, seeds/labs/scidex-agents/seeds/.... Caveat: seeds/templates/*/policies/*.py are imported (mechanism), not just data — they need an import contract before the in-repo tree is deleted.

  • agent_driver/ (driver + prompts/ + contexts + kris_bootstrap) → scidex-agents (this is the literal agent runtime living in the store).

  • debate/personas._BUILTIN_PROMPTS, seed_personas.INITIAL_PERSONAS[*].instructions → source from the agents corpus; keep the seeding mechanism.

  • STAYS: seeds/schema_registry/ (validation schema), bundle_loader.py, seeding mechanism, skill/persona_decision_skills.py resolver, system_policies.py.

5. Migration waves

  1. Wave 0 (done): cross-linking — linking_maintenance HYBRID worker + entity-linking/artifact-editing/… skills. The reference pattern.

  2. Wave 1 (done, #2089; superseded by work-packet split): A shared proposal authorship skill consolidated the original 7 duplicate prompts. The proposal-trigger workers now queue agent_work_packet artifacts instead of loading that skill in substrate.

  3. Wave 1.5 (done, #2091): digest-authoring consolidates brief_writer_daily

    • daily_digest shared editorial policy.

  4. Wave 2 (DONE — 100% across complete substrate, verified iter-115): skill-ified all LLM workers (§4.1) + on-demand verbs (§4.3a) + non-worker modules (§4.3b) + ingest extractors. iter-37 declared Wave-2 complete for scheduled_workers/; iter-113/114 broadened SPEC-198 audit to ingest/ + skills/ directories and closed 2 final gaps (experiment_extraction + kg_extractor). iter-115 #2173 codified as 8th cross-repo CI guard (test_skill_body_loaded_when_inline_prompt_used). Notable workers/verbs migrated since the iter-37 milestone: causal_triple_extraction (#2117), semantic_belief_extraction (#2118), persona-runtime-decision (#2120), paper-shredding (#2123 final 3rd-layer), gap_decompose, falsification_scorer, scrutiny_worker, prediction_miner, claim_extractor, persona_drift_daily, all 9 verbs from §4.3a — 28+ total skills in the user-facing/authoring/operational catalog.

  5. Wave 2 enrichment (DONE, agents #68 + iter-26 + iter-41 + iter-55-57 cross-refs): all 22 agents-side operational skills carry verified “Substrate verbs you call” sections; rubric skills got cross-refs to peer rubrics (iter-55/56/57); meta-discipline applied to authoring skills (iter-84).

  6. Wave 3 (SUBSTANTIVELY DONE — 21+ explicit externalizations): §4.2 + §4.3c constants externalized via lightweight env-var pattern. Per §4.2 catalog (above), 21+ externalizations through iter-119 batch (atlas DEFAULT_THETA / link_checker / 10× gap_detector). Only ☐ remaining: dividend_tick SQL-embedded literals (different parameterization shape; deferred per ledger-touching risk per user flag).

  7. Wave 4 (gated; 0 of 4 preconditions met as of iter-70 #80): relocate seed/persona/agent-driver corpus (§4.3, §4.3d, §6) to scidex-agents. Operator-facing gate status at scidex-agents/docs/wave-4-seed-corpus-gate-status.md.

Execution-wiring track (orthogonal to migration waves) — done:

  • Mechanism: _select_runtime_skills force-includes discovery.seed_skill_refs (agents #65) — pinned skills run every cycle.

  • Coverage matrix: all 16 personas (6 governance + 10 mission scientists) have seed_skill_refs populated; every core maintenance task has ≥1 guaranteed owner; Allen-Institute scientists get broad access with economics permission-gated (agents #66, skills/MAINTENANCE-COVERAGE.md).

6. Seed-corpus relocation gate (PR #2088 carry-forward)

Operator-facing gate-status tracker: scidex-agents/docs/wave-4-seed-corpus-gate-status.md (iter-70 #80) — current state of each of the 4 preconditions below.

PR #2088 made substrate prefer /home/ubuntu/scidex-agents/seeds with a fallback to the in-repo seeds/. As of 2026-05-22 that path does not exist; substrate runs entirely on the in-repo fallback. The “true delete” of seeds/mimeos + seeds/templates + dropping the fallback must NOT land until:

  1. scidex-agents/seeds/{mimeos,templates}/ is populated with the corpus.

  2. That path is deployed + readable in every substrate runtime that seeds (the scidex.seed.mimeos verb host, scheduler/memory_compactor_registration, and the test suite), or SCIDEX_SEED_CORPUS_DIR is set there.

  3. The importable seeds/templates/*/policies/*.py modules have a stable import home (only template-kg-extractor has a src/ mirror today).

  4. Tests resolve bundles via SCIDEX_SEED_CORPUS_DIR, not the in-repo path.

Until all four hold, keep the fallback. Deleting early creates a fragile hard dependency and breaks seeding/tests.

7. The norm for new code

  • New agent-performable work is authored as a SKILL.md first; the substrate side is only the minimal verb/hook it calls.

  • New LLM workers/verbs load their prompt from a skill; never hardcode.

  • New tunable thresholds/weights are config a policy skill authors; never module constants.

  • New canned content/personas live in scidex-agents, not substrate.

  • Reviewers should push back on PRs that add hardcoded prompts, magic policy constants, or canned content to substrate.