Placeholders. Names like
<ops-hub-host>,<pve-host>,<pve-b-host>,<nas-host>,<pve-provider>stand in for this deployment's real values, which are deployment-local and never tracked in git. Recall them withsearch_knowledge tag:deployment-*on the deployment's platform (see conventions/deployment-knowledge.md).
Status: v5 — revised 2026-08-12. v3 folded addenda 1–3 (system-function depth: SDWAN,
module lifecycle + template composition, storage/volume); v4 added addendum 4 (agent
fleet + skill lifecycle) + the pass-3 §4 precision corrections; v5 records §4 as
RATIFIED by the operator, fixes the pass-4 precision point on the composer's SDWAN
claim, and re-derives (not restates) the one-campaign verdict. Supersedes v1–v4 in place.
Predecessor: platform-autonomy-dryrun (019fdffd-aeed) — zero-intervention E2E PASS
2026-08-09 (run 20260809g). This campaign is its sequel and reuses its substrate wholesale.
Owner surface: docs/operations/ (this doc) + the Ai::Campaign record once started.
Shape: per autonomous-campaigns convention.
Ship a working, fully closed supervised-evolution loop for a deployed system, then exercise it end-to-end — where the actuation genuinely traverses the system stack (provider, template composition, module materialization, SDWAN membership, storage, container runtime), not a loop that closes at the database.
One dimension, both directions (depth, not breadth):
horizontal scale/drift, out and back in: live drift signal → adaptation proposal → supervised approval → actuation through the real full-stack path → re-verification →
RemediationOutcomeflips pending→effective because the metric row actually cleared — and after scale-in, zero orphaned resources (peers, volumes, instances).
Scale-out forces the traversal: a new replica provisions on a real PVE node, composes
its template, materializes its modules (existing published versions), joins
dryrun-fabric (peer attach, /128 allocation), completes the DockerHost handshake, and
gets a per-instance volume. Scale-in forces the detach half: peer detach + credential
cleanup (the PeerDetacher FK-orphan history), volume detach/cleanup, instance terminate.
The predecessor's F-2 clean-teardown assertion generalizes into a first-class scale-in
oracle family, not just a teardown check.
This campaign builds the missing production wiring first (INC-1..5), then exercises it (INC-6..8). It produces a working loop, not another gap report.
| Leg | Capability | Status |
|---|---|---|
| 1. DESIGN | brief → deterministic plan synthesis | PROVEN — reused |
| 2. DEPLOY | plan → real instances +, SDWAN enroll, DockerHost, live-PVE verify, prefix teardown | PROVEN — reused |
| 3. EVOLVE | the closed bidirectional scale/drift loop above | this campaign builds and proves it |
- Self-improvement = topology/config ONLY (never the baseline's own source code).
- No new
RECOMMENDATION_TYPESfor topology — the adaptation lane is the lane. - One closed dimension — horizontal scale/drift. The pass-2 addenda add depth (real system-function traversal + its oracles), not new dimensions.
- No failure injection — drift induced by amending the declared scale (§3).
adaptingstays sensor-driven (no handoff-RalphLoop resurrection; IMP-3a590c8dbbba).- The supervision seam is RATIFIED (operator, 2026-08-12 — §4): fleet ApprovalRequest
chain + InterventionPolicy as THE policy gate for adaptations; gate before composition;
the proposer's ApprovalWorkflowService call removed (not "fixed"); downgrade-only
bounds check; removals never auto-apply; one queue for sensor + MCP paths;
adaptation_gateregistry seam, fail-closed park. Tradeoff accepted on the record: approvals land in the fleet queue, not concierge cards. The successor campaign (evolution-dimensions, §11) is endorsed as the deferral target, this campaign strictly first. - Exercise real system functions — SDWAN, module lifecycle, template composition, storage/volume (addenda 1–3), and agent fleet + skill lifecycle (addendum 4) — through the one loop.
- Skill lifecycle in scope; skill AUTO-EVOLUTION out (addendum 4 boundary, argued in
§11): resolution, binding, and usage-counter recording are exercised — the loop's own
actuator IS a skill. The baseline auto-evolving/mutating its own skills
(
auto_evolve_skill/mutate_skill/compose_skills) is NOT authorized by decision 1: skills are account-level shared config consumed by every agent/mission that binds them, so a baseline mutating its skills mutates behavior OUTSIDE its prefix blast-radius boundary — it fails this campaign's own containment rail. It is a candidate dimension for the successor campaign, needing its own operator sign-off.
Bidirectional means scale-in through the supervised lane, which requires the
scale-down actuator v2 had deferred (offer 019ff3d1-6e9b, no removal strategy on
ScaleProjectExecutor). That offer is promoted to blocking (INC-4). It is the one
genuine scope addition; everything else the addenda ask for lands as composition input
threading, oracles, and rails on machinery the loop already traverses —
add_replicas already passes network_id + with_storage_gb through to
ProvisionFullStackExecutor and records sdwan_peer_ids/storage_volume_ids in its
outputs envelope (verified), so SDWAN + storage depth is reached by supplying those
inputs, not by new actuation machinery.
- Re-proving legs 1+2; latency/availability/cost telemetry (deferred offer
019ff39f-636a). - SLO-driven, cost-driven, vertical (module-version), and relocation evolution dimensions — the pre-chartered successor campaign (§11).
- Module builds, publishes, promotions, or build-batch dispatches of any kind (§9 rails).
- Snapshots of any kind — most critically any zvol snapshot on (§9 HARD RAIL).
- SDWAN network CRUD/topology mutation — the loop consumes membership of the existing
dryrun-fabric(019fe651-b757, permanent substrate); it never mutates the network. - Service exposure, storage migration, K3s — see the coverage table (§7).
- Agent-fleet launches or new agents (
launch_agent_fleetuntouched; the loop uses the existing seeded Fleet Autonomy agent + the mission's agent — idle agents burn credits, the launcher is default-OFF for exactly this reason; all spend under the existing config-driven ceilings). - Skill auto-evolution/mutation/composition by the baseline (see §1 boundary above).
- Autonomous-tier flips; production (none exists); trading extension.
Unchanged from v2 in substance; restated compactly with the addenda's control-group frame:
- Sense arc live for drift —
ProjectMetricsCollectoron every tick writesreplica_count/region_countfrom runner step outputs (source: "live"); exactly the narrowed loop's signal. Latency/availability/cost honest-unavailable (deferred). - Decide arc real, act arm dead —
project.*routes toproject.adapt/project.cost_controlwith per-mission dedup, then dead-ends:skill: nil, noREMEDIATION_APPLIERSentry,propose_from_signalshas zero production callers (~33 spec invocations only; the M2 smoke drives the proposer directly — a green M2 is never actuation evidence). Applier-less proceeds mint unclearable pendingRemediationOutcomes → F3-11 false stuck escalations. - The control group (addendum 2/3 framing): four sibling lanes are already wired
end-to-end —
system.module_drift(sync_modules),system.config_drift(apply_config),system.template_closure_drift(TemplateApplyService),system.storage_assignment_drift(reconcile) — same sensor→binding→gate→applier shape, actuating today.project.*is the outlier. INC-1 copies the wired shape rather than inventing one; INC-8 compares the new lane's oracle readings against the wired lanes' historicalRemediationOutcomerecord (read-only). If the new lane behaves unlike all four under the same oracles, that is a finding. - Composition never shaped to the actuator — drift composes
brief+1overshoot (ignores the signal's ownobserved/target); emitted inputs cannot bindScaleProjectExecutor#performkwargs; and (stated precisely — the composer file DOES contain SDWAN references, so grep carefully): there is nonetwork_idorwith_storage_gbthreading on thescale_projectpath specifically. SDWAN exists in the composer only as a separate step type (configure_sdwan_for_project, in ADAPTATION_SKILLS at :59 and mapped at :67) bound to a different change class (security_change) — one no scale/drift signal ever selects. So a dispatched scale-out is compute-only — no SDWAN membership, no volume — despite the composer "having SDWAN": a surface that looks covered, which is worse than a plain omission. All one offer (019ff3d1-1f58, INC-3). - No scale-down actuator —
STRATEGIES = add_replicas|vertical_resize|add_region; the executor's only terminate is its own rollback (019ff3d1-6e9b, now INC-4). - Execution/verification absent —
adaptation_diffhas one occurrence (its write); no runner append;auto_applydropped; no post-adapt verify; gate silently absent in core mode via the stale:governancecapability check (019ff39f-1d59, INC-2). - Harness cannot hold a baseline under observation (
019ff39f-9d80, INC-5). - Approval-mechanism sprawl — now four mechanisms (§4).
- The skill/agent surface is already on the critical path (addendum 4):
ScaleProjectExecutordeclaresskill_descriptor(name: "scale_project", …)(scale_project_executor.rb:29-39) and is bound to Fleet Autonomy — the loop's actuator IS a skill and its decider IS an agent, so skill resolution, binding, and usage recording are traversed on every actuation; exercising this surface means asserting it, at zero footprint cost. The F5 history dictates the assertion shape: the skills oracle read 0 live across multiple dryrun runs from TWO distinct causes (slug-vs-display-name lookup,019fe817; and a barecreate!bypassingusage_count/effectiveness counters —Ai::Skill#record_usage!had to become the write path). Both fixes are deployed, but the lesson stands: assert counters, not row existence — and its sibling from the silently-untracked-global-agents incident: count records from ground truth, never infer from "the call returned success" (anAgentExecution.create!once failed inside a rescue and executions simply vanished). Both are standing oracle rules in §8.
Baseline: brief with container-runtime use case, scale.initial: 2, regions
+ (1 each), docker leg, powernode-ops-cell template (stamped
sdwan_network_id → auto-enroll on dryrun-fabric), with_storage_gb: small
per-instance volume (config-driven size) so the full-stack path exercises storage on
every provision, watch_policies.auto_scale_max_replicas: 3, config-driven budget cap.
run_id evo-NN → dryrun-evo-NN prefix = blast-radius boundary.
Drift protocol — non-destructive, both directions:
- Out: amend declared scale 2 → 3. Collector observes 2 vs 3 → live
system.project_drift→ proposal (add_replicas, delta 1, network + storage threaded) → gate → actuate → replica provisions/enrolls/handshakes/attaches → metric clears at 3. - Back in: amend declared scale 3 → 2. Drift (3 vs 2) → downscale proposal (remove_replicas, victim = the newest replica, i.e. the one the loop added) → gate (always require_approval for removal — destructive) → actuate → peer detach + credential cleanup, volume detach/cleanup, terminate → metric clears at 2 → zero-orphan oracle family (peers, volumes, instances) — the F-2 assertion promoted from teardown check to evolution oracle.
Footprint (restated honestly — up from v2, still small): steady 2 instances +
2 volumes + 2 fabric peers; transient peak 3 instances + 3 volumes + 3 peers during
the out-leg; back to 2/2/2 after the in-leg; teardown to baseline at run end.
v2's "3 peak" figure carried no volumes/peers because v2's loop was compute-only upscale —
the depth requirement is exactly why those columns now exist. Infra cost still pennies on
own PVE; LLM ≈ zero (deterministic paths); ceilings config-driven
(ai.dryrun.budget_usd, brief cap, ai.evolution.* SiteSettings).
SETTLED. The operator ratified this design as recommended, including the two precision points below and the accepted tradeoff on the record: adaptation approvals land in the fleet approvals queue alongside module-promotion / CVE / cert approvals, not as concierge chat cards — design-time stays mission gates, run-time evolution joins fleet ops. The evaluation is retained below as the rationale of record; it is no longer a recommendation awaiting decision, and it no longer blocks INC-2.
Four mechanisms were evaluated (addendum 3 added the fourth):
| (a) Fleet ApprovalRequest chain + InterventionPolicy | (b) "Fix" ApprovalWorkflowService's core-mode nil | (c) Mission gates / concierge cards | (d) Domain state-machine gate (storage-migration pattern) | |
|---|---|---|---|---|
| Works in core mode | Yes, today — Ai::ApprovalChain is core (c706737bd), chains seeded, create_pending_approval verified |
Only by re-implementing (a) as a second namespace | Yes | Yes (pure model statuses + permission-gated action) |
| Gate semantics | Policy-resolved, per-mission dedup, cooldown, consent budgets, F3-11 forcing, kill-switch — existing, verified | None of these | Linear phase machine; N dynamic gates for repeated events (the 40d8a3cd4 fragility) | Checkpoint inside ONE long-running operation; no policy resolution, no dedup domain, no cross-action posture |
| Species | Policy gate — "may this class of autonomous action run?" | (would be) policy gate | Workflow gate — checkpoint in a specific operation | Workflow gate |
The taxonomy the fourth mechanism reveals (verified: System::StorageMigration is a
pure status machine — planned → operator advances via the permission-gated
system_approve_storage_migration action → the on-node agent advances through
preparing/syncing/verifying/cutover; zero Ai::ApprovalRequest/InterventionPolicy
references anywhere in the model or storage services): the platform's four mechanisms are
really two species. Workflow gates (mission gates, storage-migration approve) are
checkpoints inside one long-running operation and are legitimately domain-shaped.
Policy gates (fleet chain; the governance chains aspire to this) decide whether a
CLASS of autonomous action may run, with posture, dedup, and budgets. So the honest
answer to "is storage migration the pattern to follow or a fifth silo?": neither — it
is the other species, consistent with mission gates, and not a competitor for
adaptation gating. But the sprawl finding stands: four mechanisms, two species, no shared
queue or UX, and one of them (governance chains) silently inert in core mode. The
charter's recommendation therefore comes with a taxonomy rule worth adopting platform-wide:
a new gate must declare its species; recurring autonomous actions get policy gates,
checkpoints inside a single operation get workflow gates.
Ratified design: (a) — with the species argument: an
adaptation is a recurring autonomous action class, so it takes the policy species, and
(a) is the platform's one working policy gate in core mode. Shape (unchanged):
gate BEFORE composition on the sensor path; the proposer's embedded
ApprovalWorkflowService call is removed (the stale :governance check dies with the
call-site — the honest disposition of (b)); auto_apply? becomes a downgrade-only
bounds check; removal actions are exempt from auto-apply — remove_replicas is
always require_approval regardless of bounds (destructive); the operator MCP path
joins the same queue; core purity via an adaptation_gate registry seam (the F2
provision_verifier precedent: core defines the seam + fail-closed park default, the
system extension registers the fleet-chain implementation — no core→extension
dependency, SDWAN/module/storage specifics all stay extension-side behind it); envelope
gains the explicit gate: disposition (routed | auto_apply_within_bounds | parked_gate_unavailable). Option (c) retained as an optional presentation bridge only.
Two precision points (pass-3 corrections), stated exactly:
- Mechanism vs policy split. Core owns the ApprovalChain mechanism
(
server/app/models/ai/approval_chain.rb, sincec706737bd); the system extension seeds the fleet chains (extensions/system/server/db/seeds/fleet_autonomy_agent.rb, theAi::ApprovalChainblock, and sibling agent seeds — the chains stayed in the seeds when IMP-10e4f6c3bcd2 moved every declared POLICY row toPolicyReconciler). So "fleet chains are seeded" holds only whereextensions/systemis present — which is every fleet-running deployment, since the sensors, DecisionEngine, and executors this loop rides are all extension-side too. On an install without the extension there is no fleet, noproject.*signal, and no chain — and if the seam is ever reached anyway, the fail-closed core default explicitly covers it: no registered gate implementation → plan parked indraft+gate: parked_gate_unavailable. Never a silent proceed, never a silent success. - The seam is named on BOTH sides of the boundary. Gate side: the
adaptation_gateregistry seam (core defines + defaults fail-closed; extension registers the fleet-chain implementation). Actuator side:ScaleProjectExecutoris itself extension-side (extensions/system/.../skills/scale_project_executor.rb), so INC-2's dispatch must not name it either — it routes through the existing skill-resolution seam (the runner resolves"scale_project"/"remove_replicas"byAi::Skillslug → bound executor, exactly as provisioning steps already resolveprovision_full_stack). Core therefore names two seams and zero extension constants; both sides stay extension-registered.
Related, one sentence (addendum 3): System::Platform::StorageRecommendations
(get/update via the fleet tool) is a third parallel recommend→apply surface alongside
ImprovementRecommendation and the adaptation lane; the campaign does not exercise it,
but any future consolidation of "recommendation" surfaces should count all three.
Ai::Campaignplatform-evolution-loopdrivingdev-improvefor INC-1..5 (each task back-links its approved offer) plus operator-triggered E-runs.decision_authority: trustedfor build increments (test-first, STAGE-only, individual offer approval, core-purity, crypto-safety, 3-strikes,emergency_halt).- Live-run posture:
project.adapt/project.cost_control→require_approvalfor the campaign account before any E-run;notify_and_proceedwithin bounds only in E-run B, campaign account only, and never for removals.
Parked: fleet-wide post-campaign policy defaults (INC-8 retrospective input); skill-auto-evolution sign-off (successor-campaign gate, §12). §4 is no longer parked — ratified 2026-08-12.
BUILD ORDER RE-SEQUENCED — operator ratified 2026-08-12: INC-3 → INC-2 → INC-1 → INC-4 → INC-5. The increments keep their numbers (offers and tasks reference them); only the ORDER changed.
Why. The original 1→2 order put the producer before its consumer, and INC-1 was driven to a
blockedoutcome (taskIMP-4f7f7a0c9d33) after three/code-review highpasses in which every correction traded one pathology for another: unactuated → falsefleet.remediation_stuck; actuated but unconsumed → same escalation; + validate-arc skip → F3-11 brake removed, ~144 proceeds/day; + open-plan idempotency guard → nothing moves a plan out ofdraft, so the lane goes permanently silent after one plan withineffective_streakpinned at 0 and no alarm. A propose-only lane with no consumer has no reachable correct state — that is a property of the increment boundary, not a bug in the code.The rule this yields: a producer/consumer split is only a valid increment boundary if one half is independently exercisable, and that half lands FIRST. Here INC-2 is (the operator-initiated MCP
propose_changepath already mints identically-shapedadaptation_diffplans, so the consumer is testable with no producer); INC-1 is not. INC-3 precedes INC-2 so the runner never dispatches a composition that overshoots on its first use.INC-1 work is preserved uncommitted in the
extensions/systemsubmodule (506 insertions acrossdecision_engine.rb,remediation_validator.rb,decision_engine_spec.rb). The validator skip is independently verified correct; the 16-example spec — including the example that flips a plan toapprovedby hand — is the clearest existing specification of what INC-2's consumer must do. Resume from it; do not restart.PREREQUISITE FOR THE §8 SDWAN + STORAGE ORACLES —
IMP-cdc1d0703e5a, approved 2026-08-12.PlanComposerService#merge_resolved_inputs!never stampsnetwork_idorwith_storage_gbinto a provision step's inputs (verified: zero references in the file; it merges onlycount,dry_run,provider_region_id,provider_instance_type_id,template_id). The actuator supports both fully —run_provisionthreads them and recordssdwan_peer_ids/storage_volume_ids, and rollback already cleans volumes — so the capability is plumbed everywhere except the point that would request it. Consequence: every provisioned and scaled-out replica is currently BARE COMPUTE — no peer, no volume. INC-3's footprint threading is correct in shape but inert, because the plan it reads never holds the keys.This makes the §8 SDWAN and storage oracle rows a trap until it lands: they assert a scale-out produces a peer and a volume, and today they would fail — or worse, pass vacuously against an empty expectation. Assert NON-ZERO peers and volumes, never merely "no error". Drains after INC-3 (queued at position 238, i.e. after INC-4/INC-5; the build increments do not depend on it).
CORRECTED 2026-08-12 — the paragraph above is wrong about the SDWAN half, and "assert non-zero" is NOT a sufficient fix. IMP-cdc1d0703e5a was implemented, reviewed, and REVERTED after verifying in source that
Sdwan::TopologyCompiler.compile_for_networkisnetwork.peers.includes(:keys).map { compile_peer_view(peer) }(topology_compiler.rb:68-73) — it maps the network's pre-existing peers and creates none.ProvisionFullStackExecutornever enrols anything; the only provisioning-side enrol isSdwan::PeerEnroller, called solely fromconfigure_sdwan_for_project, a skill that is not inALLOWED_EXECUTORSand thatvalidate_planwould reject. So the capability is not "plumbed everywhere except the point that would request it" — the peer-attach step does not exist in a provisioning plan at all.Consequently
outputs.sdwan_peer_idsis non-empty whenever the target network already has peers, and a NON-ZERO assertion still passes vacuously by counting the fleet that was already there. The §8 SDWAN oracle must assert that the peers belong to the newly creatednode_instance_ids. Tracked as offer019ff6d4-7dda(0.95), which is the real prerequisite for the SDWAN rows. Storage has a separate blocker —019ff6d4-e8cf(0.90) — see the §8 storage row.Integrity check for the preserved work — use THIS, not the combined total. The two PRODUCTION files (
decision_engine.rb+remediation_validator.rb) stand at 162 insertions / 8 deletions and are byte-identical to how INC-1 left them. That is the invariant to verify, because it isolates production code from the spec and survives authorized spec-only edits. The combined three-file total has intentionally moved (506 → 561+ insertions) and is therefore NOT a useful check.Two authorized edits were made to
decision_engine_spec.rbby INC-3 (IMP-02b4bc9f8bd8), both attributed in-file, neither committed. (a) The mission fixture now stamps a provisioning plan withtemplate_id/provider_region_id/provider_instance_type_id, because INC-3 added a compose-time footprint precondition — without it, eight examples whose expectations are CORRECT failed on stale fixtures, and eight misleading reds would have invited the next person to revert a correct guard. (b) The:1382assertion was corrected, not relaxed: it counted account-wideAi::AgentGoals as a proxy for adaptation goals, which only held while the mission had never been provisioned —Ai::GoalPlan belongs_to :goalis required, so any real provisioned mission has a provisioning goal and the count is 2 after one adaptation. It is now scoped tometadata @> {"kind" => "adaptation"}, preserving the stated intent ("one goal per mission, not one per signal") and making it correct in production. Coverage increased; nothing was softened.The cost_control red has TWO independent causes — fixing one will not make it green. The example at
:1314fails first at:1322(change { Ai::GoalPlan.count }.by(1), and the lane now composes nothing), and would then fail again at:1328, which assertsexecution_config.dig("inputs","target_cost_usd") == 200.0against the cost_control branch INC-3 deleted. Both belong to the same example (it ends at:1329), so the file measures 80/2, not 80/3 — but anyone repairing only the count will be surprised. And this one reaches the gate:scripts/validate.shDOES run extension specs (opt-outs list onlytradingandsupply-chain, soextensions/systemis gated), which means this example will failvalidate.shonce the blocked task's work is committed. Resolve it as part of resuming INC-1, not by restoring the deleted branch.SECOND expected red —
decision_engine_spec.rb:1331, "derives a relocate plan from region_count drift". By design; do not restore the composition. INC-3 added a bindability guard at the single exit ofbuild_steps_for: a composed step whose skill declares required inputs the step does not carry is dropped rather than persisted.relocate_workloadrequires eight inputs and the heuristic branch supplies one (target_regions), so aregion_countdrift now composes nothing. Proven by execution, not inspection — a review fed the composed inputs to the real executor and got{success: false, error: "missing required input: project_id"}with all eight missing.This is not a capability regression. The composition was already unbindable and already failed at dispatch — after persisting a plan and minting an approval request an operator had to action for something that could never succeed. The guard makes the failure honest and early. Same reasoning as the cost_control red below: the expectation asserts a lane we have decided should not compose while nothing can actuate it.
attach_storageandconfigure_sdwan_for_projecthad the identical hole. The missing composer isIMP-019ff49b-a8e5(filed, not approved) — note its blocking design question is where the live-instance read forsource_instance_idsbelongs. Costs this campaign nothing: relocation is explicitly deferred to theevolution-dimensionssuccessor.Expected red in that preserved spec — by design, do not "fix" it blindly. After INC-3 landed,
decision_engine_spec.rb:1244("routes a cost breach throughproject.cost_controlinto acost_controlplan") FAILS: it assertsAi::GoalPlan.countchanges by 1, and INC-3 made thecost_controlbranch decline to compose while no scale-in strategy exists. Measured both sides in one session: 80 examples / 0 failures before the decline, 1 failure after — that example only. The spec now encodes an expectation we have deliberately reversed, because composing there mints an approval request for a step that raisesArgumentError: missing required input: project_idat execution. Resolution belongs to INC-4 (remove_replicas), which makes downscale composable for real; until then the correct expectation is "composes nothing." Revise the expectation when resuming INC-1 — do not restore the old composition to make it green.RESOLVED 2026-08-21 (IMP-e68a93c47106). INC-4's
remove_replicaslanded and the proposer'scost_controlarm was wired to it, so the expectation above was revised to the composed shape — the lane now composes a scale-IN and the plan is held for approval (a removal is never auto-apply eligible). TheUNSUPPORTED_CHANGE_TYPESentry that advertised "no scale-in strategy" was deleted in the same commit. Nothing here is still an open instruction.Carried into whichever increment lands the producer — auto-execution path, MECHANISM-VERIFIED 2026-08-12. The pass-3 review raised this and stated it unconditionally; the claim was then checked against source and is real but doubly gated. Stated precisely, because the guard must protect the right thing:
The lane's propose-only guarantee rests entirely on
find_or_create_goal!creating theAi::AgentGoalwith statuspending, and nothing asserts it.GoalDrivenSchedulerService#next_action(goal_driven_scheduler_service.rb:41-79) drives anyactivegoal through draft →:validate→can_auto_approve?→current_plan.approve!(user: nil)→:execute_step, and the steps are the proposer's destructive multi-step provisioning skills. This path never consults theproject.adaptInterventionPolicy — the fleet-side consent decision is genuinely bypassed.Gate 1 — reachable, confirmed:
Ai::AgentGoal#activate!(agent_goal.rb:82-83) flipspending → active, andApi::V1::Ai::GoalsController#updatepermits:status(:70), so an operator can activate an adaptation goal through the ordinary goals API. Gate 2 — fails closed by default, NOT mentioned in the review:can_auto_approve?(:109-118) readsAi::AgentTrustScorefor the plan's agent, defaults the tier tosupervised, andTRUST_THRESHOLDSmapssupervised => 0; it returns false whenever the threshold is zero. So an agent with no trust score, or a supervised one, cannot auto-approve at all. Above that the brake is a cost ceiling —monitored => $1,trusted => $5, andautonomous => Float::INFINITY, i.e. no cost ceiling whatsoever.Net: not "any activated goal auto-executes." It requires an activated goal AND a non-supervised trust tier AND (below
autonomous) an estimated plan cost under the tier threshold. What the producer increment changes is EXPOSURE, not reachability — the path exists today for operator-initiatedpropose_changeadaptations; wiring the fleet loop moves it from one goal per explicit operator request to one goal per breaching mission, minted automatically. Guard both conditions: assert the goal is createdpendingand cannot be auto-driven, and treat theautonomous-tier infinite threshold as the case that actually matters.
| # | Increment | Depends on | Verifiable by |
|---|---|---|---|
| INC-1 | Wire the act arm (BUILD 3rd), copying the wired-lane shape (REMEDIATION_APPLIERS-style entry for project.* on proceed + execute_approved! replay → propose_from_signals); fix the false engine comment; exempt applier-less project categories from record_proceeded! until a proposal exists. Must assert the goal is created pending and cannot be auto-driven. Red-first. |
INC-2 (consumer must exist first — see re-sequence note) | drift signal → persisted plan via the reconciler path; no orphan pending outcome; plan reaches a terminal state so the open-plan guard releases; applier shape matches the four wired lanes |
| INC-2 | Supervision seam + dispatch + verify (BUILD 2nd): adaptation_gate registry seam (fleet-chain impl, fail-closed park default), runner dispatch with append semantics, post-adapt VerificationService re-run, fingerprint-clear → RemediationOutcome, envelope gate: disposition. |
INC-3 (§4 ratified 2026-08-12) | approved plan → executed → verified → outcome effective; parked + explicit disposition when gate unavailable; exercisable via the operator propose_change path with no producer |
| INC-3 | Convergence composition, contract-shaped, stack-threaded (BUILD 1st — no dependencies, and required before any dispatch path exists so the runner never executes an overshooting composition on first use): drift composes converge-to-target (delta = target − observed, signed); executor-bindable inputs (project_id/target_count/scaling_strategy + template/region/type resolved from the mission); threads network_id + with_storage_gb from the mission's existing instances/brief so scale-out is never silently compute-only; deterministic-first (LLM only for unrecognized; provenance stamp); drop the hardcoded model fallback. |
— | same signal → byte-identical, bindable steps carrying network+storage; desired == target; zero LLM executions |
| INC-4 | remove_replicas strategy (extension-side): victims newest-first among the mission's own replicas; reuses ProvisioningService.terminate_instance (the one terminate path — protection flags respected); records removed ids in outputs; floor at 1; peer + volume cleanup asserted (not assumed) via the zero-orphan checks; irreversible-with-approval (no rollback-by-reprovision claimed). |
— | spec: 3→2 drift composes one remove step, victim = newest; terminate + detach paths exercised with stubbed provider |
| INC-5 | Harness soak mode + evolution/system oracles (§8), per-run_id concurrent guard, explicit teardown command (cancel-before-sweep). | — | soak leaves mission active + sensor emits; teardown terminalizes |
| INC-6 | E-run A (supervised, out and back in): deploy 2; soak; amend 2→3, approve in the fleet queue, converge (full §8 sweep incl. SDWAN/module/storage rows); amend 3→2, approve removal, converge; zero-orphan family passes; optional negative-path drill (reject a proposal → nothing actuated, cooldown respected); teardown. | INC-1..5 + 3 offers below | §8 oracles, all ground-truth |
PRE-FLIGHT FOR INC-6 — read before scheduling E-run A (recorded 2026-08-12).
- A clean soak exits 1, not 0. Every project metric currently returns
unavailable, and INC-5 deliberately grades that as amediumobservationfinding rather than letting absent observation read as observation ("not measured ≠ pass"). So E-run A must either expect exit 1 with dimensionobservation, or land the collector fix first. Land it first — offer019ff5ea-3500, theresolvable_instance_idsdig path being one level too shallow. It also turns the drift leg from unfireable into real, so it is a prerequisite twice over.- The SDWAN and storage §8 rows cannot pass honestly yet — offers
019ff6d4-7dda(scale-out enrols no peer; a non-zero peer count passes VACUOUSLY off the pre-existing fleet) and019ff6d4-e8cf(volumes never attached; the zero-orphan sweep is blinded by the same nil FK it checks). See the corrected rows in the coverage and §8 tables.- Minor: a reaped gate claim leaves the
routingdimension unobserved but graded:lowwith a message naming the wrong cause — offer019ff6da-4134. Affects diagnosis of E-run A, not its fleet outcome.All three are FILED AND PENDING OPERATOR APPROVAL; none is queued. INC-6 should not be scheduled until at least (1) and (2) are drained, or its oracle sweep will report green on dimensions it did not observe — the exact failure this campaign exists to eliminate. | INC-7 | E-run B (bounded autonomy + control drills): in-bounds scale-OUT auto-applies under
notify_and_proceed(INC-3 hard prerequisite — nothing LLM-composed may auto-apply); out-of-bounds parks; removals still park;emergency_haltdrill mid-soak. | INC-6, INC-3 | auto-applied out-leg; parked out-of-bounds + removal; timestamped zero-writes during halt | | INC-8 | Control-group comparison + momentum + report: compare the new lane'sRemediationOutcome/decision/oracle readings against the four wired lanes' historical record (read-only); disagreement under the same oracles = finding. Repeat E-run A: convergence non-increasing, zero repeat findings. Campaign report + scoreboard + retrospective (incl. successor-campaign backlog confirmation, §11). | INC-6..7 | run-over-run + cross-lane deltas from persisted records |
| Offer | Finding | Criticality |
|---|---|---|
019ff39e-d156 |
act arm dead-end | BLOCKS — INC-1 |
019ff39f-1d59 |
no execution/verify path; core-mode gate absent | BLOCKS — INC-2 |
019ff3d1-1f58 |
overshoot + contract mismatch + (v3) no network/storage threading | BLOCKS — INC-3 |
019ff3d1-6e9b |
no scale-down strategy | BLOCKS — INC-4 (promoted from deferred by the bidirectional-depth decision) |
019ff39f-9d80 |
no harness soak mode | BLOCKS — INC-5 |
019ff39f-d9e2 |
LLM-first composition | Blocks INC-7 only; folds into INC-3 |
019ff3a0-0ff5 |
hardcoded model fallback | Deferred hygiene; folds into INC-3 |
019ff39f-636a |
SLO/cost telemetry unwired | OFF critical path; re-activates with the successor campaign (§11) |
019ff2aa-ace9 (pre-existing, pending) |
build fan-out overbuilds | Not this campaign's to fix — cited as a rail rationale (§9); stays in the general backlog |
| Surface | Status | Notes / reason |
|---|---|---|
| Template composition + closure resolution | Genuinely exercised | every provision composes the template; closure materializes module assignments |
| Module materialization (existing published versions) | Genuinely exercised | on each new replica; version identity asserted from registry ids/digests (§8) |
| SDWAN membership lifecycle on an existing network | BLOCKED — not exercisable as written (corrected 2026-08-12) | A provisioning plan contains no peer-attach step: ProvisionFullStackExecutor only compiles a read-only view of the network's existing peers, and the enrolling skill (configure_sdwan_for_project) is not in ALLOWED_EXECUTORS. Scale-out therefore attaches nothing, and sdwan_peer_ids reports pre-existing peers. The scale-in detach half is real. Blocker: offer 019ff6d4-7dda. Oracle must bind peers to the new node_instance_ids, not merely assert non-zero |
| SDWAN topology/routing read surfaces | Genuinely exercised (as oracles) | get_topology / routing summary reflect membership changes |
| Storage volume create/attach/detach + chown status | PARTLY BLOCKED — create yes, attach NO (corrected 2026-08-12) | ProvisionFullStackExecutor provisions a per-instance volume but never calls VolumeManagementService.attach, so ProviderVolume#node_instance_id stays nil. Both the scale-in teardown and its own zero-orphan sweep query that same nil FK, so the sweep certifies 0 orphans it structurally cannot see. Do not read a green zero-orphan volume result as evidence until offer 019ff6d4-e8cf lands; chown cannot be exercised on an unattached volume either |
| Provider lifecycle (PVE create/terminate, per-node placement) | Genuinely exercised | region_code IS the PVE node; placement fail-loud |
| Container runtime handshake (DockerHost) | Genuinely exercised | on the new replica |
| Fleet sense→decide→gate→act→validate arc | Genuinely exercised | the campaign's subject |
| Skill resolution + binding + usage-counter recording | Genuinely exercised | the actuator IS a skill (scale_project, bound to Fleet Autonomy); every dispatch resolves, binds, and must move usage_count/effectiveness via record_usage! — asserted as counters, not rows (§8) |
| Agent decision/policy records (Fleet Autonomy) | Genuinely exercised | the decider IS an agent; its decisions, policy resolutions, and approval requests are the loop's own gate records |
AgentExecution / LLM routing records |
Incidental, precondition-gated | the deterministic loop makes no LLM call; routing oracles require ai_task_tier_routing_enabled ON (default OFF) — read only under stated preconditions, never as a silent zero (§8 rule v) |
| Node identity PKI / Vault enrollment | Incidental | traversed by enroll; asserted only via enroll success |
| Signal/FleetEvent plumbing, skill-usage (F5), budget ledger | Incidental | recorded and read, not the subject |
| Disk-image boot path | Incidental | replica boots the promoted image; asserted via instance running only |
| Module builds / publishes / promotions / build batches | Deliberately NOT | prohibition (§9): unstoppable batches, auto-promote, fan-out (21-module incident, offer 019ff2aa-ace9). The loop plans against existing published versions only |
| Module changes on RUNNING instances (live recompose, 64MB scratch) | Deliberately NOT | assignments freeze at provisioning; mutating a running instance's modules is the vertical dimension — successor campaign |
| SDWAN network CRUD, VIPs, firewall/route policies, OVN, federation | Deliberately NOT | the loop consumes membership of dryrun-fabric; network-shape evolution is not the dimension (~150-tool surface; the membership slice is what scaling actually traverses) |
| Service exposure (Sdwan::Service, VIP/ACME/ingress) | Deliberately NOT | baseline runs no reachable service; exposure drags cert/ingress surfaces orthogonal to scale |
| Storage migration / chown-retry / snapshots | Deliberately NOT | migration is its own workflow-gated lifecycle; snapshots prohibited outright (§9 HARD RAIL) |
| Agent-fleet launches / new agents / teams | Deliberately NOT | real LLM cost + idle-burn (launcher default-OFF lesson); the loop uses only the existing seeded agents, bounded under config-driven ceilings |
| Skill auto-evolution / mutation / composition | Deliberately NOT | outside decision 1 (§1 boundary): skills are shared account-level config — mutating them escapes the prefix blast radius; successor-campaign candidate needing operator sign-off |
| K3s / cluster runtimes | Deliberately NOT | docker leg only |
| CVE / honeypot / GitOps / package lanes | Deliberately NOT (read-only) | other wired lanes; read as the INC-8 control group only |
Standing rules: (i) a green M2 smoke is never actuation evidence (drives the proposer
directly); loop oracles observe reconciler-path artifacts. (ii) DB rows alone are
never deployment proof — live pvesh/topology reads confirm. (iii) mtime proves
nothing; unit names are discovered (systemctl list-units 'powernode-*'), never
guessed. (iv) module/version identity is asserted from registry version ids/digests,
never "latest built" (the stage15 default-branch trap ships byte-identical artifacts
that look successful). (v) conditional oracles state their preconditions, and a zero
read with a precondition unmet is "not measured", never "pass" — the routing oracle
requires the ai_task_tier_routing_enabled flag ON (default OFF; off, it reads empty and
looks identical to "routing was fine"); the deterministic loop makes no LLM call, so
routing/execution oracles apply only if an LLM path is ever taken. (vi) count records
and counters from ground truth, never infer from a returned success — executions have
vanished inside a rescue before (silently-untracked global agents), and a
SkillUsageRecord row proves less than an incremented usage_count (the F5 bare-create!
counter bypass). (vii) plane: every assertion in this table reads the ops-hub
platform (dev-cell's DB is a fixture shell; its MCP proxies to ops-hub) — stated once so
no oracle silently reads the wrong plane.
| Dimension | Measurement | Ground truth | Pass |
|---|---|---|---|
| Legs 1–2 (reused) | harness §5 grading unchanged | predecessor protocol | hard |
| Self-observation | replica_count rows source: "live" for the soaking mission |
system_project_metrics |
hard |
| Signal | system.project_drift within 2 ticks of each amendment (both directions) |
signal stream (recent_signals) |
hard |
| Gate | ONE open request per (mission, category) on the fleet chain; removal requests never auto-approved; envelope gate: correct |
ai_approval_requests |
hard |
| Proposal | reconciler-path adaptation_diff plan; delta = target − observed (signed); steps carry network + storage inputs; zero LLM executions |
ai_goal_plans + ai_agent_executions |
hard |
| Actuation (out) | +1 instance under prefix on the right template/node; convergence time recorded | system_node_instances + live pvesh |
hard |
| SDWAN membership (out) | peer count on dryrun-fabric == replica count; the new peer has an issued /128; topology/routing summary reflects the member |
sdwan peer/allocation tables + sdwan_get_topology / routing-summary reads |
hard |
| Module/template (out) | new replica's assignment set == template closure (composed-vs-intended); every version id/digest matches the intended existing published version | assignments + module-version tables, drift_report / module_diff |
hard |
| Storage (out) | one volume created + attached per new replica, on the correct per-node storage; chown completed (not assumed — the retry surface exists because it fails) | provider_volumes / storage assignments + storage_chown_status |
hard |
| Verified-changed | post-adapt verify healthy; RemediationOutcome pending→effective because the metric cleared; on-node state via drift_report / discovered-unit is-active — never mtime |
remediation_outcomes + system_project_metrics + node reads |
hard |
| Skill lifecycle (per dispatch) | scale_project/remove_replicas resolved by slug and executed via the runner seam; usage_count/effectiveness deltas over the run (via record_usage!), not bare row existence |
ai_skills counters + ai_skill_usage_records |
hard |
| Actuation (in) | victim = newest replica; terminate confirmed against live PVE | instances + pvesh |
hard |
| Zero-orphan family (in + rollback) | after scale-in: 0 orphan peers (F-2 generalized), 0 orphan membership credentials, 0 orphan volumes/assignments, 0 orphan instances — one family, same shape per resource. Applies to the ROLLBACK path too, where it asserts EXISTING behavior: rollback_scale_project (:81-94) already reverse_eaches both node_instance_ids and storage_volume_ids — so a rollback-path orphan is a regression, not a feature gap. CAVEAT (2026-08-12): the volume limb of this family is currently self-blinding — teardown and the sweep both key off ProviderVolume#node_instance_id, which nothing populates (offer 019ff6d4-e8cf), so a green "0 orphan volumes" proves nothing until attach lands. Treat that limb as unproven, not passing |
peer/credential/volume/instance tables + live reads | hard |
| Supervision integrity | zero adaptations while halted; out-of-bounds and ALL removals park; rejected proposal actuates nothing + cooldown respected | approvals/kill-switch/instance timestamps | hard |
| Non-oscillation | adaptations/hour ≤ ceiling; no A→B→A inside the hysteresis window | plan/approval timestamps | hard |
| Control group (INC-8) | new lane's decision/outcome shape consistent with the four wired drift lanes under the same oracle reads; divergence = finding | remediation_outcomes + decision records, historical |
graded |
| Learning | ≥1 tagged CompoundLearning per E-run; repeat run no slower, no repeat findings | ai_compound_learnings + reports |
graded |
Prohibitions (by construction — the campaign's steps cannot reach these):
- HARD RAIL — no snapshots, and categorically no zvol snapshot on : 's ZFS
z_zvoltaskq is wedged; any zvolzfs snapshothangs in uninterruptible D state forever. No campaign step performs a snapshot of any kind; no rollback/durability mechanism in this charter may be designed in terms of snapshots. (If any future snapshot-adjacent step appears,qm snapshotrequires--vmstate 0— noted only so the rail survives charter edits; today nothing gets that far.) - No module builds, publishes, promotions, or build-batch dispatches. The loop plans
against existing published module versions exclusively. Rationale is incident
history, not caution: publish AUTO-PROMOTES; a dispatched batch cannot be stopped
(neither aborting tasks nor deleting the ref); one system-base edit planned 21+ modules
(offer
019ff2aa-ace9); two empty artifacts once whiteout-deleted Go+gitleaks off a live node. Nothing in scale/drift needs a build. - No module changes against running instances (assignments freeze at provisioning; live-recompose + its 64MB scratch budget belong to the vertical dimension, successor campaign).
- No mutation of
dryrun-fabricitself — peers attach/detach under the run prefix; the network, its addressing plan, and its hub are substrate. - No agent-fleet launches, no new agents, no skill mutations. The loop runs on the
existing seeded agents (Fleet Autonomy + the mission's agent) — bounded agent count by
construction, all spend under the existing config-driven ceilings; and no campaign step
calls
auto_evolve_skill/mutate_skill/compose_skills(§1 boundary). - Nothing outside the
dryrun-evo-NNprefix; ops-hub's own resources untouchable; PVE protection flags honored.
Rails on what the loop DOES do:
- Removal: always
require_approval, newest-first victims from the mission's own replicas, floor 1, via the platform's one terminate path. - Bounds:
MAX_DELTAouter clamp;auto_scale_max_replicas: 3campaign bound; per-mission dedup; consent budgets. - Storage placement is per-PVE-node and fail-loud — INC-3 threads
provider_region_idfrom the mission so volume plans name their node; a placement failure is a loud actuation failure, never a silent compose success. - Durability claims in oracles account for
/persistsemantics: the loop performs terminate (destroys instance state by design) and never re-provision-in-place or soft-reboot of retained nodes; no oracle asserts persistence across those operations. - Verification: discovered unit names,
daemon-reload-before-restart awareness, drift_report/module_diff — never mtime, never guessed units (§8 rules).
Stop conditions (any → stop, report, retain): emergency_halt; any artifact outside
the prefix (hard abort + page); > ai.evolution.max_adaptations_per_hour (SiteSetting,
default 6) or any A→B→A inside ai.evolution.hysteresis_minutes (default 30); LLM spend
at ai.dryrun.budget_usd; an orphan-family oracle failure after scale-in (stop before
teardown so forensics see the orphan); 3 failed attempts at one increment.
Teardown: PASS → soak-teardown command (cancel mission BEFORE sweep), prefix sweep
with completeness as an exit-code FINDING, zero-orphan family re-asserted at teardown
(peers, credentials, volumes, instances); dryrun-fabric persists. FAIL →
retain-with-runId + documented manual cleanup.
| Risk | Guard |
|---|---|
| Loop damages the fleet | prefix boundary; ops-hub out of scope; protection flags; operator gate until INC-7; bounds |
| Non-convergence / overshoot | INC-3 converge-to-target; convergence oracle |
| • Wrong victim on scale-in | newest-first from the mission's own replicas; victim identity is an oracle; removals never auto-apply |
| • Orphaned peers/credentials/volumes after scale-in | zero-orphan oracle family + stop-condition (retain for forensics); the PeerDetacher FK fix is live but asserted, not trusted |
| • Compute-only scale-out silently skipping SDWAN/storage | INC-3 threads network+storage; §8 rows fail hard if the peer/volume is missing |
| • Chown failure on the new volume | chown-status oracle (the retry surface exists because this fails in practice) |
| • Any step drifting toward builds/publishes/snapshots | §9 prohibitions by construction; no increment contains a build, publish, or snapshot verb |
| Oscillation | hysteresis + adaptations/hour ceiling; amendments are operator-initiated, one convergence each |
| F3-11 false-stuck while approvals wait | INC-1 exemption; E-run A observes settle-vs-approval interplay before INC-7 |
| LLM-composed plan auto-applies | INC-3 prerequisite for INC-7; provenance stamp checked |
| Harness double-drives / soak blocks other work | observer mode preserved; per-run_id guard (INC-5) |
| Gate silently absent (core mode) | fail-closed park + explicit envelope disposition (INC-2) |
| Wiring regresses legs 1–2 | red-first per increment; full provisioning suite + harness spec; extension specs run explicitly |
| • A zero-reading oracle mistaken for a pass (the F5 shape: 0 live across runs from a slug bug + a counter bypass) | §8 rules v–vi: counters not rows, preconditions stated, "not measured" ≠ "pass"; skill oracle asserts usage_count deltas via record_usage! |
| • Agent activity silently untracked (executions once vanished inside a rescue) | §8 rule vi: count records from ground truth; loop oracles name which record each dispatch writes and read it back |
One campaign — this one — and the second already exists in outline as its successor. Re-derived (not restated) at five surfaces, from an explicit split test.
The split test. A stacked surface forces a second campaign when it demands any of: (T1) its own actuation machinery beyond the loop's dispatch; (T2) a footprint change that alters the cost/risk envelope; (T3) its own approval posture incompatible with the ratified §4 gate; (T4) oracles that observe a different loop than the one being closed. Scored:
| Surface | T1 new machinery | T2 footprint | T3 posture | T4 different loop |
|---|---|---|---|---|
| SDWAN membership | no — network_id already threads into ProvisionFullStackExecutor; fix is composition input |
no (peers ride instances) | no | no — peer attach/detach IS the scale actuation |
| Module/template | no — traversed at every provision; builds prohibited | no | no | no — composed-set oracle reads the same provision |
| Storage/volume | no — with_storage_gb already threads; rollback even cleans volumes today |
+1 volume per instance (inside envelope) | no | no |
| Agent + skill | no — the actuator IS a skill, the decider IS an agent; assertions on records already written | zero | no | no |
| (the dimension itself) | yes, once: remove_replicas (INC-4) |
transient +1 | removals never auto-apply (§4, ratified) | — |
The only T1 "yes" in the whole table belongs to the dimension (bidirectional scale), not to any depth surface — and it is one extension-side executor strategy. Surface five is the cheapest fold of all precisely because the loop's dispatch already IS a skill-executed-by-an-agent; it contributes zero to every column. Eight increments (five build), footprint 2/2/2 steady → 3/3/3 transient peak, unchanged by addendum 4. Verdict: one campaign.
What would flip it (the same test, applied forward): a surface that scores a T1
"yes" with real machinery — skill auto-evolution (needs its own containment design;
excluded by the §1 boundary), SLO-telemetry-driven scaling (needs the telemetry
backend, 019ff39f-636a), or vertical module-version evolution (needs live-recompose
- canary machinery and touches the build system). All three are exactly the successor's dimensions — which is the verdict confirming itself: the split seam the operator asked me to find is the T1 line, and it is where the deferrals already sit.
A split on the "wire-the-lane vs exercise-the-surfaces" seam would be artificial: the
surfaces cannot be exercised through the evolution loop until the lane is wired, and
exercising them without the loop is legs 1+2 redux (already proven). The defensible
second campaign is the one this charter has been deferring all along —
evolution-dimensions (working title): SLO-telemetry-driven scaling (re-activates
offer 019ff39f-636a), cost-driven downscale policy, vertical module-version evolution
(live recompose + canary/rollback + the module-lifecycle surfaces this charter
deliberately excludes), relocation, and — as a candidate requiring its own operator
sign-off — baseline skill auto-evolution (the §1 boundary: it escapes the prefix
blast radius, so it needs its own containment design, not a quiet widening of
decision 1). Its backlog already exists as this campaign's deferred offers; its
dependency order is strict — it consumes the lane, seam, soak harness, and
remove_replicas actuator this campaign builds. This campaign runs first because every
dimension of the successor actuates through the machinery INC-1..5 create.
Nothing blocks the build sequence — §4 was ratified 2026-08-12 (recorded in §1/§4); the increments start on individual offer approvals alone. Genuinely deferred items:
- Fleet-wide post-campaign policy defaults — INC-8 retrospective input.
- Skill-auto-evolution: excluded by the §1 containment boundary (a baseline mutating account-level shared skills escapes its own prefix blast radius) — requires its own operator sign-off before any successor charter admits it as a dimension.
- Successor-campaign charter (
evolution-dimensions, operator-endorsed as the deferral target) — drafted after INC-8 confirms its backlog from evidence.