Orchestrata Ultimate › Plans › promotion-and-deployment-plan

Promotion to test + deployment — session 262 work

Source: plans/promotion-and-deployment-plan.md at 8a2cb42ad · last changed 2026-08-12 · first committed 2026-08-12 · 200 lines.

Prose pending.

Plan text

Promotion to test + deployment — session 262 work

Authored 2026-08-12. Covers everything fixed this session plus the five open items, through documentation, review, commit, promotion and deployment verification. Every number here was measured, not estimated; where a number is unknown the step says so and names the command that produces it.

Why the order is forced

Two rules pin the sequence, so this is not a preference:

  1. RUL-008 — the review battery runs before ANY commit, and every bug it finds gets fixed regardless of scope. scripts/gates.py --all is currently rc=1 (parallel-safety, 130 violations). So the triage gates the commit.
  2. Promotion requires committed code. scripts/promote_env.py test runs the suite pinned to an env; promote --to test advances, restarts and verifies a deployment. Neither is meaningful against 97 uncommitted files.

A third constraint shapes where the work happens: RUL-023's wiring gate SKIPs with "no diff against master", so work sitting uncommitted on master produces a green that is absence of evidence. Doing this on a branch makes that gate produce real evidence for the first time in this session.

Phase 0 — Branch and baseline

# Step Verification
0.1 Create feat/session-262-gate-and-provenance off current master git branch --show-current
0.2 Record the pre-work baseline: gate counts, battery rc, suite totals committed to reports/ as a baseline artifact
0.3 Provision the branch's env per RUL-021 (scripts/provision_worktree_env.py) — no symlinked .venv, no exported PYTHONPATH scripts/check_source_provenance.py

Baseline to capture before anything changes (so every later claim has a denominator): gates.py --all rc + per-gate lines; gate 0.4 count; the observed suite totals; git diff --stat.

Phase 1 — Close the five open items

These are independent and run in parallel except where noted.

1.1 Triage the 130 parallel-safety violations — the critical path

Measured shape: 130 violations / 51 files. Groups (from the tree-wide measurement): (a) 12 hardcoded global key literals in 4 files; (b) 91 keys from non-test-scoped expressions; (c) 70 destructive verbs the old detector never knew (delete 51, hdel 11, srem 7, lrem 1); (d) 75 receivers the old regex could not match.

Exposure that makes this real: tests/conftest.py::redis_client passes no db=, so it lands on shared db0 — where the live kernel and gateway run — and its teardown reclaims only {test_id}:*.

Disposition rule, per site, no third option:

"It uses a per-worker DB" is a valid waiver reason and several sites qualify (db=_worker_redis_db()); it must be stated, not assumed.

Sharded by file cluster so shards never touch the same file. Each shard proves its own work: the sites it owns disappear from the gate output, and its modules pass observed.

1.2 Gate 0.4 — the unsound exemption (BLOCKER, latent)

_resolve_module_calls (gate_parallel_safety.py:606) accumulates next_seeds[func] across all call sites into one per-function set, so one call passing a test_id-derived key retroactively exempts every other call site of that helper — including one passing a hardcoded global key. Isolated with a two-file control: byte-identical hazard, FLAGGED alone, NOT FLAGGED once an unrelated scoped caller exists in the module.

Same defect class as the ±13-line window it replaced: an exemption that fires without proving anything about the site it exempts. Armed but not live — 8 helpers in tests/ already have mixed derivation, none currently containing a write verb.

Fix: make seeding per-call-site, not per-function. Red-first against the existing two-file control.

Majors to close in the same pass (each narrows what the gate sees):

1.3 Worktrees — 2.0 GB, and a banned-dependency re-entry path

Four abandoned worktrees, zero live processes, all four carry the banned library import and dependency, so merging any of them reintroduces what this session removed. Three hold unmerged commits that are real work:

branch commit content
agent-a19bd3f45a6c20c7d 79917186 share ToolSearchTool.create() across test_search fixtures
agent-a90e6c1fb767671db 705a46e7 trace_intel cold-KB build once per module, not twice
agent-aad6ad8280c27cf26 e83fe602 one AST daemon per run instead of per-batch re-warm

Per branch: review → strip the banned dependency → verify green on this branch → cherry-pick or abandon explicitly → git worktree remove + branch delete. Dirty files in them are regenerated plans/ast-*.json / reports/*.json which stay out per the K3 artifact ruling.

1.4 test-purity gate scope

scripts/gates.py invokes audit_test_purity.py with no path argument, so the new banned-import check covers tests/ only. A reintroduced banned import under src/ or scripts/ is outside the gate entirely. Extend the invocation and prove it with a red-first probe in each root.

1.5 Stale docstrings describing deleted behaviour

Three texts still say PipelineEventBus.emit "stamps driver on events" — the mutation that was deleted. tests/kernel/test_pipeline_events_enum.py:144,185 and tests/kernel/test_pipeline_event_forwarder.py (~L140). A docstring that describes behaviour the code no longer has is how the next reader re-adds it.

Phase 2 — Bug investigation sweep

Not "fix what we know" — hunt for what the same defect class implies elsewhere. Every defect this session shared one shape: a checker that cannot distinguish "nothing wrong" from "didn't look."

Confirmed instances: the codebase-map generator wrote 452 bytes of headers and reported success; the purity audit whitelisted the import it existed to catch; gate 0.4 excused three global keys because its regex matched a substring of the test's own function name; a before/after measurement reported a confident zero because the module resolved its scan root from __file__ in /tmp.

Sweep every gate and checker in scripts/ for the same shape, specifically:

Each finding gets a red-first proof and a fix, or a written argument that it is unreachable.

Phase 3 — Documentation, specs, wiki

Nothing here is optional; RUL-012 puts documentation inside the definition of done.

Target Change
specs/001-kernel/pipeline-event-type-addendum.md the driver field: producer-owned attribution, "" = unattributed and why that sentinel is load-bearing, the resolve-not-mutate contract, the two production producers and the structural guard that discovers a third
Spec for parallel-safety gate 0.4's contract: what it flags, the two dispositions, and the exemption's soundness requirement (per-call-site, not per-function)
AGENTS.md / CLAUDE.md the shared-db0 exposure and the test_id-derived-key requirement, if not already stated in enforceable terms
docs/development-and-testing.md how to read a gate 0.4 violation and the two ways to resolve it
.claude/rulings/ RUL-014 requires every ruling resolve to a mechanism — the banned-library ruling now has one (banned_import), record it
Wiki projects/orchestrata-ultimate/ new page: the artifact-vs-description defect class, with all four measured instances; update pipeline-event-convergence.md (driver field + the un-forked golden), running-the-test-suite.md and suite-parallelism-floor.md (the 130 and shared db0)

Wiki work follows the standing workflow: write the file, wiki create/update, validate, re-index (wiki-rag-ingest).

Phase 4 — Review battery and commit

  1. scripts/gates.py --all → rc must be 0, with wiring producing real evidence this time (there is now a diff against master).
  2. scripts/gate.py run → PASS, not INVALID (a stale fact base is not a pass).
  3. Full observed suite, scripts/run_tests.py — the kernel is REQUIRED; a run that does not start and gRPC-verify it is invalid/false-green.
  4. Code + test + architecture review per RUL-008; fix everything found.
  5. Commit in reviewable series by concern, not one omnibus commit. No AI attribution.

Phase 5 — Promotion to test

# Step Gate
5.1 promote_env.py validate --to test profile + infra reachability
5.2 promote_env.py test full suite pinned to env/test.env, prod-subnet guard active
5.3 promote_env.py promote --to test advance + restart + verify
5.4 Redeploy the containerized test kernel + gateway in the app container (10.93.53.13) via deploy/orchestrata-test/provision_app_container.sh units healthy
5.5 promote_env.py status ledger records the promotion

Prod is explicitly out of scope. promote --to prod is gated on a green test-env run and is the user's decision, not this plan's.

Phase 6 — Deployment verification

Claims are only made about what was verified this session (RUL-012):

Definition of done