§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)

PieceWhat landed
Scopepath_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.
Wirecli/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.
Guardnew _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

ItemStatusEvidence
reset/clear path scoping + CLI wiring + guardshippedimas-codex ae585857 (cli/sn.py +61, graph_ops.py +64, new test +255)
Testsgreen89 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.