RFC: Absorb safedel trash-store primitives into filekit (no current consumer — deferred)
Status: deferred / informational
This issue captures investigation findings for a possible future migration. No work is planned. Filed because:
- We (the owners of both repos) want to preserve the design thinking so it isn't lost
- If a future consumer emerges who needs recoverable deletes at library level, this issue is the starting point
- The investigation was substantial (2 parallel agents, multiple design docs) and shouldn't be re-derived from scratch later
The question
Should filekit absorb dz safedel's trash-store primitives (staging, recovery, listing, link classification, protection zones, time-pattern matching, volume routing) so any filekit consumer can do recoverable deletes in-process without a subprocess call?
TL;DR answer: not now
Reason: no consumer besides safedel itself currently needs this. The only caller who would benefit (dazzlecmd Phase 3.5.A mode switch) can simply subprocess to dz safedel directly, because safedel is part of dazzlecmd core and is guaranteed available wherever dazzlecmd runs.
Filekit's existing layering contract ("primitives layer — stateless, no policy, no workflow orchestration", per docs/preservelib-integration.md) explicitly puts trash-store logic in the higher workflow layer. Moving it into filekit would be a deliberate scope expansion with no justifying consumer.
What the investigation found
Safedel code surface (3,507 LOC across 8 modules in dazzlecmd/projects/core/safedel/):
_store.py (568 LOC) — TrashStore class; nearly CLI-free; prime migration candidate
_classifier.py (337 LOC) — link-type classification; blocked by unpackaged links tool dependency
_zones.py (350 LOC) — 4-tier protection zones; pure policy
_platform.py (508 LOC) — staging, EXDEV handling, metadata; partially duplicated in filekit v0.2.4
_timepattern.py (390 LOC) — time grammar; pure logic, zero deps
_volumes.py (449 LOC) — per-volume trash routing; Windows ctypes + unctools
_recover.py (572 LOC) — subcommand implementations; CLI-heavy
Filekit current state (v0.2.4, C:\code\filetoolkit\github\dazzle_filekit\):
operations.py, metadata.py, paths.py, platform/windows.py, utils/, verification.py
- No trash-related code
- Phase 8 migration (commit
d5a56b3, 2026-04-11) already moved primitives FROM safedel INTO filekit: atomic_write_json, copy_tree_preserving_links, metadata module
- Explicit scope boundary from
docs/preservelib-integration.md: "filekit = stateless primitives, no manifest concepts, no workflow orchestration, no tool-specific policy"
Historical context:
- Safedel was born in
dazzlecmd/projects/core/ on 2026-04-08. It was never considered for filekit's codebase.
- Phase 8 completed the intended primitive-level migration (things that belonged in filekit moved there)
- What remains in safedel is intentionally workflow and policy — TrashStore lifecycle, zone enforcement, time-pattern grammar, volume registry
- The
2026-04-10__21-28-00__claude-plan__filekit-v0.2.4-additive-consolidation-restart.md plan explicitly listed "Moving volume serial / SUBST detection (stays in safedel)" under out-of-scope
If we DID do this migration — phased plan (for future reference)
Phase 1 (~150 LOC, cleanup only, no new filekit code): Remove duplicated ADS detection and detect_platform() from safedel's _platform.py; safedel imports from dazzle_filekit.platform.windows and dazzle_filekit.utils.compat directly. Zero-risk cleanup.
Phase 2 (~1,200 LOC into filekit v0.3.x): Move pure-logic modules with no CLI coupling and no links tool dependency:
_timepattern.py → dazzle_filekit/trash/timepattern.py
_zones.py → dazzle_filekit/trash/zones.py (config path becomes configurable, not hardcoded ~/.safedel/)
- Dataclasses from
_store.py → dazzle_filekit/trash/types.py
Phase 3 (~1,000 LOC into filekit): Staging, TrashStore, volumes, pure recovery logic. Requires links dep resolution (Phase 4 blocker) OR design classifier to accept pre-classified input.
Phase 4 (~340 LOC): Classifier migration; dz safedel tool becomes ~400 LOC CLI wrapper.
Total: ~2,500 LOC migrate from safedel to filekit, with ~2,500 LOC of tests portable. dz safedel the tool shrinks from 3,500 to ~600 LOC.
Architectural tension (the reason for deferral)
From the oracle-agent investigation:
"Upstreaming trash-store logic into filekit would create primitives with no consumer other than safedel itself. No other tool in the ecosystem (claude-session-logger, fixpath, links, github-traffic-tracker, preservelib) has trash-store needs."
The layering contract at docs/preservelib-integration.md would need to be explicitly relaxed ("filekit may host workflow orchestration when justified by multiple consumers"), OR we accept that this migration makes filekit broader-scope on the promise of future consumers.
Blocker for Phase 4 (if migration ever starts)
_classifier.py imports detect_link from projects/core/links/links.py — a sibling dazzlecmd tool that is not a pip-installable package. This blocks classifier migration to filekit cleanly. Options when the time comes:
- Package
links as standalone dazzle-links library; filekit adds optional dep
- Port link-detection logic into filekit itself (duplicates
links.py functionality)
- Design classifier to accept pluggable link-detector protocol
Sources
- Senior-engineer audit:
2026-04-16__14-40-26__both_destructive-ops-should-use-filekit-and-safedel.md (dazzlecmd private/claude) — Appendix A
- Oracle design history: same doc — Appendix B
- Phase 8 completion: commit
d5a56b3 in dazzle-filekit repo
- Safedel original design:
safedelete-tool-design.md in projects/core/safedel/private/claude/
- Filekit layering contract:
docs/preservelib-integration.md
Related
The alternative we chose instead
On 2026-04-16, after this investigation, we chose a different path for dazzlecmd's trash needs: direct Python import of safedel as a library, via a dedicated api.py module in projects/core/safedel/. Rationale:
- safedel IS part of dazzlecmd core (always present wherever dazzlecmd runs)
- Python imports are in-process and fast — no subprocess overhead
- safedel's
TrashStore and staging helpers are already library-quality code with structured return values
- A dedicated
api.py gives mode.py a stable import surface decoupled from safedel's private internals
- Zero scope expansion for filekit; zero new library built with one consumer
That work is tracked in a companion issue on dazzlecmd: "Expose safedel public API (api.py) and adopt from mode.py". This filekit RFC becomes moot for dazzlecmd's needs.
Next steps
None planned. This issue stays open as an informational placeholder documenting the investigation. Close with wontfix if we conclude the migration will never happen; re-prioritize if a real second consumer emerges who needs in-process trash primitives AND can't use safedel directly.
RFC: Absorb safedel trash-store primitives into filekit (no current consumer — deferred)
Status: deferred / informational
This issue captures investigation findings for a possible future migration. No work is planned. Filed because:
The question
Should filekit absorb
dz safedel's trash-store primitives (staging, recovery, listing, link classification, protection zones, time-pattern matching, volume routing) so any filekit consumer can do recoverable deletes in-process without a subprocess call?TL;DR answer: not now
Reason: no consumer besides safedel itself currently needs this. The only caller who would benefit (dazzlecmd Phase 3.5.A mode switch) can simply subprocess to
dz safedeldirectly, because safedel is part of dazzlecmd core and is guaranteed available wherever dazzlecmd runs.Filekit's existing layering contract ("primitives layer — stateless, no policy, no workflow orchestration", per
docs/preservelib-integration.md) explicitly puts trash-store logic in the higher workflow layer. Moving it into filekit would be a deliberate scope expansion with no justifying consumer.What the investigation found
Safedel code surface (3,507 LOC across 8 modules in
dazzlecmd/projects/core/safedel/):_store.py(568 LOC) —TrashStoreclass; nearly CLI-free; prime migration candidate_classifier.py(337 LOC) — link-type classification; blocked by unpackagedlinkstool dependency_zones.py(350 LOC) — 4-tier protection zones; pure policy_platform.py(508 LOC) — staging, EXDEV handling, metadata; partially duplicated in filekit v0.2.4_timepattern.py(390 LOC) — time grammar; pure logic, zero deps_volumes.py(449 LOC) — per-volume trash routing; Windows ctypes + unctools_recover.py(572 LOC) — subcommand implementations; CLI-heavyFilekit current state (v0.2.4,
C:\code\filetoolkit\github\dazzle_filekit\):operations.py,metadata.py,paths.py,platform/windows.py,utils/,verification.pyd5a56b3, 2026-04-11) already moved primitives FROM safedel INTO filekit:atomic_write_json,copy_tree_preserving_links,metadatamoduledocs/preservelib-integration.md: "filekit = stateless primitives, no manifest concepts, no workflow orchestration, no tool-specific policy"Historical context:
dazzlecmd/projects/core/on 2026-04-08. It was never considered for filekit's codebase.2026-04-10__21-28-00__claude-plan__filekit-v0.2.4-additive-consolidation-restart.mdplan explicitly listed "Moving volume serial / SUBST detection (stays in safedel)" under out-of-scopeIf we DID do this migration — phased plan (for future reference)
Phase 1 (~150 LOC, cleanup only, no new filekit code): Remove duplicated ADS detection and
detect_platform()from safedel's_platform.py; safedel imports fromdazzle_filekit.platform.windowsanddazzle_filekit.utils.compatdirectly. Zero-risk cleanup.Phase 2 (~1,200 LOC into filekit v0.3.x): Move pure-logic modules with no CLI coupling and no
linkstool dependency:_timepattern.py→dazzle_filekit/trash/timepattern.py_zones.py→dazzle_filekit/trash/zones.py(config path becomes configurable, not hardcoded~/.safedel/)_store.py→dazzle_filekit/trash/types.pyPhase 3 (~1,000 LOC into filekit): Staging, TrashStore, volumes, pure recovery logic. Requires
linksdep resolution (Phase 4 blocker) OR design classifier to accept pre-classified input.Phase 4 (~340 LOC): Classifier migration;
dz safedeltool becomes ~400 LOC CLI wrapper.Total: ~2,500 LOC migrate from safedel to filekit, with ~2,500 LOC of tests portable.
dz safedelthe tool shrinks from 3,500 to ~600 LOC.Architectural tension (the reason for deferral)
From the oracle-agent investigation:
The layering contract at
docs/preservelib-integration.mdwould need to be explicitly relaxed ("filekit may host workflow orchestration when justified by multiple consumers"), OR we accept that this migration makes filekit broader-scope on the promise of future consumers.Blocker for Phase 4 (if migration ever starts)
_classifier.pyimportsdetect_linkfromprojects/core/links/links.py— a sibling dazzlecmd tool that is not a pip-installable package. This blocks classifier migration to filekit cleanly. Options when the time comes:linksas standalonedazzle-linkslibrary; filekit adds optional deplinks.pyfunctionality)Sources
2026-04-16__14-40-26__both_destructive-ops-should-use-filekit-and-safedel.md(dazzlecmd private/claude) — Appendix Ad5a56b3in dazzle-filekit reposafedelete-tool-design.mdinprojects/core/safedel/private/claude/docs/preservelib-integration.mdRelated
dz safedelsubprocess" — the pragmatic alternative that doesn't require this migrationdata.copy/move/sync/verify,DataRefAPI, optional-peer integrationThe alternative we chose instead
On 2026-04-16, after this investigation, we chose a different path for dazzlecmd's trash needs: direct Python import of safedel as a library, via a dedicated
api.pymodule inprojects/core/safedel/. Rationale:TrashStoreand staging helpers are already library-quality code with structured return valuesapi.pygives mode.py a stable import surface decoupled from safedel's private internalsThat work is tracked in a companion issue on dazzlecmd: "Expose safedel public API (
api.py) and adopt from mode.py". This filekit RFC becomes moot for dazzlecmd's needs.Next steps
None planned. This issue stays open as an informational placeholder documenting the investigation. Close with
wontfixif we conclude the migration will never happen; re-prioritize if a real second consumer emerges who needs in-process trash primitives AND can't use safedel directly.