crabidy/quality/orphans.md

5.8 KiB

Quality gates — the orphans provider

Gates for architecture/orphans.md and the crabidy-server/src/orphans.rs stub. Each is pass/fail by reading/reasoning or by a named test. Tests live in crabidy-server/src/crabidy_store.rs (the store methods) and crabidy-server/src/orphans.rs (the provider), run via devenv shell -- cargo test -p crabidy-server.

Correctness of the orphan diff

  • G1 — All items enumerated. list_orphans counts every store entry that has both a <name>.cbd-store.toml sidecar and a readable <name> audio file. Test: capture two distinct tracks (two store entries), reference neither → both listed.
  • G2 — Referenced entries excluded. An entry referenced by a Playable::Store toml under any ref_root is not listed. Test: capture one track into a /crabidy save (which writes a store toml), then list_orphans with that tree as a ref root → the entry is not an orphan; delete the save's toml → it becomes an orphan.
  • G3 — /fs references count. A Playable::Store toml under the /fs root (as scan --capture writes) excludes its target. Test: hand-write a store toml under an fs ref root → its target is not an orphan.
  • G4 — References outside ref roots do not count. A store toml under a directory that is not a ref root does not rescue its target from being an orphan (documented boundary). Test asserts the target is still listed.
  • G5 — Malformed residue is reclaimable. A sidecar with no audio (or audio with no sidecar) is reported as an orphan, never as "referenced". Test: drop a lone x.cbd-store.toml into the store → listed.
  • G6 — Fresh every call. No persisted orphan list; two consecutive list_orphans calls straddling a reference change reflect the change (covered by G2's two-phase assertion).

Presentation contract (reuse, no new wire)

  • G7 — Child capability flags. Every /orphans child advertises is_queable, is_editable, is_deletable, and is_captured all true, and is_downloadable = false. The root is is_queable = true, is_creatable = false. Verified by reading get_lib_node; test asserts the flags on a listed child.
  • G8 — Leaf carries exactly one track. get_lib_node("/orphans/<seg>") returns a childless, queueable node with one track whose is_captured is true and whose metadata matches the sidecar. Test asserts tracks.len() == 1 and children.is_empty().
  • G9 — Queueing resolves the audio. Resolving an orphan node (default walk) yields one track whose get_urls_for_track returns store_root/<name>. Test drives resolve_tracks_into("/orphans") and checks the resolved URL is the store path.
  • G10 — No proto/TUI/web change. Confirm by inspection that the feature adds no field to any proto message and no binding/gesture to cbd-tui or cbd-web: it relies solely on existing is_editable/is_deletable/ is_queable handling. (If this gate cannot hold, the design in architecture/orphans.md must be revisited before implementing.)

Rename safety

  • G11 — Renames both files. rename_orphan(old, new) renames the audio <old><new> and <old>.cbd-store.toml<new>.cbd-store.toml; neither old name remains on disk. Test.
  • G12 — Rename updates the index. After a rename, the index resolves the same content hash / provider ids to the new name and no longer to the old (so a subsequent capture de-dups against the renamed entry). Test via StoreIndex lookups or a follow-up capture.
  • G13 — Rename validates and refuses collisions. An empty / separator / leading-dot new is rejected (InvalidInput); a new already taken by another store audio or sidecar is refused without touching disk. Tests for both.
  • G14 — Rename is reference-safe by construction. Only unreferenced entries are exposed under /orphans, so a rename never invalidates a live toml. Verified by reasoning against the diff definition; no test needed beyond G2.

Delete safety

  • G15 — Deletes both files. delete_orphan(name) removes the audio and the sidecar and drops the entry from the index. Test asserts both files gone and list_orphans no longer lists it.
  • G16 — Idempotent. Deleting an already-gone entry returns Ok (a refreshed root), not an error. Test.
  • G17 — Delete stays inside the store root. The name is a validated bare file name; delete joins it onto store_root and never follows separators or ... Verified by reasoning (mirrors Playable::Store bare-name validation) plus a test that a crafted /orphans/..%2Fx path is MalformedPath, not a file operation.

Always-on rules (AGENTS.md)

  • G18 — No panics on input or I/O. Every error path (missing entry, unreadable dir, bad toml, taken rename target, malformed path) is a typed ProviderError/StoreError; no unwrap/expect/panic!/todo! remains in shipped code. A bad sidecar or track toml during the walk is skipped with a warning, never fatal (mirrors StoreIndex::scan and fsdy listing).
  • G19 — Bounded work, no unbounded channels. The reference walk and enumeration use ordinary async fs iteration; if any channel is introduced it is bounded. No new external calls (so no timeout/retry surface is added).
  • G20 — Public items documented. Every public item in orphans.rs and every new public CrabidyStore/fsdy::Client method has a doc comment stating intent and error behavior.
  • G21 — Mount is optional and non-fatal. /orphans mounts only when the store is present; its absence (no data/state dir) drops the subtree without affecting the server, exactly like /crabidy and /fs. get_lib_root includes the orphans child only when mounted. Verified by reading the orchestrator wiring.