Repository navigation
archcheck: count writes of the sandboxes row outside sandbox-store - #690
Merged
Merged
Conversation
ac-pplx
force-pushed
the
ac/sandbox-writes-ratchet
branch
2 times, most recently
from
October 20, 2026 06:03
2c67df9 to
f03301b
Compare
ac-pplx
marked this pull request as ready for review
October 20, 2026 06:24
johnlouie-pplx
approved these changes
Oct 20, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 21, 2026
Writes of the control-plane Postgres `public.sandboxes` row are to move into its store, `pplx-space-sandbox-store`. A new archcheck instance, `pattern_ratchet.sandbox_writes`, counts the writers that are still outside it, per file, so the count can only shrink. Configuration only: no production code, schema, wire format, metric or configuration change. - ARCHITECTURE.toml: the instance counts each production line that opens a statement on the row's table (`UPDATE`, `INSERT INTO`, `DELETE FROM`, `MERGE INTO` or `TRUNCATE` naming `sandboxes`; any case, extra whitespace, `ONLY`, a `public.` schema or quoting, before any alias, also right after a `\n`, `\r` or `\t` escape), and each call to the sandbox-portability renderers that build part of its SET list (`sandbox_assignments()` as a method or path call, and the three `*_current_local_artifact_marker_assignments` functions, also after `return`, `match`, `if`, `in`, `else` or `while`). `allow` covers the store, sbx-mgr's crates whole (node-local SQLite has its own `sandboxes` table, a different database already guarded by the `sbxmgr_*` rules, and a narrower glob would fire on moves within sbx-mgr), and the `#[cfg(test)]` support modules and fixtures that archcheck's filename-based test rule counts as production, the same list `substrate_literals` uses. `no_crossing`, `listed_no_growth` and `stale_entries` are required; `no_new_entries` and `no_raised_caps` are advisory. The comment lists what the patterns miss, their false positives, and the exception paths: a new entry for a new writer, a raised cap for another write in a listed file, new entries and a lower cap for a module split, and a `*_tests` file for test-only writes. - architecture-baseline.toml: `[pattern_ratchet.sandbox_writes]` lists the 28 files that write the row today, 100 matching lines in all: 86 statement heads (56 that write `lifecycle`, 23 that write other columns of the row, 7 hard deletes) and 14 renderer calls. Other tables are unchanged. The two placement-machine claim statements under `crates/libs/placement/machine/src/postgres/sql/` are loaded with `include_str!`, and archcheck reads only `.rs` files, so they are not counted.
ac-pplx
force-pushed
the
ac/sandbox-writes-ratchet
branch
from
October 21, 2026 04:19
f03301b to
4390797
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 0 of giving the control-plane Postgres
public.sandboxesrow one writer. This PR adds an oracle: an archcheck ratchet that counts, per file, the production code that writes the row outsidepplx-space-sandbox-store. Writes of the Postgres sandboxes row move into sandbox-store; this ratchet only shrinks. One commit, configuration only (ARCHITECTURE.toml,architecture-baseline.toml). It makes no production code, schema, wire format, metric or configuration change.Scope: only the control-plane Postgres
public.sandboxestable. sbx-mgr's node-local SQLite database also has asandboxestable (crates/libs/sbx/**). That is a different database on a different machine, with its own store and fences (sbxmgr_sqlite,dependency_fence.sbxmgr_*). It is not counted, and the rule allows sbx-mgr's crates.Rebase onto
mainThe branch was rebased onto
84963a0. Between59789a1and there, #734 (a revert of #217) removed the twoUPDATE sandboxes SET target_node_id …statements,claim_create_targetandrelease_create_target, fromcrates/libs/api/src/controllers/substrate/direct.rs. Thestale_entriesrule is required, so the old entry for that file (7) failed once combined withmain.The counts were refreshed after the rebase onto
main, because #734 removed two statements fromdirect.rs:crates/libs/api/src/controllers/substrate/direct.rstarget_node_idstatements are gone;cargo archreported exactly thisTotals: 102 → 100 matching lines, 88 → 86 statement heads (56 that write
lifecycle, 25 → 23 that write other columns, 7 hard deletes), 14 renderer calls, still 28 files. The inventory below drops those two rows (renumbered) and refreshes thenode_drain/store.rsline numbers that moved onmain. Every other entry matches its file, and no production file outsideallowgained a match. The patterns,allowand modes are unchanged. The review-round sections and the earlier probe rows keep the figures as measured before the rebase.The rule:
[rules.pattern_ratchet.sandbox_writes]H matches
UPDATE,INSERT INTO,DELETE FROM,MERGE INTOandTRUNCATE [TABLE]namingsandboxes. It matches in any case and with any whitespace, withONLY, apublic.schema, or quoting (including an escaped\"sandboxes\"in a Rust string), before any alias. It also matches right after a\n,\ror\tescape in a one-line string ("WITH c AS (…)\nUPDATE sandboxes …"). The trailing\bkeepssandbox_k8s_executions,sandboxes_archiveand similar names out.R/M: the statements in
suspend.rs,orphaned_sandboxes.rs,started_sandbox_repair/store.rs,failed_restorable_repair/store.rs,published_state.rsandpod_tracking.rsinterpolate SET lists fromcrates/libs/sandbox/portability/src/lib.rs. These areSuspendedK8sFinalizationSql::sandbox_assignments, as a method call or a path call, and the*_current_local_artifact_marker_assignmentsfunctions, after punctuation, a line start, orreturn,match,if,in,elseorwhile. Each call is counted too. So a file that composesUPDATE sandboxes s SET {sandbox_assignments}counts the head and each renderer call. Portability's own definitions, its privatesandbox_assignments(...)helper, anduselists don't match, so portability needs no entry or allow. Portability calls that helper bare. A path-qualified call to it there (Self::...,super::...) would match and need a reviewed entry; the comment says so.allow:crates/libs/sandbox/store/**, the owner.crates/libs/sbx/**andcrates/apps/sbx/**: sbx-mgr's node SQLite. The comment says it is a different database. The crates are allowed whole. Thesbxmgr_sqliteratchet and thesbxmgr_*dependency fences guardcrates/libs/sbx/mgr/**andcrates/apps/sbx/**, which today are all ofcrates/libs/sbxandcrates/apps/sbx. A narrower glob would make this required rule fire on moves within sbx-mgr, which another effort is restructuring.#[cfg(test)]support and fixture globs assubstrate_literals(**/*_tests/**,**/*_test_support.rs,crates/libs/db-test-fixtures/**, …). archcheck's test rule is filename-based, so these count as production. After the rebase, a recount over the same globs finds 32 matching lines in 13 files.#[cfg(test)]code inline in a production file counts as production, so test-only writes go in a*_testsfile. I checked each matching file: all are#[cfg(test)]modules or the dev-only fixtures crate.Modes:
no_crossing,listed_no_growthandstale_entriesarerequired.no_new_entriesandno_raised_capsareadvisory, as inkubernetes_access,capability_identityandsubstrate_literals. On this PR,no_new_entriesreports each of the 28 entries, as expected for a new instance.Exception paths (in the comment), each a reviewed baseline edit:
no_new_entries);no_raised_caps);--renamespairs only whole-file renames.node_drain/store.rs, with 15, is a likely candidate.Not caught (the same list as the instance comment):
UPDATEandsandboxeson different lines) or built at run time (format!("UPDATE {table} ..."));COPY sandboxes FROM ...;.rsfile, such as the placement state machine'sinclude_str!claim statements underpostgres/sql/;\n,\ror\t(\0,\x0a);use ... asrename, passed as a function value (.map(SuspendedK8sFinalizationSql::sandbox_assignments)), or called right afterbreakoryield;src/.False positives:
//or/* */comment, in#[doc = "…"], in a SQL--comment, or in an ordinary string such as an error message ("failed to update sandboxes …");Whole-line
//comments are not code lines, so the doc comments that mentionUPDATE sandboxesdon't count. Onmain, nothing in the tree hits these classes: run over every.rsfile outside sbx-mgr, tests included, every match is an upper-case statement head or a renderer call.Inventory: production writes of Postgres
public.sandboxes, refreshed after the rebase ontomain(84963a0)Method: I scanned every
.rsand.sqlfile undercrates/for the three statement heads, across lines and in any case. I also checked forMERGE/TRUNCATE/COPY, for heads built from a{table}placeholder, and for SET-list renderers, and then classified each SET list. Excluded: test files, the#[cfg(test)]support modules above, sbx-mgr's crates, and whole-line comments (four doc and//comments mention a head).placement,active_node_id,target_node_idandplacement_changed_at. "Markers" meanscurrent_local_artifact_{id,kind,origin}. "Lease" meansrestore_lease_tokenandrestore_lease_expires_at.sandbox_assignmentswith target-node mode V. It always writeslifecycle(suspended, or quarantined forquarantine_orphan),placement = 'unplaced',active_node_id = NULLandcommand_ready_pending = FALSE.Clearaddstarget_node_id/placement_changed_at = NULL,PreserveOrBackfillActivebackfillstarget_node_id, and the caller's marker assignments are appended.api/src/controllers/sandbox/lifecycle.rs:180begin_delete_against_active_forklifecycle→deletingFOR UPDATEand no-fork check in the same txapi/src/controllers/sandbox/lifecycle.rs:201restore_lifecycle_after_failed_deletelifecycle→previouslifecycle = 'deleting'api/src/controllers/sandbox/lifecycle.rs:238restore_lifecycle_after_failed_finalize_deletelifecycle→previouslifecycle = 'deleting'api/src/controllers/sandbox/lifecycle.rs:393complete_wake_lifecyclelifecycle→started,placementapi/src/controllers/sandbox/queries.rs:59finalize_created_sandboxlifecycle,placement,active_node_id,placement_changed_at,command_ready_pending,preserve_local_artifact_on_restore_failureapi/src/controllers/sandbox/queries.rs:1465create_with_sandbox_id_innerapi/src/controllers/sandbox/queries.rs:1542create_with_sandbox_id_innerapi/src/controllers/sandbox/queries.rs:1583create_with_sandbox_id_inneractive_node_id,placement_changed_atapi/src/controllers/sandbox/responses.rs:350maybe_update_sandbox_lifecyclelifecycle→action resultapi/src/controllers/sandbox/responses.rs:380maybe_update_sandbox_statelifecycle→started,placementapi/src/controllers/sandbox/responses.rs:781update_and_fetch_post_wake_sandboxlifecycle→started,placement,active_node_id,placement_changed_atapi/src/controllers/sandbox/updates.rs:399set_network_policynetwork_policy_override_json,network_policy_generationapi/src/controllers/sandbox/updates.rs:520set_snapshots_enabled_desiredsnapshots_enabled_desiredapi/src/controllers/sandbox/updates.rs:1054rollback_created_sandboxapi/src/controllers/sandbox/updates.rs:1075rollback_created_sandboxapi/src/controllers/sandbox/wake_completion.rs:236fenced_completion_caslifecycle→started,placement'restoring', lease lapsed, routable executionapi/src/controllers/substrate/direct.rs:961persist_owned_restore_route_identity_receiptactive_node_id,target_node_id,placement,placement_changed_atapi/src/controllers/substrate/direct.rs:2414claim_direct_restore_leaselifecycle→restoring, leaseapi/src/controllers/substrate/direct.rs:2480release_direct_restore_leaselifecycle→suspended (kept on retry), lease'restoring'and lease tokenapi/src/controllers/substrate/direct.rs:2530renew_direct_restore_leaserestore_lease_expires_atapi/src/controllers/substrate/direct.rs:2558complete_direct_restore_leaselifecycle→started,placementapi/src/controllers/substrate/kubernetes/store/activation_fence.rs:503clear_command_ready_pendingcommand_ready_pending'started'and pendingapi/src/controllers/substrate/kubernetes/store/execution_rows.rs:660ensure_active_execution_claim_for_lifecyclerestore_lease_expires_atapi/src/controllers/substrate/kubernetes/store/pod_placement.rs:213persist_pod_node_placementactive_node_id,target_node_id,placement,placement_changed_atapi/src/controllers/substrate/kubernetes/store/pod_tracking.rs:431clear_pod_tracking_in (clock bump)placement,active_node_id,target_node_id,placement_changed_atapi/src/controllers/substrate/kubernetes/store/pod_tracking.rs:439clear_pod_tracking_in (no bump)placement,active_node_id,target_node_idapi/src/controllers/substrate/kubernetes/store/pod_tracking.rs:464recover_markerless_restoring_after_execution_cleanup_inlifecycle→suspended, placement, markers,command_ready_pending, lease'restoring', no marker, no active execution, safe snapshotapi/src/controllers/substrate/kubernetes/store/pod_tracking.rs:588recover_started_with_current_suspend_snapshot_inlifecycle→suspended, placement,command_ready_pending, markers (renderer call :577)'started', unplaced, execution proof, eligibilityapi/src/controllers/substrate/kubernetes/store/restore_lease.rs:251revert_lifecycle_after_rollback_best_effortlifecycle→suspendedapi/src/controllers/substrate/kubernetes/store/restore_lease.rs:345begin_restore_lifecycle_for (marker branch)lifecycle→restoringapi/src/controllers/substrate/kubernetes/store/restore_lease.rs:397begin_restore_lifecycle_for (user)lifecycle→restoring,evacuation_restore_target, leaseapi/src/controllers/substrate/kubernetes/store/restore_lease.rs:520claim_markerless_restoring_restore_lease'restoring', no marker, lease lapsed, no fresh executionapi/src/controllers/substrate/kubernetes/store/restore_lease.rs:584release_user_restore_leaserestore_lease_expires_atapi/src/controllers/substrate/kubernetes/store/suspend.rs:144finalize_suspended_sandbox_inlifecycle,placement,active_node_id,command_ready_pending, markers (renderer calls :129, :150)api/src/controllers/substrate/kubernetes/store/suspend.rs:305persist_or_accept_suspend_commitlifecycle→suspending, markers'suspending'api/src/controllers/substrate/kubernetes/store/suspend.rs:339claim_suspend_executionlifecycle→suspendingapi/src/controllers/substrate/kubernetes/store/suspend.rs:679finalize_missing_suspending_pod_with_current_snapshotlifecycle,placement,active_node_id,command_ready_pending, markers (renderer calls :671, :678)'suspending'in the same txapi/src/controllers/substrate/mod.rs:357persist_optional_current_local_artifactplacement/machine/src/postgres.rs:105completion_sql (complete_placement)lifecycle→COALESCE(marker, started), placement,evacuation_restore_target, lease'stopped'sandbox/teardown/src/lib.rs:129finalize_sandbox_db_delete_txcluster/controller/server/src/drained_node_terminator/store.rs:275mark_unrecoverable_sandboxes_lost_querylifecycle→failed'suspended', terminated target, not durable, settle guardcluster/controller/server/src/drained_node_terminator/store.rs:647mark_unrecoverable_sandboxes_lostplacement,active_node_idcluster/controller/server/src/failed_restorable_repair/store.rs:145revert_failed_to_suspendedlifecycle,placement,active_node_id,command_ready_pending,target_node_id,placement_changed_at, markers, lease (renderer calls :132, :155)'failed', aged, lease lapsed, no executioncluster/controller/server/src/node_drain/store.rs:125claim_evacuation_suspendlifecycle→suspending,evacuation_restore_targetcluster/controller/server/src/node_drain/store.rs:360claim_stopped_evacuationevacuation_restore_target→stopped'stopped'on the node, marker and pin checkscluster/controller/server/src/node_drain/store.rs:501record_stopped_evacuation_artifactcluster/controller/server/src/node_drain/store.rs:583revert_evacuation_suspend_claimlifecycle→previous,evacuation_restore_target'suspending', no suspended executioncluster/controller/server/src/node_drain/store.rs:1253schedule_restore_retry_queryrestore_attempt_count,next_restore_attempt_atcluster/controller/server/src/node_drain/store.rs:1284redrive_stranded_suspending_querylifecycle→suspended'suspending', marker, unplaced, artifactcluster/controller/server/src/node_drain/store.rs:1485mark_sandbox_lost_querylifecycle→failed'suspended', target node, not durable, no uploadcluster/controller/server/src/node_drain/store.rs:1525mark_sandbox_lost_on_gone_node_querylifecycle→failed'suspended', gone node, no ready snapshot, identity guardscluster/controller/server/src/node_drain/store.rs:1682adopt_remote_snapshot_on_gone_node_queryevacuation_restore_target,placement_changed_at'suspended', target node, aged, ready snapshot, identity guardscluster/controller/server/src/node_drain/store.rs:1753mark_sandbox_failed_on_node_querylifecycle→failed,evacuation_restore_targetcluster/controller/server/src/node_drain/store.rs:1830adopt_remote_snapshot_for_drain_queryevacuation_restore_target,placement_changed_at'suspended', target node, aged, ready snapshotcluster/controller/server/src/node_drain/store.rs:1915abandon_stale_resident_sandboxes_querylifecycle→failedcluster/controller/server/src/node_drain/store.rs:2010fail_resident_sandboxes_on_gone_node_querylifecycle→failed,evacuation_restore_targetcluster/controller/server/src/node_drain/store.rs:3406SubstrateSink::restore_evacuated_and_finalizelifecycle→target,placement,evacuation_restore_target'restoring'cluster/controller/server/src/node_drain/store.rs:4499SubstrateSink::evacuate_sandboxlifecycle→suspended'suspending'cluster/controller/server/src/orphaned_sandboxes.rs:1602recover_orphan_to_suspendedlifecycle,placement,active_node_id,command_ready_pending,target_node_id,placement_changed_at, markers (renderer calls :1575, :1582)cluster/controller/server/src/orphaned_sandboxes.rs:1663quarantine_orphanlifecycle,placement,active_node_id,command_ready_pending,target_node_id,placement_changed_at(renderer call :1644)cluster/controller/server/src/orphaned_sandboxes/quarantine_recovery/store.rs:139recoverlifecycle→suspended, placement, markers,command_ready_pending, lease,lifecycle_changed_at,updated_atcluster/controller/server/src/started_sandbox_repair/store.rs:206revert_with_execution_identitylifecycle,placement,active_node_id,command_ready_pending,target_node_id,placement_changed_at, markers (renderer calls :192, :217)'started', aged, no execution, max generation, eligiblecluster/controller/server/src/suspended_execution_reaper/store.rs:206retire_stale_executionplacement,target_node_id,active_node_id, markerscluster/controller/server/src/tombstone_retention.rs:168prune_tombstones'deleted'rows past retentioncluster/controller/server/src/transient_lifecycle_sweeper/store.rs:75HEAL_COMMAND_READY_PENDING_SQLcommand_ready_pendingmanaged_by, pendingcluster/controller/server/src/transient_lifecycle_sweeper/store.rs:118REPAIR_FAILED_ACTIVE_EXECUTION_SQLlifecycle→started'failed', on node, active execution, Kubernetescluster/controller/server/src/transient_lifecycle_sweeper/store.rs:140REAP_COMMAND_READY_PENDING_SQLlifecycle→failed'started'and pendingcluster/controller/server/src/transient_lifecycle_sweeper/store.rs:180undo_revert_to_suspended_querylifecycle,preserve_local_artifact_on_restore_failure'suspended'and own write tokencluster/controller/server/src/transient_lifecycle_sweeper/store.rs:196undo_reap_command_ready_pending_querylifecycle→started,command_ready_pending'failed'and own tokencluster/controller/server/src/transient_lifecycle_sweeper/store.rs:519fail_candidate_querylifecycle→failedcluster/controller/server/src/transient_lifecycle_sweeper/store.rs:532undo_fail_aged_candidate_querylifecycle'failed'and own tokencluster/controller/server/src/transient_lifecycle_sweeper/store.rs:569converge_restoring_querylifecycle→started/paused'restoring', aged, lease lapsed, routablecluster/controller/server/src/transient_lifecycle_sweeper/store.rs:609revert_to_suspended_querylifecycle→suspended'restoring', aged, ready snapshotcluster/controller/server/src/transient_lifecycle_sweeper/store.rs:722fail_unrestorable_restoring_querylifecycle→failed'restoring', unplaced, no source or uploadinternal/api/server/src/data_plane_sessions/service_route.rs:333clear_sandbox_command_ready_pendingcommand_ready_pending'started'and pendinginternal/api/server/src/published_state.rs:1780DELETE_ABSENT_STOPPED_SANDBOXESinternal/api/server/src/published_state.rs:2832publish_sandbox_states (activity)last_access_at,last_command_at,last_used_atinternal/api/server/src/published_state.rs:2916publish_sandbox_stateslifecycle→started'restoring', node says started, routable, no markerinternal/api/server/src/published_state.rs:3016publish_sandbox_stateslifecycle→stoppedinternal/api/server/src/published_state.rs:3144publish_sandbox_stateslifecycle→quarantinedinternal/api/server/src/published_state.rs:3460project_node_published_failed_sandboxeslifecycle→failedinternal/api/server/src/published_state.rs:3747apply_suspended_k8s_finalizationlifecycle,placement,active_node_id,command_ready_pending,target_node_id, markers (renderer calls :3622, :3754)internal/api/server/src/sandbox_quarantine_store.rs:387consume_activation_completion_receiptslifecycle→receipt target,placementinternal/api/server/src/sandbox_quarantine_store.rs:558apply_activation_rollback_candidateslifecycle, placement,command_ready_pending,lifecycle_changed_atinternal/api/server/src/sandbox_quarantine_store.rs:645recover_pause_failed_from_inventorylifecycle→started/paused'quarantined'and matching holdinternal/api/server/src/status.rs:44PRUNE_ABSENT_NODE_SANDBOXES_QUERY (prune_absent_node_sandboxes)lifecycle→deleted, placement,command_ready_pending, lease,lifecycle_changed_at,updated_atThe table has 86 statements: 56 write
lifecycle, 23 write other columns of the row (placement, lease, markers,evacuation_restore_targetand restore backoff,command_ready_pending, activity timestamps, network policy,snapshots_enabled_desired), and 7 are hard deletes. The 14 renderer calls are atsuspend.rs:129, :150, :671, :678;pod_tracking.rs:577;orphaned_sandboxes.rs:1575, :1582, :1644;started_sandbox_repair/store.rs:192, :217;failed_restorable_repair/store.rs:132, :155; andpublished_state.rs:3622, :3754.Writes the ratchet does not count
crates/libs/sandbox/store/src/create.rs:43,:98insert_unplaced,insert_pooled(INSERT INTO sandboxes)allow)crates/libs/sandbox/store/src/lib.rs:474,:515update_desired_state(metadata,vcpu,memory_mib),set_auto_lifecycle_policy(auto_lifecycle_json,auto_lifecycle_state_json,next_auto_event_at)allow)crates/libs/placement/machine/src/postgres/sql/user_restore_claim.sql:44claim_placement(UserRestore):lifecycle→restoring,evacuation_restore_target, lease; guard: suspended or lapsed restoring, no live execution; no production caller.sqlloaded withinclude_str!; archcheck reads only.rs…/sql/evacuation_restore_claim.sql:49claim_placement(Evacuation):lifecycle→restoring, lease; guard: marker set, lease and backoff lapsed; no production callerscripts/admin/prune_cluster_artifacts.py,scripts/repro-same-node-restore-no-remote-fallback.py,space_smoke/adapters.py,scripts/tests/*src/crates/libs/sbx/**)sandboxesallow)#[cfg(test)]support and fixturesallow)Totals (86 counted statements; 100 matching lines with the renderer calls)
finalize_sandbox_db_delete_tx(sandbox-teardown) is called from API delete and the public-operation executor, API create rollback, internal-api's auto-lifecycle delete receipts, cluster-controller's orphan classification and warm-pool cleanup.SubstrateSink, and pod cleanup. The API writers also run inside cluster-controller's public operation dispatcher.Probe
Per pattern (each probe line was checked alone against each pattern with
rg, which uses archcheck's regex engine): 45 positives each matched only their own pattern (H 26, R 6, M 13); 36 look-alikes (H 21, R 8, M 7) and 8 listed misses matched none; the 5 listed false positives matched. Lines added for the review round are in bold.UPDATE sandboxes SET …;UPDATE sandboxes s SET;UPDATE sandboxes AS s;update sandboxes set;UPDATE sandboxes<TAB>SET;UPDATE public.sandboxes;UPDATE \"sandboxes\";r#"UPDATE "public"."sandboxes""#;UPDATE ONLY sandboxes; bare"UPDATE sandboxes";format!("UPDATE sandboxes s SET {sandbox_assignments} …");WITH moved AS (UPDATE sandboxes …);sql!("DELETE FROM sandboxes …");DELETE FROM sandboxes s;delete from sandboxes;DELETE FROM public.sandboxes;INSERT INTO sandboxes (…);INSERT INTO public.sandboxes (;insert into sandboxes(id);MERGE INTO sandboxes s USING …;TRUNCATE sandboxes;TRUNCATE TABLE sandboxes CASCADE;"WITH c AS (…)\nUPDATE sandboxes s SET …";"SELECT pg_advisory_xact_lock($1);\tDELETE FROM sandboxes …";"SELECT 1;\r\nINSERT INTO sandboxes …";"\rUPDATE sandboxes …"UPDATE sandbox_k8s_executions e;UPDATE sandbox_port_exposures spe;DELETE FROM sandbox_quarantine_state;DELETE FROM sandbox_direct_route_identities;INSERT INTO sandbox_events;INSERT INTO sandboxes_history;UPDATE sandboxes_archive;SELECT … FROM sandboxes … FOR UPDATE;FOR NO KEY UPDATE OF s;FOR UPDATE OF sandboxes;ON CONFLICT … DO UPDATE SET;UPDATE nodes;UPDATE sandbox_k8s_executions … FROM sandboxes s;SELECT lifecycle FROM sandboxes;"updated sandboxes after drain";let sandboxes_updated = …;"… WHERE id = $1\nFOR UPDATE";"WITH c AS (SELECT 1)\nUPDATE sandbox_k8s_executions e …";"SELECT 1;\tDELETE FROM sandboxes_archive …";"rows:\n{n} updated sandboxes";"COPY sandboxes TO STDOUT"sandbox_assignments = finalization.sandbox_assignments(),;let a = finalization.sandbox_assignments();; chained.sandbox_assignments();;suspended_k8s_finalization_sql(…).sandbox_assignments();SuspendedK8sFinalizationSql::sandbox_assignments(&finalization);pplx_space_sandbox_portability::SuspendedK8sFinalizationSql::sandbox_assignments(&f)SET {sandbox_assignments};pub fn sandbox_assignments(&self);fn sandbox_assignments(; private callsandbox_assignments(at line start;.active_execution_assignments("e");.open_port_exposure_assignments();quarantine_state_conflict_assignments();render_sandbox_assignments(&f)let m = clear_…(; line-startclear_…(;write_…(; line-startpublish_clear_or_preserve_…(;pplx_space_sandbox_portability::clear_…(;render(&clear_…(;format!("SET {}", write_…(…));return clear_…(…);;match write_…(…) {;if publish_clear_or_preserve_…(…).is_empty() {;for part in clear_…(…).split(',') {;} else if write_…(…).is_empty() {;while pplx_space_sandbox_portability::clear_…(…).is_empty() {pub fn …(definitions;pub(crate) fn write_…(;SuspendedTargetNodeAssignment, clear_…,(use list);use …::write_…;;matched_clear_…("s")(a longer name)"UPDATE/sandboxes SET …split over two lines;format!("UPDATE {table} SET …");format!("{verb} sandboxes SET …");"COPY sandboxes FROM STDIN";use …::clear_current_local_artifact_marker_assignments as clear_marker;and thenclear_marker(…);include_str!("postgres/sql/user_restore_claim.sql").// … UPDATE sandboxes SET …;#[doc = "UPDATE sandboxes …"]; a SQL-- DELETE FROM sandboxes …comment;anyhow!("failed to update sandboxes on node {node}"); an sbx-mgr-style"UPDATE sandboxes SET state = ?1 WHERE id = ?2"outside sbx-mgr's crates.Through archcheck (scratch files, deleted afterwards; the rows up to the probe rows were measured before the rebase, the last two after it):
sandbox_writesresultmainbefore the rebase (59789a1) + thisARCHITECTURE.toml, base baseline (no table)no_crossingfindings, 102 lines;cargo archexits 1origin/mainat188c1a3(before #734) + this config and baselinef03301b)sandbox_writesfinding without--base-baseline; with the CI flags, 28 advisoryno_new_entries(one per entry);cargo archexits 0no_crossing: exactly 50 lines (45 positives + 5 false positives)84963a0+ this commit), before the fixstale_entries(direct.rs7 → 5);cargo archexit 1direct.rslowered to 5sandbox_writesfinding; with the base comparisons (--base-baseline,--base-architecture) 28 advisoryno_new_entries, nono_raised_caps;cargo archexits 0crates/libs/sbx/mgr/store/src/db/and incrates/apps/sbx/mgr/cli/src/sbxmgr_sqlite, as it should be)*_test_support.rs, a*_tests/module andcrates/libs/sandbox/store/src/DELETE FROM sandboxesintombstone_retention.rslisted_no_growth(1 → 2)updates.rshead renamed off the tablestale_entries(4 → 3)The rule matches exactly the inventory above, per file. It matches no sbx-mgr statement, comment, doc string or test.
Review round 1
\n/\r/\tescape in a one-line string was missed--renamespairs only whole-file renames) and inline#[cfg(test)]writes (move them to a*_testsfile)sbxmgr_*ratchet and fences already guard those crates, and a narrower glob would fire on moves within sbx-mgrNone of the newly caught forms occur in the tree, so the baseline is unchanged: 28 files, 102 lines, at this branch's base and at the then-current
origin/main. The branch merged cleanly at that point, so it was not rebased yet.Review round 2
Comment wording only; the patterns and baseline are unchanged.
sbxmgr_*rules guardcrates/libs/sbx/mgr/**andcrates/apps/sbx/**, which today are all ofcrates/libs/sbxandcrates/apps/sbx. The glob is kept.::form would catch a path-qualified call to portability's private helperSelf::/super::call there needs a reviewed entry. Checked: both forms match, the bare call doesn't.break/yield-prefixed calls, and heads after a\0/\x0aescape. Each was checked to miss. This body's list matches the comment.Validation
cargo arch(rebased)main(84963a0)cargo arch --format rdjson --base-baseline <main> --renames <diff> --base-architecture <main>main's 98 + 28pattern_ratchet.sandbox_writes.no_new_entrieswarnings; 0ERRORcargo test --locked -p pplx-space-archcheck(unit,tests/cli.rs,tests/repository.rs)4390797(includingrust_static / Rust clippy, which runscargo archwith the base comparisons, andcommon / Rust tests);kind_smokeis skippedNo Rust code changes, so no workspace build or test beyond archcheck's own; per the repository's validation rules, a configuration-only change needs only the checks that prove it.
Notes for review
crates/libs/sandbox/store/src/lib.rs).sql!locators embedline!(), so any line added there would change the statement text of every verb below it. The rule's comment inARCHITECTURE.tomland the baseline header carry the contract instead.space-data-store-developmentskill still say that guarded transitions are not store verbs and that placement transitions belong topplx-space-placement. This ratchet counts both as writes outside the store, which is the plan's direction. Updating that text belongs with the phase that moves the first guarded transition, so this PR doesn't touch it.crates/libs/sandbox/store/src/lib.rs) say there are "81 productionUPDATE sandboxesstatements" outside the node-local table. That figure predates Revert "Preserve authenticated scope in cached template lookups (#217)" #734, which removed two of them (see "Rebase ontomain" above); this inventory has two fewer: 79UPDATEheads in.rsfiles outside the store (86 − 7 deletes).🤖 Generated with Claude Code