Decide deletion confirm-gates and temporary-tier durations #43

Closed
opened 2026-07-14 16:52:05 +00:00 by jared · 1 comment
Owner

Map: #31

Question

Define autonomy tiers for clean's delete ops (what deletes silently vs needs confirmation), the confirm gate for untracked files, and default durations/triggers for the temporary tier (age thresholds, purpose-served signals like openspec archive).

Map: #31 ## Question Define autonomy tiers for clean's delete ops (what deletes silently vs needs confirmation), the confirm gate for untracked files, and default durations/triggers for the temporary tier (age thresholds, purpose-served signals like openspec archive).
jared added the
wayfinder:grilling
label 2026-07-14 16:52:05 +00:00
jared self-assigned this 2026-07-14 17:39:31 +00:00
Author
Owner

RESOLUTION: Deletion autonomy tiers, gates, and temporary-tier retention locked.

1. Rule-backed deletes are auto (no per-run prompt). A rule confirmed per #39 is the standing consent; re-confirming every run would reduce the rulebook (#40) to an advisory list and strip :calibrate (#42) of its payoff. Escape hatch: an optional per-rule confirm: truehuman-settable only; a model-proposed rule may not set it (it can only ask a human to), so the flag can't quietly drift everything back to always-confirm.

2. graphify-out/** and .dochygiene/** are NOT delete targets — they are an IGNORE surface. The #41 inventory proposed both as temporary; that proposal is rejected. graphify-out is a tool's working store, deliberately gitignored, expensive and slow to rebuild — hygiene must never touch or even walk it. .dochygiene/ reports are self-superseding (the next check overwrites them). Corollary, and a constraint handed to #44: gitignored ≠ deletable (graphify-out) and gitignored ≠ keepable (autoresearch raw rounds) — the ignore surface is its own explicit list, not an inference from .gitignore. Directory-rule walk-pruning (#40) implements this for free.

3. #35's tracked/untracked gate stands as written — no recoverable_via field, no "regenerate" class. Once graphify-out leaves the delete set, the deletable population (openspec changes, old plans, autoresearch raw rounds post-extraction, scratch notes) maps cleanly onto tracked/untracked. Resulting matrix:

IGNORE (never walked)                 : graphify-out/**, .dochygiene/**
lifetime "keep"                       : scanned + reported, never deleted
tracked + delete rule + clean         : auto      (openspec/changes/, docs/plans/)
tracked + delete rule + DIRTY         : CONFIRM   (uncommitted diff is not in history)
untracked + delete rule               : CONFIRM   (autoresearch rounds, scratch notes)
no rule match                         : unmanaged; existing signals only, never deleted

The dirty-file case is new: "tracked" alone does not mean recoverable — an uncommitted diff dies with the file. clean verifies tracked+clean at runtime (git ls-files + dirty check); it does not trust the rule's word for it.

4. temporary tier = retain-recent-N + age, not age alone. Global defaults retain_recent: 3, max_age_days: 3 (both per-rule overridable). The newest 3 entries matching a rule are always kept regardless of age — they show current trajectory, i.e. what was most recently worked on; an entry ranked 4th or older is deleted once it exceeds max_age_days. 90-day-style windows were rejected as far too slow in the age of AI. Retention unit = the rule's match entry: a file for file rules, a run directory for directory rules (so: "3 most recent autoresearch runs", not "3 most recent files inside one run"). temporary therefore survives as a real tier — #33's three lifetimes stand.

5. Age is measured from git commit time, falling back to filesystem mtime for untracked files. mtime alone was rejected: a clone or branch switch resets every mtime to "now", silently putting the whole rulebook to sleep on a fresh checkout.

6. delete-once-served splits by evidence quality — two slots.

{ "glob": "openspec/changes/*/", "lifetime": "delete-once-served",
  "served_when_path": "openspec/changes/archive/{id}/" }         // scanner PROVES it -> auto
{ "glob": "docs/plans/*.md", "lifetime": "delete-once-served",
  "served_when": "the effort this plan describes has shipped" }  // classifier JUDGES it -> forced CONFIRM

A deterministically-proven served signal (served_when_path, checked by the scanner) may delete silently under the tier matrix above. A classifier-judged signal (served_when, free text per #40) is always downgraded to confirm, regardless of tracked status. The LLM may propose; it may never silently destroy on a hunch. This puts the auto/confirm line exactly where evidence quality changes.

7. New design principle — hygiene drives projects toward structurally-obvious maintenance. When a rule's served signal is subjective (classifier-judged), the tool does not merely downgrade it to confirm: it names the subjectivity and recommends a concrete structural convention — an archive/ bucket, a status: shipped frontmatter key — that would let the rule graduate to served_when_path and become silent. Confirm-fatigue is thereby converted into the incentive to fix the convention. Mechanics (where the nudge surfaces, whether it can propose the rule edit) are handed to :calibrate (#42).

Amendments to #40's locked schema (additive; fold into #46's ADR set alongside the already-flagged repo-root-dotfile ADR):

  • retain_recent: 3 (new; default 3)
  • max_age_days default is 3, not null-means-forever
  • served_when_path (new; deterministic sibling to served_when)
  • confirm: true (new; optional, human-settable only)
  • an IGNORE posture distinct from lifetime: "keep" (never walked vs. walked-but-never-deleted)

ADR-worthy for #46: (a) the auto/confirm line drawn on evidence quality + recoverability rather than on file type; (b) retain-recent-3 / 3-day retention as the cc-os default; (c) the determinism-promotion principle in item 7. /os-adr:find confirms no existing ADR governs clean's safety tiers (only ADR-0032, on index/progressive-disclosure, touches this plugin), so none of this reverses a recorded decision.

RESOLUTION: Deletion autonomy tiers, gates, and temporary-tier retention locked. **1. Rule-backed deletes are `auto` (no per-run prompt).** A rule confirmed per #39 *is* the standing consent; re-confirming every run would reduce the rulebook (#40) to an advisory list and strip `:calibrate` (#42) of its payoff. Escape hatch: an optional per-rule `confirm: true` — **human-settable only**; a model-proposed rule may not set it (it can only ask a human to), so the flag can't quietly drift everything back to always-confirm. **2. `graphify-out/**` and `.dochygiene/**` are NOT delete targets — they are an IGNORE surface.** The #41 inventory proposed both as `temporary`; that proposal is **rejected**. graphify-out is a tool's working store, deliberately gitignored, expensive and slow to rebuild — hygiene must never touch or even walk it. `.dochygiene/` reports are self-superseding (the next `check` overwrites them). Corollary, and a constraint handed to #44: **gitignored ≠ deletable** (graphify-out) and **gitignored ≠ keepable** (autoresearch raw rounds) — the ignore surface is its own explicit list, not an inference from `.gitignore`. Directory-rule walk-pruning (#40) implements this for free. **3. #35's tracked/untracked gate stands as written** — no `recoverable_via` field, no "regenerate" class. Once graphify-out leaves the delete set, the deletable population (openspec changes, old plans, autoresearch raw rounds post-extraction, scratch notes) maps cleanly onto tracked/untracked. Resulting matrix: ``` IGNORE (never walked) : graphify-out/**, .dochygiene/** lifetime "keep" : scanned + reported, never deleted tracked + delete rule + clean : auto (openspec/changes/, docs/plans/) tracked + delete rule + DIRTY : CONFIRM (uncommitted diff is not in history) untracked + delete rule : CONFIRM (autoresearch rounds, scratch notes) no rule match : unmanaged; existing signals only, never deleted ``` The dirty-file case is new: "tracked" alone does not mean recoverable — an uncommitted diff dies with the file. `clean` verifies tracked+clean at runtime (`git ls-files` + dirty check); it does not trust the rule's word for it. **4. `temporary` tier = retain-recent-N + age, not age alone.** Global defaults **`retain_recent: 3`, `max_age_days: 3`** (both per-rule overridable). The newest 3 entries matching a rule are always kept regardless of age — they show current trajectory, i.e. what was most recently worked on; an entry ranked 4th or older is deleted once it exceeds `max_age_days`. 90-day-style windows were rejected as far too slow in the age of AI. Retention unit = the rule's match **entry**: a file for file rules, a run *directory* for directory rules (so: "3 most recent autoresearch runs", not "3 most recent files inside one run"). `temporary` therefore survives as a real tier — #33's three lifetimes stand. **5. Age is measured from git commit time**, falling back to filesystem mtime for untracked files. mtime alone was rejected: a clone or branch switch resets every mtime to "now", silently putting the whole rulebook to sleep on a fresh checkout. **6. `delete-once-served` splits by evidence quality — two slots.** ```json { "glob": "openspec/changes/*/", "lifetime": "delete-once-served", "served_when_path": "openspec/changes/archive/{id}/" } // scanner PROVES it -> auto { "glob": "docs/plans/*.md", "lifetime": "delete-once-served", "served_when": "the effort this plan describes has shipped" } // classifier JUDGES it -> forced CONFIRM ``` A deterministically-proven served signal (`served_when_path`, checked by the scanner) may delete silently under the tier matrix above. A classifier-judged signal (`served_when`, free text per #40) is **always downgraded to `confirm`**, regardless of tracked status. The LLM may propose; it may never silently destroy on a hunch. This puts the auto/confirm line exactly where evidence quality changes. **7. New design principle — hygiene drives projects toward structurally-obvious maintenance.** When a rule's served signal is subjective (classifier-judged), the tool does not merely downgrade it to `confirm`: it **names the subjectivity and recommends a concrete structural convention** — an `archive/` bucket, a `status: shipped` frontmatter key — that would let the rule graduate to `served_when_path` and become silent. Confirm-fatigue is thereby converted into the incentive to fix the convention. Mechanics (where the nudge surfaces, whether it can propose the rule edit) are handed to `:calibrate` (#42). **Amendments to #40's locked schema** (additive; fold into #46's ADR set alongside the already-flagged repo-root-dotfile ADR): - `retain_recent: 3` (new; default 3) - `max_age_days` default is **3**, not null-means-forever - `served_when_path` (new; deterministic sibling to `served_when`) - `confirm: true` (new; optional, human-settable only) - an IGNORE posture distinct from `lifetime: "keep"` (never walked vs. walked-but-never-deleted) **ADR-worthy for #46:** (a) the auto/confirm line drawn on evidence quality + recoverability rather than on file type; (b) retain-recent-3 / 3-day retention as the cc-os default; (c) the determinism-promotion principle in item 7. `/os-adr:find` confirms **no existing ADR governs `clean`'s safety tiers** (only ADR-0032, on index/progressive-disclosure, touches this plugin), so none of this reverses a recorded decision.
jared closed this issue 2026-07-14 18:12:20 +00:00
Sign in to join this conversation.
No description provided.