Skip to content

disk-hygiene: persist a catalog of investigated entries — record shape, scope, investigation procedure, and operator answers #4008

Description

@kyle-sexton

Drafted by an AI agent from an operator-confirmed interview (run 20260908-home-d1).

Parent

Parent: #4004. Refs #3858, which implements ranking signals computable from existing snapshot data. That issue makes the component notice more; this one makes it remember. They are complementary and neither substitutes for the other: signals rank what to look at, the catalog records what looking at it concluded.

Covers four confirmed decisions together — record shape, catalog scope, investigation procedure, and what happens when the owner is still unknown — because they are one artifact and one pass.

Problem

The component investigates and then forgets.

On this run, deciding that .sbx-denybin (104 bytes, four stub ssh/scp batch files) was removable took a nine-source investigation: User PATH, Machine PATH, the claude-remote-control scheduled-task action, .claude.json, .codex\config.toml, .claude, the chezmoi source, and the sandbox checkout. That reasoning exists only in a report in the operator's data directory. The next run re-derives it from nothing, or does not.

Worse are the entries kept because the investigation ran out of road, with no way to record that it did:

  • .cache\verifier-scratch — 173 KB, contains a .git, newest file 2026-09-02, no config anywhere references it. Owner unknown, so kept.
  • .cache\performance-harness — 87 bytes, same shape. Owner unknown, so kept.
  • ansel — empty, created 2026-09-01, zero provenance anywhere on the machine. Name-only, so kept.

Each of those will be re-investigated, reach the same dead end, and be kept again, every run, forever. The operator knows the answer for at least one of them and was never asked in a form the component could retain.

What to build

One artifact under the plugin data root: catalog.json plus a rendered CATALOG.md.

Record shape

One record per catalogued entry:

  • path
  • identity — device, inode, kind
  • owner
  • provenance — a narrative of 2-4 sentences
  • evidence — a list, each item carrying its source: a file path, a command, or a URL
  • disposition
  • tier
  • size
  • first_seen_run, last_seen_run, last_verified
  • source — engine or human

How a record is used

  • The scan annotates a matching entry with prior_disposition.
  • The report leads with new or changed entries; unchanged ones get one line each. This is the point of the artifact: a re-run of a catalogued home directory should be short.
  • Identity or descendant-set change invalidates a record. A moved inode or a changed child set means the thing being described is not the thing that was described.
  • A record is a hint, never authorization. The same rule the component already applies to filename hints. A catalogued remove disposition does not shorten the approval ceremony, does not skip a preview, and does not survive a failed revalidation.

Catalog scope

Catalogue:

  • every immediate child of the target,
  • every hinted or empty entry at any depth,
  • every entry flagged out of place — no protected reason, no recognizable app or config convention.

Deeper entries catalogue at owner level: one record per owning tool or product, not one per file.

The operator's constraint, verbatim: "Nothing that looks out of place is skipped." An out-of-place entry is investigated, catalogued, and inventoried with enough context to justify its disposition, whatever that disposition turns out to be. "Small", "empty", and "probably fine" are not reasons to skip; they are findings to record.

Investigation procedure

The skill ships a required local procedure, run per catalogued entry:

  • manifests and READMEs
  • config file contents
  • Get-Command (or the platform equivalent)
  • running processes
  • scheduled tasks
  • PATH, both user and machine
  • installed programs
  • git remotes and status
  • dotfile and settings references

Escalation to /discovery:research is presence-gated and permitted only when the local procedure finds no owner. /discovery:explore applies only to repository strays.

Unknowns

The report ends with one question per entry whose owner is still unknown after the local procedure and any escalation.

The operator's answer is written into the catalog as human-sourced provenance (source: human) and is not asked again while identity holds. Until answered, the entry's disposition stays keep.

Design constraints

  • The catalog records conclusions and their evidence; it never records an approval.
  • An entry with source: human provenance is not re-investigated by the local procedure while its identity is unchanged, but it is still re-verified for identity.
  • Unknown must remain visibly unknown. An entry no investigation resolved reads as unresolved, not as low-priority — the failure mode this whole artifact exists to prevent is a dead end quietly aging into an assumption.
  • The rendered CATALOG.md is for the operator to read without running anything.

Acceptance criteria

  • catalog.json and a rendered CATALOG.md are written under the plugin data root, with the record shape above.
  • A scan annotates matching entries with prior_disposition, and the report leads with new or changed entries while giving unchanged ones one line each.
  • A change to an entry's identity or descendant set invalidates its record, demonstrated by a test.
  • A catalogued disposition never shortens or skips any approval, preview, or revalidation step, demonstrated by a test.
  • Catalog scope covers every immediate child, every hinted or empty entry at any depth, and every out-of-place entry; deeper entries are catalogued at owner level.
  • The local investigation procedure ships in the skill, is required per catalogued entry, and its sources are enumerated.
  • /discovery:research is invoked only when the local procedure finds no owner, and only when present.
  • The report ends with one question per still-unknown-owner entry; an operator answer is persisted with source: human and is not re-asked while identity holds.
  • An entry with an unanswered question keeps disposition keep.
  • scripts/affected-tests.sh --run selects and passes the suites mapped to the changed files.

Out of scope

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: mediumReal value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions