agents — Module Spec

1. Purpose

agents (module #28) is the agent runtime — the module that actually claims work, makes decisions, executes tools, and can be killed. It is Phase 5 of 6 of the agents-v2/v3 build authorization (Phase 1 reopened platform for the module registry + AI-capacity layer; Phase 2 reopened ai for the model-registry/deployment/prompt/routing layer; Phase 3 built semantics, the shared business ontology; Phase 4 built signals, the bitemporal feature/forecast/outcome store — all 4 already schema-locked before this phase started). Where semantics defines the vocabulary an agent decision reasons about and signals stores the facts and outcomes it reads and writes, agents is the module that actually does something: it is this codebase's first genuinely new orchestration layer, and every prior autonomy mechanism this codebase already had (identity.agent_identity, identity.agent_duty_grant, ai.agent_execution) is a dependency this module consumes, not a competitor it replaces.

agents is not a new design — it is a full merge of 3 documents into one coherent build, not merely the latest delta: v1's own 21-table orchestration baseline (agent_task, agent_thread, agent_event_log, agent_schedule, agent_trigger, agent_eval_suite/_run/_case, agent_performance_profile, agent_shadow_run/_decision, agent_autonomy_profile, agent_action, rollback_recipe/_execution, tool_catalog, agent_tool_grant, agent_catalog_entry/_required_tool, tenant_agent_deployment, agent_incident — never previously built, confirmed live: zero tables existed anywhere in this schema before this phase), merged with v2's amendments (A1–A8) and v3's corrections (BLOCKER 1–8, I1–I8, v3 wins on any conflict). Design of record: vrida-agents-module-design-proposal-2026-07-11.md (v1), vrida-agents-v2-design-amendment-2026-07-11.md (A1–A8), vrida-agents-v3-correction-pass-2026-07-12.md (BLOCKER 1–8, I1–I8).

47 logical tables / 475 columns. Migration: packages/db/migrations/20260716000000_agents_module_new_schema.sql (~1650 lines). Full build record: docs/decisions/PROJECT_DECISIONS.md, Phase 5 entry.

2. Ownership

Owns — 47 tables, 475 columns, grouped by concern (matching the 12 Drizzle source files under packages/db/src/schema/agents/):

Family File Tables Cols Role
Tool/toolset catalog tool.ts tool_definition, tool_version, tool_version_module, toolset_definition, toolset_version, toolset_version_member 6 / 44 A2b version-row catalog pattern. Supersedes v1's flat, unversioned tool_catalog (see DR-1).
Skill catalog + assignment skill.ts skill_definition, skill_version, skill_version_toolset, skill_version_required_duty, skill_version_module, agent_skill_assignment 6 / 36 A2b version-row pattern for skills; agent_skill_assignment is the moved-from-identity activation point (see DR-3, item 8).
Marketplace / deployment marketplace.ts agent_catalog_entry, agent_catalog_entry_required_tool, tenant_agent_deployment, agent_tool_grant 4 / 43 v1's marketplace/entitlement surface — which agents a tenant can deploy, and which tools a deployed agent identity actually holds.
Task orchestration task.ts agent_schedule, agent_trigger, agent_task, agent_thread, agent_event_log 5 / 89 The unit-of-work core (v1 Gap 1, amended A1/A5/E3/BLOCKER4) — atomic claim/fencing/lease, runaway-ceiling caps, kill-switch gate, saga-gate anchor.
Evaluation eval.ts agent_eval_suite, agent_eval_suite_version, agent_eval_run, agent_eval_case, agent_performance_profile 5 / 56 Pre-deployment and ongoing agent quality measurement, version-pinned (see DR-4).
Shadow / autonomy shadow.ts agent_shadow_run, agent_shadow_decision, agent_autonomy_profile 3 / 31 Shadow-mode dry-runs and the tenant's own per-domain autonomy-mode recommendation surface.
Certification certification.ts skill_certification 1 / 13 A3's gate — the pass/fail record validate_skill_activation() requires before a skill can be enabled for an agent.
Decision context / evidence decision_context.ts evidence_retention_policy, decision_context_snapshot, decision_context_manifest, decision_context_feature, decision_context_forecast, decision_context_metric, decision_context_policy, decision_context_knowledge_source 8 / 64 A4/BLOCKER6's evidence envelope — partitioned, append-only, the reconstructible "what did the agent know" record.
Action / rollback / incident action.ts agent_decision, agent_action, rollback_recipe, rollback_execution, agent_incident 5 / 67 The actual write path (BLOCKER2/I1/BLOCKER4) plus the safety-net rollback and incident-tracking tables.
Kill switch kill_switch.ts kill_switch_event, kill_switch_scope_state 2 / 14 BLOCKER7's history/state split.
Policy / capacity policy.ts skill_execution_policy, workload_class 2 / 18 Tenant-bound spend/concurrency ceilings and the AI-capacity workload classification ai.routing_policy now reads.
Total 47 475

Does NOT own:

  • Agent identity/authority itselfidentity.agent_identity, identity.agent_duty_grant (already built and locked). agents reads both constantly (every kill-switch check, every validate_skill_activation() call) but introduces no competing authority mechanism (see §9).
  • The append-only execution ledgerai.agent_execution (already built, Phase 2). This phase amends it (+8 columns, see §10) but the table itself, and its ownership, stays with ai.
  • The shared business ontologysemantics.metric_definition/goal_definition/attribution_model_definition (Phase 3). decision_context_snapshot reads semantics.metric_version but does not duplicate it.
  • The feature/forecast/outcome storesignals.feature_value/forecast/experiment_assignment/outcome_observation (Phase 4). decision_context_snapshot reads signals.feature_value/forecast/experiment_assignment; agent_action is now the real FK target signals.outcome_observation.agent_action_id points at (a forward-ref closed by this phase, not owned by it).
  • The agent_reader role and its dedicated connection helper — deferred to Phase 6 (see §11).

3. Layer & Dependencies

Foundation layer (per SCHEMA_CONVENTIONS.md's foundation/business/consumer taxonomy) — same positioning as ai, approvals, semantics, signals. agents is the runtime that sits on top of all 4.

Depends on:

  • platformtenant.id, module_catalog.id (A1's agent_task.domain/tool_definition.domain/skill_definition.domain retype), polymorphic_target_registry (I6, this phase's own companion table, see §6/§10).
  • identityactor.id (attribution columns throughout), agent_identity.id/.status (the kill-switch fail-closed check reads this directly), agent_duty_grant (both check_agent_execution_not_blocked() and validate_skill_activation() query it).
  • aimodel_version.id, prompt_version.id (both read by decision_context_snapshot and by the retirement-check triggers), agent_execution (amended this phase, not owned by it).
  • semanticsmetric_version.id (real FK from decision_context_snapshot).
  • signalsfeature_value.id, forecast.id, experiment_assignment.id (real FKs from decision_context_snapshot); this phase also closes signals.outcome_observation.agent_action_id/outcome_authority.agent_action_id as real composite FKs into agents.agent_action (a Phase-4-disclosed forward-ref, now wired — see §10).
  • filesdocument_chunk.id (real FK from decision_context_knowledge_source; this phase adds the prerequisite UNIQUE(id, tenant_id) on files.document_chunk as a companion migration step, since it didn't exist before).
  • approvalsapproval_request.id (both ai.agent_execution.approval_request_id and the saga-gate exemption path reference it); this phase widens approval_request's own source_module CHECK to add 'agents'.

Depends on NO nursery-vertical product module — same reverse-dependency discipline every foundation-layer module in this codebase holds itself to.

Depended on by: every future consumer of the agent runtime — agent_reader (Phase 6, not yet built) is the intended tenant-facing read role; no module currently queries agents.* in production code, since no service layer exists yet (see §7).

4. Tables — grouped

See the ownership table in §2 for the 12-family grouping and per-family column counts. Full column-level detail (types, nullability, defaults, CHECK constraints, indexes, triggers, partition mechanics) lives in docs/database/schema_docs/agents.md — not restated here.

Partitioned tables (3, monthly BY RANGE(created_at), per E1): decision_context_snapshot, decision_context_manifest, agent_decision, agent_action — all append-only (REVOKE UPDATE, DELETE + platform.reject_append_only_mutation() on the parent and every partition, the same per-partition DO-loop pattern Phase 2/Phase 4 already established).

5. Capabilities — AI Capability Plane application

agents is not a module that applies the AI Capability Plane to some other business surface (the way crm or pos do) — this module IS the AI Capability Plane's own runtime substrate. Every control the plane names in the abstract (authority level, autonomy mode, spend/step ceilings, kill-switch, evidence trail, shadow-mode evaluation) has a concrete table here:

  • Authoritydraft_only / needs_approval / may_act_alone, the same 3-value vocabulary identity.agent_duty_grant already established, now also on agent_task.autonomy_mode, skill_version_required_duty.minimum_authority_level, and agent_performance_profile.recommended_autonomy_mode — one vocabulary, reused everywhere, never re-invented per table.
  • Stateagent_task.status (pending/in_progress/awaiting_human/completed/failed/canceled), with an atomic UPDATE ... WHERE status='pending' AND (lease_expires_at IS NULL OR lease_expires_at < now()) RETURNING fencing_token claim (E3) — no two workers can claim the same task, and a stale lease is reclaimable.
  • Spend/step ceilingsagent_task.max_steps/max_cost_cents vs. step_count/cost_cents_accrued, enforced BLOCK-not-flag (v1's own explicit decision: an agent runaway is a safety property, not a business-judgment tolerance call like receiving's over-shipment) by enforce_task_runaway_ceiling().
  • Kill-switch — a 5-scope precedence chain (global > tenant > agent_identity > skill/tool/model) resolved through kill_switch_event (history) + kill_switch_scope_state (current state, native-upsert-maintained) — see §6 GUARD 2.
  • Evidencedecision_context_snapshot + its 6 typed child tables (_feature, _forecast, _metric, _policy, _knowledge_source, plus _manifest) is the reconstructible "what did the agent know, and from where" record BLOCKER6 requires — typed FKs into signals/semantics/files/ai, not a single opaque JSONB blob.
  • Shadow-mode / graceful degradationagent_shadow_run/agent_shadow_decision let a skill run in observe-only mode before it is trusted with real autonomy; agent_autonomy_profile is the tenant's own per-domain recommendation surface, fed by agent_performance_profile's measured reliability scores. Every AI-OFF path in this codebase (identity's agent_duty_grant, ai's agent_execution) still works with agents absent entirely — nothing here is a hard runtime dependency for non-agent flows.

⚠ PROHIBITION — read before building the agent runtime. agent_autonomy_profile.current_mode above is a label with no structural enforcement — see docs/open-items/OPEN_ITEMS.md ("Autonomy level is advisory — PROHIBITION until structurally bound"). Structurally binding current_mode to a real enforcement path, live-reproduced, is the first task of the agent-runtime / AgentsService build — not a later hardening pass. Do not ship an executor without it.

6. Enforcement Mechanisms — Trigger-Backed Guards

Unlike semantics/signals (mostly CHECK/EXCLUDE-enforced), agents is trigger-heavy — its own runtime correctness (claim-once, don't-exceed-ceiling, don't-act-while-killed, don't-drift-across-modules) cannot be expressed as a static constraint. 7 critical guards were named up front (matching v3's own BLOCKER numbering) and a genuine 8th (T1) was added during this build's own Section 4 self-audit — disclosed honestly as 8, not forced to fit the original count of 7:

# Guard Trigger function(s) Table Fires on
1 E3 — atomic task claim + fencing (no dedicated trigger — the claim itself is the proven UPDATE ... WHERE ... RETURNING idiom; lease renewal is a deliberately separate statement that does NOT bump fencing_token, closing v2's own self-fencing bug, CC-2) agent_task claim vs. renewal (2 different statements)
2 BLOCKER7 — kill-switch history/state split + resume-safe propagation resolve_kill_switch_state(), propagate_kill_switch_fencing() kill_switch_eventkill_switch_scope_state, agent_task AFTER INSERT on kill_switch_event
3 A5, corrected — kill-switch check fires on task CLAIM, not just creation check_agent_task_not_blocked(), check_agent_execution_not_blocked(), is_agent_blocked() (shared resolver) agent_task, ai.agent_execution BEFORE INSERT OR UPDATE OF status on agent_task (not INSERT-only); BEFORE INSERT on agent_execution
4 runaway-ceiling (v1 Gap 1) — BLOCK not flag enforce_task_runaway_ceiling() ai.agent_execution BEFORE INSERT
5 BLOCKER4 + I1 — saga gate against real writes, plus decision/action consistency enforce_single_module_autonomy(), validate_action_decision_execution_consistency() agent_action BEFORE INSERT (both)
6 A3 + BLOCKER5 + A8 Rule 4 — skill activation: certification + duty-grant authority + spend-ceiling validate_skill_activation() agent_skill_assignment BEFORE INSERT OR UPDATE OF is_enabled
7 A2b retirement rule 1 — retirement blocks new executions ai.reject_retired_tool_version(), agents.reject_retired_decision_context_versions() ai.agent_execution, decision_context_snapshot BEFORE INSERT (both)
8 (T1) Section-4-audit-added — the same retirement-rule split re-tested as its own guard, closing a coverage gap the original 7 didn't explicitly exercise reject_retired_decision_context_versions() (same function as #7, additional test coverage) decision_context_snapshot BEFORE INSERT

Two more trigger functions round out the enforcement surface but weren't part of the "named" count: ai.require_idempotency_key_for_write_tools() (a write-tool execution without an idempotency_key is rejected outright) and ai.validate_agent_memory_source_polymorphic_ref() (GUARD 7/I6 — the real validator for ai.agent_memory_source.source_ref, resolved through the new platform.polymorphic_target_registry + platform.validate_polymorphic_reference(), see §10).

Two genuine PL/pgSQL bugs were found and fixed during this build's own live guard-reproduction (not the read-only Section 4 audit):

  • A record-typed PL/pgSQL variable's IS NOT NULL check is unreliable when used directly as an IF condition — empirically confirmed via a bare DO block (a genuinely non-null, fully-populated record still evaluated as if NULL in that boolean context). This silently swallowed the entire A8 Rule 4 spend-ceiling check inside validate_skill_activation(). Fixed by rewriting to scalar variables, checking each scalar's own NULL-ness directly.
  • SELECT MIN(...) ... FOR UPDATE is syntactically rejected by Postgres ("FOR UPDATE is not allowed with aggregate functions"), also inside validate_skill_activation()'s Point 3. Fixed by splitting into a plain PERFORM ... FOR UPDATE (lock only) followed by a separate SELECT MIN(...) INTO ... (aggregate over the now-locked rows).

One genuine security gap was found and fixed during the Section 4 self-audit itself (item B): kill_switch_event had tenant_id + GRANT SELECT, INSERT TO authenticated but zero RLS policies — any tenant session could read every other tenant's kill-switch history and forge a kill/suspend/resume event against another tenant's agents. Fixed with the same 2-policy mixed-scope RLS pattern already established by agent_eval_suite/rollback_recipe in this same build; live-reproduced clean (cross-tenant SELECT returns 0 rows, forged cross-tenant/global INSERT rejected, legitimate own-tenant INSERT succeeds).

7. Service Contract — AgentsService

No AgentsService exists yet — schema-only this phase, matching every phase of the agents-v2/v3 build so far (Phase 1's platform reopen, Phase 2's ai reopen, Phase 3's semantics build, Phase 4's signals build were all schema-only too). Downstream code (once it exists) would query agents.* directly via Drizzle, and — once agent_reader (Phase 6) is granted — through that dedicated read role rather than the tenant-scoped authenticated connection ordinary application code uses.

The mechanisms that matter most for correctness — the atomic claim/fencing, the kill-switch precedence chain, the runaway ceiling, the saga gate, the skill-activation checks — are NOT waiting on that service layer to be correct: they are already live, atomic, and enforced directly by the schema (see §6) regardless of what future application code does or doesn't do.

8. Data-Flow / Population Model

agents has no first-party UI in this phase and is not populated by a human filling out a form directly, with 2 partial exceptions:

  • agent_schedule/agent_trigger are expected to be tenant-admin-configured (via a future AgentsService/UI), defining when/why a task gets auto-created.
  • agent_task is the entry point — created either by a human (created_by_actor_id set, automation_source='human') or by another agent decision (decision_provenance JSONB traces the originating agent_decision_id, automation_source='agent'). Once created, an elevated worker process claims it via the atomic UPDATE ... WHERE status='pending' ... idiom (E3) — never authenticated directly.
  • decision_context_snapshotagent_decisionagent_action is the append-only chain a claimed task produces: a snapshot of what the agent knew, the decision it made, and (if it wrote anything) the action it took — each partitioned monthly, each write-once.
  • skill_certification is populated by an evaluation pipeline (via agent_eval_run/agent_eval_case), not by a tenant directly — agent_skill_assignment cannot be enabled without a passing, unexpired, unrevoked certification (validate_skill_activation() Point 1).
  • kill_switch_event is the one table any authorized human or system process can write to directly today (GRANT SELECT, INSERT TO authenticated, RLS-scoped) — an emergency-stop path deliberately kept simple and always-available, not routed through the task/decision/action pipeline.

agent_reader (Phase 6, not yet built) is the expected first dedicated read consumer for tenant-facing surfaces; until then, agents.* can be populated and queried by an elevated service connection, but nothing in the live system actually consumes it yet — the same deliberate "build the substrate before the speaker" sequencing semantics/signals established for themselves.

9. Agent Authority Mapping

agents introduces no competing authority mechanism — every authority check in this module's own triggers resolves against identity.agent_duty_grant (already built and locked), never a parallel table:

  • check_agent_execution_not_blocked() requires an active agent_duty_grant row for the specific permission_id referenced on the ai.agent_execution row, and an active agent_tool_grant row for the specific tool_version_id.
  • validate_skill_activation() (GUARD 6) requires a sufficient agent_duty_grant (ordinal-CASE-compared, not a raw text comparison — see the trigger's own inline comment on why 'may_act_alone' >= 'needs_approval' as plain text would silently invert) for every permission skill_version_required_duty names, and binds skill_execution_policy.max_cost_cents against the LOWEST reachable duty-grant spend_limit_cents (A8 Rule 4) — a skill can never grant itself more spend authority than its own weakest required duty grant allows.
  • The kill-switch fail-closed check (check_agent_task_not_blocked()/check_agent_execution_not_blocked()) reads identity.agent_identity.status directly — a killed/suspended agent identity (or one that doesn't resolve at all) is rejected before kill_switch_scope_state is even consulted.

agents is where identity.agent_duty_grant's authority actually gets exercised — this module is the first real consumer proving that mechanism end-to-end (certification, duty sufficiency, spend-ceiling binding, kill-switch precedence) rather than just holding the grant rows.

10. Cross-Module Seams

Cataloged in docs/modules/CROSS_MODULE_CONTRACTS.md (referenced, not restated here). Key relationships built or closed by this phase:

Direction Contract
Agents → Platform tenant.id (every tenant-scoped table), module_catalog.id (A1's agent_task.domain/tool_definition.domain/skill_definition.domain retype — see DR-2), the new platform.polymorphic_target_registry + platform.validate_polymorphic_reference() (I6, built THIS phase as a companion table — the real validator for ai.agent_memory_source.source_ref, a genuinely-unavoidable 4-target polymorphic pointer; split target_schema/target_table columns, not a single free-text string, closing a live SQL-injection vector v3's own independent review found in the single-string draft).
Agents → Identity agent_identity.id/.status, agent_duty_grant (authority checks, see §9), actor.id (attribution throughout).
Agents ↔ AI ai.agent_execution amended, +8 columns: agent_task_id, sequence_index, tool_version_id, approval_request_id, retry_count, blocked_by_policy, confidence_threshold, escalation_reason — the execution ledger now links back to the task that produced it and the tool version it invoked. ai.model_version/ai.prompt_version are read by decision_context_snapshot and retirement-checked by reject_retired_decision_context_versions(). ai.routing_policy.workload_class_id → agents.workload_class(id)a forward-ref CLOSED this phase (disclosed open item since Phase 2, wired same-day, 0 orphans).
Agents ↔ Semantics decision_context_snapshot.metric_version_id → semantics.metric_version.id (real FK) — the decision context's own resolved-metric snapshot.
Agents ↔ Signals decision_context_snapshot reads signals.feature_value/forecast/experiment_assignment (real FKs). signals.outcome_observation.agent_action_id/outcome_authority.agent_action_id → agents.agent_action(id, tenant_id)2 forward-refs CLOSED this phase (disclosed open since Phase 4, wired same-day, 0 orphans; Phase 4's own pre-existing signals-schema.spec.ts fixtures were retrofitted with a real minimal fixture chain rather than left on placeholder UUIDs). agents.decision_context_snapshot's own AFTER INSERT trigger calls signals.lock_experiment_causal_basis() — the trigger statement Phase 4 deferred until this table existed.
Agents → Files decision_context_knowledge_source.document_chunk_id → files.document_chunk(id, tenant_id) (real composite FK) — files.document_chunk gained the prerequisite UNIQUE(id, tenant_id) as a companion migration step this phase, since it didn't carry one before.
Agents ↔ Approvals approvals.approval_request.source_module CHECK widened to add 'agents' (now 11 values total: purchasing, pos, orders, identity, crm, inventory, ai, offers, returns, agents, other) — the saga-gate exemption path (enforce_single_module_autonomy()) and A3's autonomy-promotion path both route through approval_request_id. A pre-existing Drizzle-file/live-DB drift ('offers'/'returns' already live but missing from the Drizzle source) was also closed in the same statement.
Agents (internal, disclosed transitional) identity.agentSkillAssignment (v1, still used by production IdentityService) and agents.agentSkillAssignment (this phase's new destination table, I5) coexist until Phase 6 drops the old one — resolved via explicit named re-exports in packages/db/src/schema/index.ts (see DR-3).

11. Design Rationale

  • DR-1 — tool_catalog (v1) is SUPERSEDED by tool_definition/tool_version, not built at all. v1's flat, unversioned catalog already carried exactly the columns A2b's version-row pattern requires — rather than build a table with zero live rows anywhere and immediately deprecate it, the module is built directly on tool_definition/tool_version from day one. Documented in packages/db/src/schema/agents/tool.ts's own header comment.
  • DR-2 — A1's domain → module_id retype is scoped to EXACTLY 3 columns, per A1's own explicit scope-limiting text: agent_task.domain, tool_definition.domain, skill_definition.domain. It is deliberately NOT extended to agent_performance_profile.domain/agent_autonomy_profile.domain, which stay free-text — a faithful port of v1's literal spec rather than an unstated 4th/5th retarget invented during the build. Documented inline in packages/db/src/schema/agents/eval.ts and shadow.ts.
  • DR-3 — agent_skill_assignment MOVED from identity to agents (I5), the 3rd instance of this pattern codebase-wide (after approvals-out-of-admin, receiving-out-of-purchasing). This resolves a real foundation-layer dependency-direction cycle: identity.agent_skill_assignment → agents.skill_version would be an outbound identity → agents edge, while agents already carries the deep, unavoidable agents → identity edge (agent_identity, actor attribution) — both directions simultaneously violates SCHEMA_CONVENTIONS.md's one-way dependency rule within the foundation layer. Zero live rows existed in identity.agent_skill_assignment at the time of this build (confirmed live), so the new table is built here empty; identity's own DROP of the old table is Phase 6's job, a deliberate build-order exception matching v2 Section 10's own disclosed sequencing. Transitional barrel collision: identity.agentSkillAssignment (still read by production IdentityService) and agents.agentSkillAssignment coexist in packages/db/src/schema/index.ts via explicit named re-exports until Phase 6 drops the old table.
  • DR-4 — agent_eval_run.eval_suite_version_id retargets to agent_eval_suite_version.id, a disclosed judgment call neither v2 nor v3 explicitly addresses. Now that the suite payload has moved to a version row (the same A2b pattern used everywhere else in this build), an eval run's own "which exact configuration was evaluated" concern is identical to A3 certification's own evaluation_suite_version_id — the same version-pinning is applied here for consistency, rather than leaving a run pointing at the identity row alone. Stays a bare FK: a composite FK cannot match a NULL-tenant global suite version's own parent.
  • DR-5 — agent_action has NO agent_task_id column. enforce_single_module_autonomy() (BLOCKER4's saga gate) derives it by joining through agent_execution_id instead — a genuine, disclosed gap in v3's own literal SQL, which referenced NEW.agent_task_id on this table without ever declaring that column existed. Fixed structurally (the join), not patched by silently adding an unstated column.
  • DR-6 — A3's "trigger on tenant_agent_deployment" resolved instead as a trigger on agents.agent_skill_assignment. tenant_agent_deployment (v1's own marketplace table) has no skill_version_id column at all, making the literally-described trigger structurally impossible to build; agent_skill_assignment is the schema's own actual "this skill is active for this agent" instantiation point and is used for all 3 checks instead (A3 certification validity, BLOCKER5 duty sufficiency, A8 Rule 4 spend-ceiling binding), folded into one function, validate_skill_activation(), rather than 3 separate triggers.
  • DR-7 — A2b's retirement-rule-1 checks are split by actual column location, since A2b's own prose names skill_version_id/model_version_id/prompt_version_id/toolset_version_id generically without pinning which table carries which reference (a mapping only settled later by I1/BLOCKER6): ai.agent_execution carries only tool_version_id and is checked by ai.reject_retired_tool_version(); agents.decision_context_snapshot carries the other 4 and is checked by agents.reject_retired_decision_context_versions().

12. Deferred / Future Items

All items tracked in docs/open-items/OPEN_ITEMS.md, attributed to agents. Summary for context:

Item Status Trigger
12 tenant-scoped tables have no tenant_id-leading index (agent_eval_case, agent_event_log, agent_shadow_decision, agent_thread, all 6 decision_context_* child tables, evidence_retention_policy, kill_switch_event, rollback_execution) open, low severity RLS correctness is unaffected; each table's natural query path is via its own FK-indexed parent. Add the supporting index if a real direct tenant-wide scan need materializes.
agent_reader role + agentReaderDB() connection helper open Phase 6 of this same build authorization.
ESLint rule banning adminDb/tenantDB/set_config from agent code paths open Phase 6.
identity.agentSkillAssignment (old table) DROP open, disclosed transitional Phase 6, after production IdentityService is retargeted to the new agents.agentSkillAssignment.
AgentsService open No service layer exists yet — schema-only this phase, matching every phase of this build so far.
Last modified: Jul 12, 2026, 7:08 AM PT
On this page
Esc