{ "pack_id": "file-triage-at-scale", "title": "File triage at scale", "version": "0.1.0", "summary": "A collection of nouns and verbs for organizing very large collections of files without losing any: a read-only inventory with allocated and apparent sizes, a placement policy, duplicates proven by full content hash rather than name, size or date, secret-bearing paths listed and never opened, a move plan a person approves before anything moves, moves journaled before they happen and verified after, sources retired only by the person, and indexes brought back in line.", "dependencies": [ "findings-to-fixes", "status-and-handoff" ], "boundaries": [ "Scans read metadata only and never open secret-bearing paths.", "Nothing is moved or deleted without a person's approval record; workers never remove a source.", "Moves stay within the local and attached roots the scope names.", "Workers create nouns and propose verbs; an authorized requester enqueues the proposals worth doing.", "Importing the pack writes nothing and enqueues nothing." ], "nouns": [ { "slug": "pack", "category": "noun.skill_pack", "role": "pack", "title": "A collection of nouns and verbs for organizing very large collections of files without losing any", "value": { "lets_workers": "Turn terabytes of scattered files into an inventory, a placement register and a move plan a person approves, without losing anything: scan read-only with secrets excluded, prove duplicates by content hash, plan moves that never overwrite, journal each move before it happens and verify it after, leave every source in place until a person retires it, and bring the indexes back in line.", "flow": [ { "verb": { "$verb": "file-triage-at-scale.scope-scan" }, "when": "Before anything is read." }, { "verb": { "$verb": "file-triage-at-scale.inventory-files" }, "when": "Once per scope; read-only and deterministic." }, { "verb": { "$verb": "file-triage-at-scale.classify-items" }, "when": "After the inventory." }, { "verb": { "$verb": "file-triage-at-scale.find-duplicates" }, "when": "After the inventory; deterministic." }, { "verb": { "$verb": "file-triage-at-scale.plan-moves" }, "when": "After classification and duplicates." }, { "verb": { "$verb": "file-triage-at-scale.approve-plan" }, "when": "Before any move; a person's verb." }, { "verb": { "$verb": "file-triage-at-scale.move-and-verify" }, "when": "Only for approved moves; deterministic." }, { "verb": { "$verb": "file-triage-at-scale.retire-sources" }, "when": "After moves verify; a person's verb." }, { "verb": { "$verb": "file-triage-at-scale.reconcile-indexes" }, "when": "After moves and retirements." } ], "guardrails": [ "Nothing is moved, deleted or edited before a person approves the plan.", "A worker never removes a source; retiring sources is the person's verb.", "Secret-bearing paths are listed by name and never opened.", "Equal content does not prove a path is unused, and dates prove nothing about disuse." ], "worked_examples": [ { "$pin": "triage-checks" }, { "$pin": "example.inventory" }, { "$pin": "example.duplicate-set" } ] }, "relations": [ [ "requires_noun", "method" ], [ "requires_noun", "placement-policy" ], [ "requires_noun", "duplicate-rules" ], [ "requires_noun", "filesystem-fit" ], [ "requires_noun", "secret-exclusions" ] ] }, { "slug": "method", "category": "noun.skill_pack", "role": "method", "title": "How a large collection is scanned, classified, planned, moved, verified and reconciled", "category_reason": "A reusable procedure with guardrails, followed by every verb in this pack.", "value": { "lifecycle": [ "scope-scan agrees the roots, exclusions, secret patterns, limits and the person's rules about what stays together.", "inventory-files walks the scope read-only; the audit's only new file is its own record.", "classify-items places each group under the placement policy and turns uncertainty into questions.", "find-duplicates groups by size cheaply, then proves equality by full content hash, inode and links.", "plan-moves drafts every move, every proposed deletion and every open question, with prechecks and a dry run.", "approve-plan is the person's decision on each move, deletion and question.", "move-and-verify journals, copies, verifies by hash and leaves every source in place.", "retire-sources is the person's decision on each verified source.", "reconcile-indexes brings registries and folder notes in line with what moved." ], "rules": [ "State units and say whether sizes are allocated or apparent; hard links and sparse files make apparent size a poor guide to space freed.", "A scan reads; a modification or access time changed by the scan itself is not evidence.", "Reconstructible caches are cleaned by the tool that owns them, not by deleting folders.", "An unclean unmount is checked before any write; repairs are the person's call.", "Workers create nouns and propose verbs; they never enqueue." ], "follow_ups": { "finding": { "$verb": "findings-to-fixes.record-finding" }, "status": { "$verb": "status-and-handoff.write-status" } } } }, { "slug": "placement-policy", "category": "noun.reflex_procedure", "role": "placement_policy", "title": "Where things belong, by what they are and what produced them", "category_reason": "Deterministic placement rules with a fixed vocabulary of roles and statuses.", "value": { "roles": { "project": "Live work and its code.", "shared_asset": "Material several projects reuse.", "data": "Inputs and datasets.", "output": "What a project produced, kept by producer.", "research": "Notes and sources.", "backup": "Copies kept for recovery, by project.", "archive": "Cold material kept for the record.", "catalog": "Indexes and registries.", "cache": "Anything a tool can rebuild.", "unknown": "Not yet placed; becomes a question." }, "statuses": [ "active_canonical", "active_copy", "staging", "reference", "cold_archive", "backup_copy", "superseded", "duplicate_pending_review", "reconstructible_cache", "structure_only", "unknown" ], "rules": [ "A folder's role is set by what the work produces, not by its name.", "A matching name, date or purpose does not prove two folders are copies.", "Groups the person names as belonging together stay together, wherever they sit today.", "Low confidence becomes a pending question with a suggested home, never a silent placement." ] } }, { "slug": "duplicate-rules", "category": "noun.reflex_procedure", "role": "duplicate_rules", "title": "What proves two files are duplicates, and what does not", "category_reason": "Deterministic duplicate evidence levels.", "value": { "evidence_levels": { "name_only": "Same name. Proves nothing.", "name_size_date": "Same name, size and date. Still proves nothing.", "size_and_partial_hash": "A cheap shortlist; never enough to remove anything.", "full_sha256": "Equal full content hash. With distinct inodes, a duplicate." }, "rules": [ "Hard links to one inode are one file, not duplicates; removing a link frees nothing.", "Reclaimable space is the allocated size of the copies removed, once consumers are moved to the kept copy.", "An archive is compared with a loose folder by its listing and hashes, never by name; an archive with fewer entries is not a copy.", "Equal content does not mean a path is unreferenced: list consumers before proposing any removal." ], "model_calls": 0 } }, { "slug": "filesystem-fit", "category": "noun.reflex_procedure", "role": "filesystem_fit", "title": "What a destination's filesystem keeps and loses", "category_reason": "Deterministic destination checks.", "value": { "check": [ "Free space for every move, with a margin.", "Allocation unit size: large units waste space on many tiny files, so pack small-file trees first.", "Permissions, ownership and symbolic links: filesystems that drop them need a permissions archive beside the copy.", "Case sensitivity and the largest file size allowed.", "Live databases, runnable environments and linked work trees need a plan of their own, not a plain copy.", "Mount health: an unclean unmount is checked read-only before any write, and repair needs a person." ] } }, { "slug": "secret-exclusions", "category": "noun.reflex_procedure", "role": "secret_exclusions", "title": "Paths that are listed and never read", "category_reason": "A fixed exclusion list applied before any read.", "value": { "patterns": [ "environment files", "private keys and certificates", "credential stores and keychains", "wallet files", "browser profiles and their cookies", "files whose names mention keys, secrets, tokens or passwords" ], "rules": [ "List matching paths by name, count them and never open them.", "Scanners skip key files, not only environment folders.", "Report where secret-bearing paths sit, especially on drives that keep no permissions; moving them to protected storage is the person's call." ] } }, { "slug": "template.scan-scope", "category": "noun.seed_hypothesis", "role": "template", "title": "Template: what a scan may read: roots and their health, exclusions, secret patterns, limits and the person's grouping rules", "category_reason": "A scope is a proposal of what to read, confirmed before reading.", "value": { "describes": "what a scan may read: roots and their health, exclusions, secret patterns, limits and the person's grouping rules", "schema": { "type": "object", "additionalProperties": false, "required": [ "scope_id", "roots", "read_only", "exclude", "secret_patterns", "limits", "group_rules", "confirmed_by_role", "scoped_at" ], "properties": { "scope_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "roots": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "object", "additionalProperties": false, "required": [ "root_ref", "filesystem", "mount_health" ], "properties": { "root_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "filesystem": { "type": "string", "minLength": 1, "maxLength": 40 }, "mount_health": { "type": "string", "enum": [ "clean", "unclean_unmount", "unknown" ] } } } }, "read_only": { "type": "boolean", "enum": [ true ] }, "exclude": { "type": "array", "minItems": 0, "maxItems": 50, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "secret_patterns": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "minLength": 1, "maxLength": 120 } }, "limits": { "type": "object", "additionalProperties": false, "required": [ "max_depth", "max_files", "max_seconds" ], "properties": { "max_depth": { "type": "integer", "minimum": 1, "maximum": 100 }, "max_files": { "type": "integer", "minimum": 1, "maximum": 10000000000 }, "max_seconds": { "type": "integer", "minimum": 60, "maximum": 604800 } } }, "group_rules": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 300 } }, "confirmed_by_role": { "type": "string", "minLength": 0, "maxLength": 120 }, "scoped_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "read_only is always true.", "Secret patterns are never empty." ] } }, { "slug": "template.inventory", "category": "noun.eval_run", "role": "template", "title": "Template: a read-only inventory: roots with free space, totals in allocated and apparent bytes, groups, errors and what was not covered", "category_reason": "An inventory is a measured, read-only scan.", "value": { "describes": "a read-only inventory: roots with free space, totals in allocated and apparent bytes, groups, errors and what was not covered", "schema": { "type": "object", "additionalProperties": false, "required": [ "inventory_id", "scope_ref", "as_of", "unit", "roots", "totals", "groups", "errors", "secret_paths_listed_not_opened", "files_changed_by_scan", "not_covered" ], "properties": { "inventory_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "scope_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "as_of": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 }, "unit": { "type": "string", "enum": [ "bytes" ] }, "roots": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "object", "additionalProperties": false, "required": [ "root_ref", "filesystem", "free_bytes", "total_bytes" ], "properties": { "root_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "filesystem": { "type": "string", "minLength": 1, "maxLength": 40 }, "free_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "total_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 } } } }, "totals": { "type": "object", "additionalProperties": false, "required": [ "files", "folders", "allocated_bytes", "apparent_bytes" ], "properties": { "files": { "type": "integer", "minimum": 0, "maximum": 1000000000000 }, "folders": { "type": "integer", "minimum": 0, "maximum": 1000000000000 }, "allocated_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "apparent_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 } } }, "groups": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": [ "path_ref", "files", "allocated_bytes", "newest_change" ], "properties": { "path_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "files": { "type": "integer", "minimum": 0, "maximum": 1000000000000 }, "allocated_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "newest_change": { "type": [ "string", "null" ], "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}", "maxLength": 40 } } } }, "errors": { "type": "array", "minItems": 0, "maxItems": 1000, "items": { "type": "object", "additionalProperties": false, "required": [ "path_ref", "kind" ], "properties": { "path_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "kind": { "type": "string", "enum": [ "permission_denied", "unreadable", "changed_during_scan", "timed_out" ] } } } }, "secret_paths_listed_not_opened": { "type": "integer", "minimum": 0, "maximum": 1000000 }, "files_changed_by_scan": { "type": "integer", "enum": [ 0 ] }, "not_covered": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 300 } } } }, "rules": [ "files_changed_by_scan is always 0." ] } }, { "slug": "template.placement-register", "category": "noun.seed_hypothesis", "role": "template", "title": "Template: where each group belongs under the placement policy, with evidence, confidence and any question for a person", "category_reason": "A register is a set of placement hypotheses.", "value": { "describes": "where each group belongs under the placement policy, with evidence, confidence and any question for a person", "schema": { "type": "object", "additionalProperties": false, "required": [ "register_id", "inventory_ref", "items", "classified_at" ], "properties": { "register_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "inventory_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "items": { "type": "array", "minItems": 1, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": [ "path_ref", "role", "status", "evidence", "confidence", "pending_question" ], "properties": { "path_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "role": { "type": "string", "enum": [ "project", "shared_asset", "data", "output", "research", "backup", "archive", "catalog", "cache", "unknown" ] }, "status": { "type": "string", "enum": [ "active_canonical", "active_copy", "staging", "reference", "cold_archive", "backup_copy", "superseded", "duplicate_pending_review", "reconstructible_cache", "structure_only", "unknown" ] }, "evidence": { "type": "string", "minLength": 1, "maxLength": 300 }, "confidence": { "type": "string", "enum": [ "high", "medium", "low" ] }, "pending_question": { "type": "string", "minLength": 0, "maxLength": 300 } } } }, "classified_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "Low confidence carries a pending question." ] } }, { "slug": "template.duplicate-set", "category": "noun.eval_run", "role": "template", "title": "Template: one set of candidate duplicates: members with sizes, inodes, links and hashes, the evidence level, consumers and reclaimable bytes", "category_reason": "A duplicate set is a deterministic comparison run.", "value": { "describes": "one set of candidate duplicates: members with sizes, inodes, links and hashes, the evidence level, consumers and reclaimable bytes", "schema": { "type": "object", "additionalProperties": false, "required": [ "set_id", "members", "evidence_level", "distinct_inodes", "consumers", "references_checked", "reclaimable_bytes", "note", "found_at" ], "properties": { "set_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "members": { "type": "array", "minItems": 2, "maxItems": 50, "items": { "type": "object", "additionalProperties": false, "required": [ "path_ref", "size_bytes", "allocated_bytes", "inode", "hardlinks", "sha256" ], "properties": { "path_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "size_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "allocated_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "inode": { "type": "string", "minLength": 1, "maxLength": 40 }, "hardlinks": { "type": "integer", "minimum": 1, "maximum": 10000 }, "sha256": { "type": [ "string", "null" ], "pattern": "^[0-9a-f]{64}$" } } } }, "evidence_level": { "type": "string", "enum": [ "name_only", "name_size_date", "size_and_partial_hash", "full_sha256" ] }, "distinct_inodes": { "type": "boolean" }, "consumers": { "type": "array", "minItems": 0, "maxItems": 50, "items": { "type": "object", "additionalProperties": false, "required": [ "path_ref", "uses" ], "properties": { "path_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "uses": { "type": "string", "minLength": 1, "maxLength": 200 } } } }, "references_checked": { "type": "string", "enum": [ "checked", "not_checked" ] }, "reclaimable_bytes": { "type": [ "integer", "null" ], "minimum": 0, "maximum": 10000000000000000 }, "note": { "type": "string", "minLength": 1, "maxLength": 400 }, "found_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "Only full_sha256 with distinct inodes counts as a duplicate." ] } }, { "slug": "template.move-plan", "category": "noun.seed_hypothesis", "role": "template", "title": "Template: a move plan for a person to approve: moves, proposed deletions, open questions, prechecks, a dry run and how moves are executed", "category_reason": "A plan is a proposal that a person approves move by move.", "value": { "describes": "a move plan for a person to approve: moves, proposed deletions, open questions, prechecks, a dry run and how moves are executed", "schema": { "type": "object", "additionalProperties": false, "required": [ "plan_id", "scope", "moves", "deletions", "pending_decisions", "pre_checks", "overwrite_policy", "dry_run_first", "execution", "approval", "planned_at" ], "properties": { "plan_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "scope": { "type": "object", "additionalProperties": false, "required": [ "scope_ref", "secret_patterns" ], "properties": { "scope_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "secret_patterns": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "minLength": 1, "maxLength": 120 } } } }, "moves": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "move_id", "source_ref", "destination_ref", "reason", "bytes", "keeps" ], "properties": { "move_id": { "type": "string", "minLength": 1, "maxLength": 40 }, "source_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "destination_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "reason": { "type": "string", "minLength": 1, "maxLength": 300 }, "bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "keeps": { "type": "string", "minLength": 0, "maxLength": 200 } } } }, "deletions": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "set_ref", "remove_ref", "keep_ref", "evidence_level", "references" ], "properties": { "set_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "remove_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "keep_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "evidence_level": { "type": "string", "enum": [ "name_only", "name_size_date", "size_and_partial_hash", "full_sha256" ] }, "references": { "type": "string", "enum": [ "checked", "not_checked" ] } } } }, "pending_decisions": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": [ "item_ref", "question", "suggested_home" ], "properties": { "item_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "question": { "type": "string", "minLength": 1, "maxLength": 300 }, "suggested_home": { "type": "string", "minLength": 0, "maxLength": 200 } } } }, "pre_checks": { "type": "object", "additionalProperties": false, "required": [ "destination_absent", "free_space", "filesystem_fit" ], "properties": { "destination_absent": { "type": "string", "enum": [ "all_absent", "some_exist", "not_checked" ] }, "free_space": { "type": "string", "enum": [ "sufficient_with_margin", "insufficient", "not_checked" ] }, "filesystem_fit": { "type": "string", "minLength": 1, "maxLength": 400 } } }, "overwrite_policy": { "type": "string", "enum": [ "never" ] }, "dry_run_first": { "type": "boolean" }, "execution": { "type": "object", "additionalProperties": false, "required": [ "journal_intent_first", "verify_by", "retire_sources" ], "properties": { "journal_intent_first": { "type": "boolean" }, "verify_by": { "type": "string", "enum": [ "sha256", "size_only" ] }, "retire_sources": { "type": "string", "enum": [ "after_verified_copy_and_person_approval", "immediately" ] } } }, "approval": { "type": "object", "additionalProperties": false, "required": [ "status" ], "properties": { "status": { "type": "string", "enum": [ "awaiting_person", "approved" ] } } }, "planned_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "The plan never approves itself.", "Deletions rest on full-hash duplicates with consumers checked." ] } }, { "slug": "template.plan-approval", "category": "noun.eval_run", "role": "template", "title": "Template: a person's decisions on a move plan: approved and rejected moves, approved deletions, answers to questions and conditions", "category_reason": "An approval records a person's checked decision.", "value": { "describes": "a person's decisions on a move plan: approved and rejected moves, approved deletions, answers to questions and conditions", "schema": { "type": "object", "additionalProperties": false, "required": [ "approval_id", "plan_ref", "approved_moves", "rejected_moves", "approved_deletions", "answers", "conditions", "decided_by_role", "decided_at", "expires" ], "properties": { "approval_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "plan_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "approved_moves": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "string", "minLength": 1, "maxLength": 40 } }, "rejected_moves": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "string", "minLength": 1, "maxLength": 40 } }, "approved_deletions": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "answers": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": [ "item_ref", "answer" ], "properties": { "item_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "answer": { "type": "string", "minLength": 1, "maxLength": 300 } } } }, "conditions": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 300 } }, "decided_by_role": { "type": "string", "minLength": 1, "maxLength": 120 }, "decided_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 }, "expires": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "Recorded by the person who owns the files; workers never write it." ] } }, { "slug": "template.move-journal", "category": "noun.eval_run", "role": "template", "title": "Template: the journal of approved moves: intent before each move, the state before, hashes of copy and source, status and failures", "category_reason": "A journal is a measured execution record.", "value": { "describes": "the journal of approved moves: intent before each move, the state before, hashes of copy and source, status and failures", "schema": { "type": "object", "additionalProperties": false, "required": [ "journal_id", "plan_ref", "approval_ref", "entries", "failures", "sources_removed" ], "properties": { "journal_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "plan_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "approval_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "entries": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "move_id", "intent_logged_at", "before", "destination_existed", "copy_sha256", "source_sha256", "source_unchanged", "status", "verified_at" ], "properties": { "move_id": { "type": "string", "minLength": 1, "maxLength": 40 }, "intent_logged_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 }, "before": { "type": "object", "additionalProperties": false, "required": [ "size_bytes", "inode", "modified" ], "properties": { "size_bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "inode": { "type": "string", "minLength": 1, "maxLength": 40 }, "modified": { "type": "string", "minLength": 1, "maxLength": 40 } } }, "destination_existed": { "type": "boolean" }, "copy_sha256": { "type": [ "string", "null" ], "pattern": "^[0-9a-f]{64}$" }, "source_sha256": { "type": [ "string", "null" ], "pattern": "^[0-9a-f]{64}$" }, "source_unchanged": { "type": [ "boolean", "null" ] }, "status": { "type": "string", "enum": [ "planned", "copied", "verified", "failed", "refused_destination_exists" ] }, "verified_at": { "type": [ "string", "null" ], "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } } }, "failures": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "move_id", "reason", "retry" ], "properties": { "move_id": { "type": "string", "minLength": 1, "maxLength": 40 }, "reason": { "type": "string", "minLength": 1, "maxLength": 300 }, "retry": { "type": "string", "enum": [ "next_run", "needs_person", "no" ] } } } }, "sources_removed": { "type": "integer", "enum": [ 0 ] } } }, "rules": [ "sources_removed is always 0; retiring sources is a separate person's verb." ] } }, { "slug": "template.retirement-record", "category": "noun.eval_run", "role": "template", "title": "Template: a person's decision on each verified source: retire, keep both or leave a link, with the checks that allowed it", "category_reason": "A retirement records a person's decision.", "value": { "describes": "a person's decision on each verified source: retire, keep both or leave a link, with the checks that allowed it", "schema": { "type": "object", "additionalProperties": false, "required": [ "retirement_id", "journal_ref", "items", "decided_by_role", "decided_at" ], "properties": { "retirement_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "journal_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "items": { "type": "array", "minItems": 1, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "move_id", "verified", "source_unchanged", "open_handles", "action" ], "properties": { "move_id": { "type": "string", "minLength": 1, "maxLength": 40 }, "verified": { "type": "boolean" }, "source_unchanged": { "type": "boolean" }, "open_handles": { "type": "string", "enum": [ "none_seen", "in_use", "unknown" ] }, "action": { "type": "string", "enum": [ "retire", "keep_both", "leave_link" ] } } } }, "decided_by_role": { "type": "string", "minLength": 1, "maxLength": 120 }, "decided_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "Only verified, unchanged sources with no open handles are retired." ] } }, { "slug": "template.index-reconciliation", "category": "noun.eval_run", "role": "template", "title": "Template: whether registries, indexes and folder notes agree with what moved: missing paths, stale notes and copies nobody registered", "category_reason": "A reconciliation is a deterministic consistency check.", "value": { "describes": "whether registries, indexes and folder notes agree with what moved: missing paths, stale notes and copies nobody registered", "schema": { "type": "object", "additionalProperties": false, "required": [ "reconciliation_id", "journal_ref", "registries_updated", "paths_checked", "missing_paths", "stale_notes", "unregistered_copies", "verdict", "checked_at" ], "properties": { "reconciliation_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "journal_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "registries_updated": { "type": "integer", "minimum": 0, "maximum": 1000000 }, "paths_checked": { "type": "integer", "minimum": 0, "maximum": 1000000000 }, "missing_paths": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "stale_notes": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "unregistered_copies": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "verdict": { "type": "string", "enum": [ "consistent", "drift_found", "unknown" ] }, "checked_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "rules": [ "Every journal entry is checked against the registries." ] } }, { "slug": "triage-checks", "category": "noun.benchmark_suite", "role": "record_checks", "title": "Record checks for move plans, with synthetic golden plans", "category_reason": "A deterministic check suite with known-good and known-bad examples; the validator recomputes every verdict.", "value": { "fixture_kind": "finding_checks", "purpose": "Checks every move plan must pass before a person is asked to approve it. The two synthetic good plans pass all of them; each synthetic bad plan fails exactly the one check named in fails.", "deterministic_checks": [ { "id": "moves-complete", "rule": { "rule": "each_has", "path": "moves", "fields": [ "move_id", "source_ref", "destination_ref", "reason" ] }, "why": "Every move names its source, destination and reason, so a person can approve or reject it on its own." }, { "id": "never-overwrite", "rule": { "rule": "enum", "path": "overwrite_policy", "values": [ "never" ] }, "why": "A move never overwrites an existing destination." }, { "id": "destinations-checked", "rule": { "rule": "enum", "path": "pre_checks.destination_absent", "values": [ "all_absent" ] }, "why": "The whole batch is checked for existing destinations before anything is written." }, { "id": "space-checked", "rule": { "rule": "enum", "path": "pre_checks.free_space", "values": [ "sufficient_with_margin" ] }, "why": "The destination has room for every move with a margin." }, { "id": "fit-stated", "rule": { "rule": "present", "path": "pre_checks.filesystem_fit" }, "why": "The plan says what the destination's filesystem loses, such as permissions or small-file efficiency, and how that is handled." }, { "id": "dry-run-first", "rule": { "rule": "enum", "path": "dry_run_first", "values": [ true ] }, "why": "A dry run lists every action before any real one." }, { "id": "awaits-person", "rule": { "rule": "enum", "path": "approval.status", "values": [ "awaiting_person" ] }, "why": "A plan never approves itself; approval lives in the person's own record." }, { "id": "deletions-full-hash", "rule": { "rule": "pattern_absent", "path": "deletions", "pattern": "^(name_only|name_size_date|size_and_partial_hash)$" }, "why": "Only a full content hash with distinct inodes proves a duplicate; names, sizes and dates prove nothing." }, { "id": "deletions-references-checked", "rule": { "rule": "pattern_absent", "path": "deletions", "pattern": "^not_checked$" }, "why": "Equal content does not mean a path is unreferenced; consumers are checked first." }, { "id": "secrets-excluded", "rule": { "rule": "min_items", "path": "scope.secret_patterns", "min": 1 }, "why": "Secret-bearing paths are listed and kept out of every read." }, { "id": "journal-first", "rule": { "rule": "enum", "path": "execution.journal_intent_first", "values": [ true ] }, "why": "Each move's intent is written down before the move starts, so an interrupted batch can be resumed or undone." }, { "id": "verify-by-hash", "rule": { "rule": "enum", "path": "execution.verify_by", "values": [ "sha256" ] }, "why": "A copy is verified by content hash; a matching size proves little." }, { "id": "retire-after-verify", "rule": { "rule": "enum", "path": "execution.retire_sources", "values": [ "after_verified_copy_and_person_approval" ] }, "why": "A source is removed only after its copy is verified, it did not change during the copy, and a person approved." } ], "golden_fixtures": [ { "id": "good-archive-plan", "label": "known_good", "provenance": "synthetic", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "good-questions-only", "label": "known_good", "provenance": "synthetic", "finding": { "plan_id": "MP-4", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem" ] }, "moves": [], "deletions": [], "pending_decisions": [ { "item_ref": "shared drive: Grants 2023 (2)", "question": "Which of the two grant folders is the one staff use?", "suggested_home": "" }, { "item_ref": "shared drive: photos-unsorted", "question": "May these photos leave the shared drive at all?", "suggested_home": "" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "No moves are planned until the two questions are answered." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:10Z" } }, { "id": "bad-move-no-reason", "label": "known_bad", "fails": "moves-complete", "why": "Moves have no reasons.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-overwrites", "label": "known_bad", "fails": "never-overwrite", "why": "The plan overwrites newer files.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "if_newer", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-destinations-unchecked", "label": "known_bad", "fails": "destinations-checked", "why": "Existing destinations were not checked.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "not_checked", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-no-space-check", "label": "known_bad", "fails": "space-checked", "why": "Free space was not checked.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "not_checked", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-fit-unstated", "label": "known_bad", "fails": "fit-stated", "why": "The filesystem's limits are not considered.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin" }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-no-dry-run", "label": "known_bad", "fails": "dry-run-first", "why": "No dry run before real moves.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": false, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-self-approved", "label": "known_bad", "fails": "awaits-person", "why": "The plan marks itself approved.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "approved" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-weak-duplicate", "label": "known_bad", "fails": "deletions-full-hash", "why": "A deletion rests on matching name, size and date.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "name_size_date", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-references-unchecked", "label": "known_bad", "fails": "deletions-references-checked", "why": "A deletion was planned without checking consumers.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "not_checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-secrets-read", "label": "known_bad", "fails": "secrets-excluded", "why": "No secret patterns are excluded.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-no-journal", "label": "known_bad", "fails": "journal-first", "why": "Moves start without a journal entry.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": false, "verify_by": "sha256", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-size-only", "label": "known_bad", "fails": "verify-by-hash", "why": "Copies are verified by size only.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "size_only", "retire_sources": "after_verified_copy_and_person_approval" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } }, { "id": "bad-retire-at-once", "label": "known_bad", "fails": "retire-after-verify", "why": "Sources are removed as soon as copies finish.", "finding": { "plan_id": "MP-3", "scope": { "scope_ref": "SC-2", "secret_patterns": [ ".env*", "*.pem", "*key*", "credential stores" ] }, "moves": [ { "move_id": "m1", "source_ref": "internal disk: datasets/survey-2024", "destination_ref": "archive drive: data/survey-2024", "reason": "Cold data, untouched since 2025; the internal disk is 97 percent full.", "bytes": 61203445760, "keeps": "" }, { "move_id": "m2", "source_ref": "internal disk: renders/old-campaign", "destination_ref": "archive drive: outputs/old-campaign", "reason": "Finished renders that the campaign folder no longer reads.", "bytes": 9126805504, "keeps": "a permissions archive beside the copy" } ], "deletions": [ { "set_ref": "DS-1", "remove_ref": "internal disk: scratch/survey-copy", "keep_ref": "internal disk: datasets/survey-2024", "evidence_level": "full_sha256", "references": "checked" } ], "pending_decisions": [ { "item_ref": "internal disk: misc/to-sort", "question": "Is this folder still part of the outreach project?", "suggested_home": "archive drive: projects/outreach" } ], "pre_checks": { "destination_absent": "all_absent", "free_space": "sufficient_with_margin", "filesystem_fit": "The archive drive keeps no Unix permissions and wastes space on tiny files; m2 keeps a permissions archive and small files are packed first." }, "overwrite_policy": "never", "dry_run_first": true, "execution": { "journal_intent_first": true, "verify_by": "sha256", "retire_sources": "immediately" }, "approval": { "status": "awaiting_person" }, "planned_at": "2026-10-30T07:00Z" } } ], "finding_schema": { "type": "object", "additionalProperties": false, "required": [ "plan_id", "scope", "moves", "deletions", "pending_decisions", "pre_checks", "overwrite_policy", "dry_run_first", "execution", "approval", "planned_at" ], "properties": { "plan_id": { "type": "string", "minLength": 1, "maxLength": 60 }, "scope": { "type": "object", "additionalProperties": false, "required": [ "scope_ref", "secret_patterns" ], "properties": { "scope_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "secret_patterns": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "minLength": 1, "maxLength": 120 } } } }, "moves": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "move_id", "source_ref", "destination_ref", "reason", "bytes", "keeps" ], "properties": { "move_id": { "type": "string", "minLength": 1, "maxLength": 40 }, "source_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "destination_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "reason": { "type": "string", "minLength": 1, "maxLength": 300 }, "bytes": { "type": "integer", "minimum": 0, "maximum": 10000000000000000 }, "keeps": { "type": "string", "minLength": 0, "maxLength": 200 } } } }, "deletions": { "type": "array", "minItems": 0, "maxItems": 2000, "items": { "type": "object", "additionalProperties": false, "required": [ "set_ref", "remove_ref", "keep_ref", "evidence_level", "references" ], "properties": { "set_ref": { "type": "string", "minLength": 1, "maxLength": 120 }, "remove_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "keep_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "evidence_level": { "type": "string", "enum": [ "name_only", "name_size_date", "size_and_partial_hash", "full_sha256" ] }, "references": { "type": "string", "enum": [ "checked", "not_checked" ] } } } }, "pending_decisions": { "type": "array", "minItems": 0, "maxItems": 500, "items": { "type": "object", "additionalProperties": false, "required": [ "item_ref", "question", "suggested_home" ], "properties": { "item_ref": { "type": "string", "minLength": 1, "maxLength": 200 }, "question": { "type": "string", "minLength": 1, "maxLength": 300 }, "suggested_home": { "type": "string", "minLength": 0, "maxLength": 200 } } } }, "pre_checks": { "type": "object", "additionalProperties": false, "required": [ "destination_absent", "free_space", "filesystem_fit" ], "properties": { "destination_absent": { "type": "string", "enum": [ "all_absent", "some_exist", "not_checked" ] }, "free_space": { "type": "string", "enum": [ "sufficient_with_margin", "insufficient", "not_checked" ] }, "filesystem_fit": { "type": "string", "minLength": 1, "maxLength": 400 } } }, "overwrite_policy": { "type": "string", "enum": [ "never" ] }, "dry_run_first": { "type": "boolean" }, "execution": { "type": "object", "additionalProperties": false, "required": [ "journal_intent_first", "verify_by", "retire_sources" ], "properties": { "journal_intent_first": { "type": "boolean" }, "verify_by": { "type": "string", "enum": [ "sha256", "size_only" ] }, "retire_sources": { "type": "string", "enum": [ "after_verified_copy_and_person_approval", "immediately" ] } } }, "approval": { "type": "object", "additionalProperties": false, "required": [ "status" ], "properties": { "status": { "type": "string", "enum": [ "awaiting_person", "approved" ] } } }, "planned_at": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?Z$", "maxLength": 30 } } }, "expected": { "good-archive-plan": { "verdict": "pass", "failed_checks": [] }, "good-questions-only": { "verdict": "pass", "failed_checks": [] }, "bad-move-no-reason": { "verdict": "fail", "failed_checks": [ "moves-complete" ] }, "bad-overwrites": { "verdict": "fail", "failed_checks": [ "never-overwrite" ] }, "bad-destinations-unchecked": { "verdict": "fail", "failed_checks": [ "destinations-checked" ] }, "bad-no-space-check": { "verdict": "fail", "failed_checks": [ "space-checked" ] }, "bad-fit-unstated": { "verdict": "fail", "failed_checks": [ "fit-stated" ] }, "bad-no-dry-run": { "verdict": "fail", "failed_checks": [ "dry-run-first" ] }, "bad-self-approved": { "verdict": "fail", "failed_checks": [ "awaits-person" ] }, "bad-weak-duplicate": { "verdict": "fail", "failed_checks": [ "deletions-full-hash" ] }, "bad-references-unchecked": { "verdict": "fail", "failed_checks": [ "deletions-references-checked" ] }, "bad-secrets-read": { "verdict": "fail", "failed_checks": [ "secrets-excluded" ] }, "bad-no-journal": { "verdict": "fail", "failed_checks": [ "journal-first" ] }, "bad-size-only": { "verdict": "fail", "failed_checks": [ "verify-by-hash" ] }, "bad-retire-at-once": { "verdict": "fail", "failed_checks": [ "retire-after-verify" ] } } } }, { "slug": "example.inventory", "category": "noun.eval_run", "role": "example", "title": "Worked example: a read-only inventory of an internal disk and an archive drive", "synthetic": true, "category_reason": "A synthetic inventory in the template's shape.", "value": { "conforms_to": { "$pin": "template.inventory" }, "instance": { "inventory_id": "INV-2", "scope_ref": "SC-2", "as_of": "2026-10-30T06:00Z", "unit": "bytes", "roots": [ { "root_ref": "internal disk", "filesystem": "ext4", "free_bytes": 60000000000, "total_bytes": 2000000000000 }, { "root_ref": "archive drive", "filesystem": "exfat", "free_bytes": 5200000000000, "total_bytes": 8000000000000 } ], "totals": { "files": 2841907, "folders": 204115, "allocated_bytes": 4701337190400, "apparent_bytes": 4655870066688 }, "groups": [ { "path_ref": "internal disk: datasets", "files": 1204, "allocated_bytes": 540106571776, "newest_change": "2025-08-02" }, { "path_ref": "internal disk: renders", "files": 18533, "allocated_bytes": 97710505984, "newest_change": "2026-03-14" }, { "path_ref": "internal disk: caches", "files": 902117, "allocated_bytes": 64424509440, "newest_change": "2026-10-30" } ], "errors": [ { "path_ref": "internal disk: system/protected", "kind": "permission_denied" }, { "path_ref": "internal disk: mail/index", "kind": "changed_during_scan" } ], "secret_paths_listed_not_opened": 3, "files_changed_by_scan": 0, "not_covered": [ "Protected system folders, which need a person with administrator access.", "Access times, which this scan's own reads can change, so they were not used." ] }, "shows": "Totals are in bytes, allocated and apparent; two errors are kept; three secret-bearing paths were listed, not opened; and the scan changed nothing.", "source": "Synthetic. Written for this pack; every name, number and identifier is made up." } }, { "slug": "example.duplicate-set", "category": "noun.eval_run", "role": "example", "title": "Worked example: three equal copies proven by full hash", "synthetic": true, "category_reason": "A synthetic duplicate set in the template's shape.", "value": { "conforms_to": { "$pin": "template.duplicate-set" }, "instance": { "set_id": "DS-1", "members": [ { "path_ref": "internal disk: datasets/survey-2024/responses.csv", "size_bytes": 4000000000, "allocated_bytes": 4000002048, "inode": "18842", "hardlinks": 1, "sha256": "6c1f0e9d8b7a69584736251403f2e1d0c9b8a7f6e5d4c3b2a1908f7e6d5c4b3a" }, { "path_ref": "internal disk: scratch/survey-copy/responses.csv", "size_bytes": 4000000000, "allocated_bytes": 4000002048, "inode": "29107", "hardlinks": 1, "sha256": "6c1f0e9d8b7a69584736251403f2e1d0c9b8a7f6e5d4c3b2a1908f7e6d5c4b3a" }, { "path_ref": "internal disk: old-exports/responses.csv", "size_bytes": 4000000000, "allocated_bytes": 4000002048, "inode": "30551", "hardlinks": 1, "sha256": "6c1f0e9d8b7a69584736251403f2e1d0c9b8a7f6e5d4c3b2a1908f7e6d5c4b3a" } ], "evidence_level": "full_sha256", "distinct_inodes": true, "consumers": [ { "path_ref": "reports/annual/build.py", "uses": "reads datasets/survey-2024/responses.csv" } ], "references_checked": "checked", "reclaimable_bytes": 8000004096, "note": "Three equal files with distinct inodes. Keeping the copy the report reads frees two allocations; matching content alone did not show the other two were unused.", "found_at": "2026-10-30T06:40Z" }, "shows": "Three files share one full hash and have distinct inodes, so removing two frees 8,000,004,096 allocated bytes, but only after the report that reads one of them is pointed at the kept copy.", "source": "Synthetic. Written for this pack; every name, number and identifier is made up." } } ], "verbs": [ { "slug": "scope-scan", "kind": "verb.problemize", "title": "Agree what a scan may read", "claimant": "agent", "summary": "Write the scan scope before anything is read: roots and their mount health, exclusions, secret patterns, limits and the person's rules about what belongs together.", "outcome": "A scan scope, or blocked with one question when a root's mount needs a check first.", "ready_outcome": "scope_written", "pinned": [ "secret-exclusions", "placement-policy" ], "params": { "roots": { "schema": { "type": "array", "minItems": 0, "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }, "description": "The roots the person wants triaged; with none named the verb ends blocked.", "default": [] } }, "produces": [ { "name": "scope", "template": "template.scan-scope" } ], "steps": [ "Verify the pins. With no roots named, end blocked and ask which roots to triage.", "List each root with its filesystem and mount health; an unclean unmount blocks the scan until a person checks it.", "Copy the secret exclusions in full and add any the person names.", "Record the person's grouping rules exactly as stated." ], "acceptance": [ { "id": "scope-matches-template", "check": "The scope validates against template.scan-scope's schema.", "method": "deterministic" }, { "id": "secrets-listed", "check": "secret_patterns has at least one entry.", "method": "deterministic" }, { "id": "rules-faithful", "check": "The grouping rules match what the person said.", "method": "judgment" } ], "bounds": { "max_elapsed_seconds": 600 } }, { "slug": "inventory-files", "kind": "verb.ingest", "title": "Inventory files read-only", "claimant": "agent", "model_use": "none", "summary": "Walk the scoped roots read-only, recording counts, allocated and apparent bytes, free space, groups and every error, without opening secret-bearing paths or changing any file.", "outcome": "An inventory, or unknown for roots that could not be read.", "ready_outcome": "inventory_taken", "pinned": [ "secret-exclusions" ], "slots": [ { "name": "scope", "category": "noun.seed_hypothesis", "description": "Pin of the scan scope." } ], "produces": [ { "name": "inventory", "template": "template.inventory" } ], "steps": [ "Verify the pins. Read metadata only; never open a path that matches a secret pattern.", "Record allocated and apparent sizes in bytes, and every permission, read or change error.", "List what was not covered, such as protected folders, instead of guessing their contents." ], "acceptance": [ { "id": "inventory-matches-template", "check": "The inventory validates against template.inventory's schema.", "method": "deterministic" }, { "id": "nothing-changed", "check": "files_changed_by_scan is 0.", "method": "deterministic" }, { "id": "errors-kept", "check": "Every error met during the walk appears in errors.", "method": "deterministic" } ], "network": [ "local_only", "Only the local and attached roots named in the scope; no remote host is contacted." ], "bounds": { "max_elapsed_seconds": 86400 } }, { "slug": "classify-items", "kind": "verb.situate", "title": "Place each group under the policy", "claimant": "agent", "summary": "Place each inventoried group under the placement policy with the evidence for its role and status, and turn every uncertain placement into a question for the person with a suggested home.", "outcome": "A placement register with pending questions.", "ready_outcome": "items_classified", "pinned": [ "placement-policy" ], "slots": [ { "name": "inventory", "category": "noun.eval_run", "description": "Pin of the inventory." }, { "name": "scope", "category": "noun.seed_hypothesis", "description": "Pin of the scan scope." } ], "produces": [ { "name": "register", "template": "template.placement-register" } ], "steps": [ "Verify the pins. Decide role by what the work produces; never by name alone.", "Mark low-confidence placements with a question and a suggested home.", "Keep the person's named groups together." ], "acceptance": [ { "id": "register-matches-template", "check": "The register validates against template.placement-register's schema.", "method": "deterministic" }, { "id": "low-confidence-asked", "check": "Every low-confidence item has a pending question.", "method": "deterministic" }, { "id": "evidence-supports", "check": "Each placement's evidence supports its role.", "method": "judgment" } ], "bounds": { "max_elapsed_seconds": 3600 } }, { "slug": "find-duplicates", "kind": "verb.verify_gate", "title": "Prove duplicates by content", "claimant": "agent", "model_use": "none", "summary": "Shortlist candidate duplicates by size, then prove them with full content hashes, inodes and link counts, list each set's consumers and compute the space actually reclaimable.", "outcome": "Duplicate sets with their evidence levels, or no_change when no candidate survives the full hash.", "ready_outcome": "duplicates_found", "pinned": [ "duplicate-rules", "secret-exclusions" ], "slots": [ { "name": "inventory", "category": "noun.eval_run", "description": "Pin of the inventory." }, { "name": "scope", "category": "noun.seed_hypothesis", "description": "Pin of the scan scope." } ], "produces": [ { "name": "sets", "template": "template.duplicate-set", "max": 50 } ], "steps": [ "Verify the pins. Use size only to shortlist; hash every shortlisted file in full.", "Treat hard links as one file; compare archives with loose folders by listing and hashes.", "Search for consumers of each member before computing reclaimable bytes." ], "acceptance": [ { "id": "sets-match-template", "check": "Every set validates against template.duplicate-set's schema.", "method": "deterministic" }, { "id": "reclaim-from-full-hash", "check": "reclaimable_bytes is filled only for full_sha256 sets with distinct inodes.", "method": "deterministic" }, { "id": "reclaim-recomputes", "check": "reclaimable_bytes equals the allocated bytes of all members but one.", "method": "deterministic" } ], "network": [ "local_only", "Only the local and attached roots named in the scope; no remote host is contacted." ], "bounds": { "max_elapsed_seconds": 86400 } }, { "slug": "plan-moves", "kind": "verb.decompose_dag", "title": "Draft the move plan", "claimant": "agent", "summary": "Draft a move plan for the person: every move with source, destination, reason and size, deletions only for full-hash duplicates with consumers checked, open questions, destination and space prechecks, filesystem fit and a dry run first.", "outcome": "A move plan that passes the triage checks and waits for the person.", "ready_outcome": "plan_drafted", "pinned": [ "triage-checks", "filesystem-fit", "duplicate-rules" ], "slots": [ { "name": "register", "category": "noun.seed_hypothesis", "description": "Pin of the placement register." }, { "name": "duplicates", "category": "noun.eval_run", "description": "Pins of duplicate sets.", "many": true, "max": 50, "required": false }, { "name": "scope", "category": "noun.seed_hypothesis", "description": "Pin of the scan scope." } ], "produces": [ { "name": "plan", "template": "template.move-plan" } ], "steps": [ "Verify the pins. Propose only moves the register and the person's rules support.", "Check the whole batch for existing destinations and free space before listing it.", "State what the destination filesystem loses and how each move handles it.", "Leave approval to the person; the plan's status stays awaiting_person." ], "acceptance": [ { "id": "plan-matches-template", "check": "The plan validates against template.move-plan's schema.", "method": "deterministic" }, { "id": "triage-checks-pass", "check": "The plan passes every check in triage-checks.", "method": "deterministic" }, { "id": "moves-justified", "check": "Each move's reason follows from the register.", "method": "judgment" } ], "bounds": { "max_elapsed_seconds": 3600 } }, { "slug": "approve-plan", "kind": "verb.verify_gate", "title": "Approve a move plan", "claimant": "human", "model_use": "none", "summary": "The person who owns the files approves or rejects each move and deletion and answers each open question. This template performs nothing itself.", "outcome": "A plan approval.", "ready_outcome": "plan_decided", "slots": [ { "name": "plan", "category": "noun.seed_hypothesis", "description": "Pin of the move plan." } ], "produces": [ { "name": "approval", "template": "template.plan-approval" } ], "steps": [ "Read each move, deletion and question; approve only what you would accept finding done.", "Set conditions and an expiry." ], "acceptance": [ { "id": "approval-matches-template", "check": "The approval validates against template.plan-approval's schema.", "method": "deterministic" }, { "id": "expiry-set", "check": "expires is filled.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 604800 } }, { "slug": "move-and-verify", "kind": "verb.compile_dag", "title": "Carry out approved moves and verify them", "claimant": "agent", "model_use": "none", "summary": "Carry out approved moves one at a time: write the intent first, refuse when the destination exists, copy, verify by content hash, confirm the source did not change during the copy, and record every failure with its retry; no source is removed.", "outcome": "A move journal, or blocked when the approval is missing or expired.", "ready_outcome": "moves_journaled", "pinned": [ "filesystem-fit" ], "slots": [ { "name": "plan", "category": "noun.seed_hypothesis", "description": "Pin of the move plan." }, { "name": "approval", "category": "noun.eval_run", "description": "Pin of the person's approval." } ], "produces": [ { "name": "journal", "template": "template.move-journal" } ], "steps": [ "Verify the pins and the approval; carry out only approved moves, in order, after a dry run lists them.", "Write each move's intent and its state before starting; refuse a move whose destination exists.", "Verify every copy by full hash and confirm the source's hash and metadata did not change.", "Leave every source in place; removing sources belongs to the person's retire-sources verb." ], "acceptance": [ { "id": "journal-matches-template", "check": "The journal validates against template.move-journal's schema.", "method": "deterministic" }, { "id": "only-approved", "check": "Every journal entry's move is in the approval's approved moves.", "method": "deterministic" }, { "id": "no-source-removed", "check": "sources_removed is 0.", "method": "deterministic" } ], "network": [ "local_only", "Only the local and attached roots named in the scope; no remote host is contacted." ], "bounds": { "max_elapsed_seconds": 604800 } }, { "slug": "retire-sources", "kind": "verb.verify_gate", "title": "Decide which verified sources to retire", "claimant": "human", "model_use": "none", "summary": "The person decides, for each verified move whose source did not change and has no open handles, whether to retire the source, keep both or leave a link. This template performs nothing itself.", "outcome": "A retirement record.", "ready_outcome": "retirements_decided", "slots": [ { "name": "journal", "category": "noun.eval_run", "description": "Pin of the move journal." } ], "produces": [ { "name": "retirement", "template": "template.retirement-record" } ], "steps": [ "Retire only sources whose copy verified and whose source did not change.", "Keep both when anything still reads the source or you are unsure." ], "acceptance": [ { "id": "retirement-matches-template", "check": "The record validates against template.retirement-record's schema.", "method": "deterministic" }, { "id": "only-verified", "check": "Every retired item has verified and source_unchanged true.", "method": "deterministic" } ], "bounds": { "max_elapsed_seconds": 604800 } }, { "slug": "reconcile-indexes", "kind": "verb.recheck_fragility", "title": "Bring indexes in line with what moved", "claimant": "agent", "summary": "Check every journal entry against the registries, indexes and folder notes, and list missing paths, stale notes and copies that were made but never registered.", "outcome": "A reconciliation, or no_change when everything already agrees.", "ready_outcome": "indexes_reconciled", "pinned": [ "placement-policy" ], "slots": [ { "name": "journal", "category": "noun.eval_run", "description": "Pin of the move journal." }, { "name": "retirement", "category": "noun.eval_run", "description": "Pin of the retirement record, if any.", "required": false } ], "produces": [ { "name": "reconciliation", "template": "template.index-reconciliation" } ], "steps": [ "Verify the pins. Check that every moved path exists where the registry says.", "List notes that still describe an earlier state, and copies nobody registered.", "Propose one fix per drift; leave source retirement to the person." ], "acceptance": [ { "id": "reconciliation-matches-template", "check": "The reconciliation validates against template.index-reconciliation's schema.", "method": "deterministic" }, { "id": "every-entry-checked", "check": "paths_checked is at least the number of journal entries.", "method": "deterministic" }, { "id": "drift-listed", "check": "verdict is drift_found whenever a list of problems is non-empty.", "method": "deterministic" } ], "network": [ "local_only", "Only the local and attached roots named in the scope; no remote host is contacted." ], "bounds": { "max_elapsed_seconds": 7200 } } ] }