§3 — Tooling: sn run --reset-to path scoping + guard ✓ landed 2026-07-05
Evergreen: catalog-naming-followups §3
(decision reset-focus-fix = scope-and-guard, followup f-cnf-001).
The bug
The CLI --reset-only/--reset-to branch called clear_standard_names/
reset_standard_names with ids_filter=None hardcoded and never threaded the
--focus path list (flat_focus) into the reset. So sn run --reset-to extracted
--focus <paths> reset far more than the focused paths, and with --include-accepted it
cleared 1863 accepted names graph-wide during the rc60 session (recovered via sn import).
Neither reset function accepted a dd-path allowlist.
The fix (scope-and-guard)
| Piece | What landed |
|---|---|
| Scope | path_allowlist: list[str] | None added to both
reset_standard_names and clear_standard_names: selection joins through
(src:IMASNode)-[:HAS_STANDARD_NAME]->(sn) filtered by src.id IN $path_allowlist
— an exact-path membership test, distinct from and combinable with the ids_filter prefix
(STARTS WITH). reset scopes every field-clear/edge-delete to the matched
sn.id set; clear uses relationship-first delete (drop the listed paths' edges, then
DETACH DELETE only now-orphaned nodes) so names on any other path — accepted ones included —
keep their edges and survive. |
| Wire | cli/sn.py passes path_allowlist=flat_focus or None into BOTH
reset branches (the --reset-only branch and the default DD/signals --reset-to branch),
so sn run --reset-to {extracted,drafted} --focus <paths> resets exactly those paths. Empty
focus → None → unchanged behaviour. |
| Guard | new _reject_unscoped_accepted_reset() fires before either
reset branch and raises click.UsageError when --reset-to … --include-accepted has NO
scope — no --focus path list AND no row-level narrowing filter
(--retry-quarantined/--below-score/--since/--before/
--tier). A bare sn run --reset-to extracted --include-accepted now hard-errors (the
1863-wipe footgun); a dry-run is exempt (previewing touches nothing). Row-level filters count as scope so the
documented --retry-quarantined --reset-to extracted --include-accepted migration keeps working. |
Outcome
| Item | Status | Evidence |
|---|---|---|
| reset/clear path scoping + CLI wiring + guard | shipped | imas-codex ae585857 (cli/sn.py +61, graph_ops.py +64, new test +255) |
| Tests | green | 89 passed / 0 failed (13 new in test_reset_focus_scoping.py); test_sn_clear, test_graph_ops, test_sn_generate_cli all green — all mocked GraphClient, production codex graph never touched |
What's next
§4 (orientation-vector completion) uses the new scoped reset: sn run --reset-to drafted --focus
"<path> <path>". The accepted-wipe footgun is closed — --reset-to … --include-accepted
without a focus/filter now errors instead of destroying committed names.