From 36a78e9f226ec828fffa1472280f16489d550112 Mon Sep 17 00:00:00 2001 From: Aly Raffauf Date: Mon, 10 Aug 2026 10:45:35 -0400 Subject: [PATCH] Clean up stale tooling references --- .gitignore | 1 - README.md | 2 +- cmd/cattery/main.go | 2 +- docs/hooks.md | 2 +- docs/reconciliation.md | 2 +- docs/repository-layout.md | 4 ++-- docs/secrets.md | 2 +- go.mod | 2 +- integration/secrets_test.go | 3 +-- internal/application/add/execute.go | 2 +- internal/application/add/plan.go | 3 +-- internal/application/add/service.go | 8 ++++---- internal/application/add/types.go | 2 +- internal/application/apply/decisions.go | 2 +- internal/application/apply/dependencies.go | 2 +- internal/application/apply/execute_aliases.go | 4 ++-- internal/application/apply/execute_files.go | 2 +- internal/application/apply/hooks.go | 2 +- internal/application/apply/prepare.go | 4 ++-- internal/application/apply/service.go | 4 ++-- internal/application/apply/source_guard.go | 4 ++-- internal/application/apply/types.go | 2 +- internal/application/apply/verify.go | 2 +- internal/application/initialize/types.go | 2 +- internal/application/inspect/diff.go | 4 ++-- internal/application/inspect/service.go | 4 ++-- internal/application/inspect/types.go | 2 +- internal/application/validate/types.go | 2 +- internal/application/version/types.go | 2 +- internal/bootstrap/adapters.go | 4 ++-- internal/cli/add.go | 2 +- internal/cli/apply.go | 2 +- internal/cli/diff.go | 4 ++-- internal/cli/init.go | 2 +- internal/cli/prompt.go | 2 +- internal/cli/render.go | 10 +++++----- internal/cli/root.go | 2 +- internal/cli/runtime.go | 6 +++--- internal/cli/status.go | 4 ++-- internal/cli/validate.go | 2 +- internal/deployment/file.go | 2 +- internal/diff/safe.go | 9 ++++----- internal/diff/safe_test.go | 2 +- internal/filesystem/alias.go | 4 ++-- internal/filesystem/mode.go | 4 ++-- internal/filesystem/precondition.go | 2 +- internal/filesystem/replace.go | 5 ++--- internal/filesystem/sync.go | 5 ++--- internal/pathsafe/ancestor.go | 2 +- internal/pathsafe/equivalence.go | 2 +- internal/pathsafe/path.go | 4 ++-- internal/pathsafe/protected.go | 4 ++-- internal/pathsafe/root.go | 4 ++-- internal/reconcile/classify_alias.go | 8 ++++---- internal/reconcile/classify_alias_test.go | 1 - internal/reconcile/classify_file.go | 17 ++++++++--------- internal/reconcile/classify_file_test.go | 2 +- internal/reconcile/classify_retirement.go | 2 +- internal/reconcile/classify_retirement_test.go | 4 ++-- internal/reconcile/decisions.go | 17 +++++++---------- internal/reconcile/decisions_test.go | 4 ++-- internal/reconcile/types.go | 4 ++-- internal/repository/collisions.go | 2 +- internal/routes/activate.go | 2 +- internal/selection/repository.go | 2 +- internal/state/database.go | 2 +- internal/state/files.go | 4 ++-- internal/state/keyfile.go | 2 +- internal/state/keyid.go | 2 +- internal/state/recovery.go | 2 +- internal/state/transitions.go | 4 ++-- internal/testfixture/database/store.go | 2 +- justfile | 2 +- 73 files changed, 121 insertions(+), 132 deletions(-) diff --git a/.gitignore b/.gitignore index 3b1c90f..e7ff42e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /.direnv/ /result -/dist/ /coverage.out diff --git a/README.md b/README.md index 3e29fec..8145fe8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ state database so it can tell an intentional edit from a conflict. ## Install -Cattery is a single static binary. Build it with Go 1.25 or newer: +Cattery is a single static binary. Build it with Go 1.26 or newer: ``` go build ./cmd/cattery diff --git a/cmd/cattery/main.go b/cmd/cattery/main.go index 0dd5d54..113cd47 100644 --- a/cmd/cattery/main.go +++ b/cmd/cattery/main.go @@ -1,7 +1,7 @@ // Package main is the sole process boundary of cattery: it creates the // signal-aware cancellation causes, requests one application from // bootstrap, calls the CLI executor, and owns the only production -// os.Exit (PLAN.md Section 12.1). +// os.Exit. package main import ( diff --git a/docs/hooks.md b/docs/hooks.md index 0442686..9ea99c0 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -3,7 +3,7 @@ Hooks let a repository run arbitrary trusted programs around an apply, such as installing packages, reloading a service, or bootstrapping a tool. Cattery has no plugin runtime and no built-in package-manager integration; hooks are the -extensibility point. This document mirrors PLAN.md Section 10. +extensibility point. ## Layout diff --git a/docs/reconciliation.md b/docs/reconciliation.md index a7a74c6..c25b2ab 100644 --- a/docs/reconciliation.md +++ b/docs/reconciliation.md @@ -6,7 +6,7 @@ local SQLite state database from the last successful apply. This three-way model is what lets Cattery distinguish an intentional edit from a conflict without ever silently destroying a file. -This document mirrors PLAN.md Sections 7, 8.5, 9, and 14. +This document describes reconciliation, retirement, and recovery behavior. ## The core matrix diff --git a/docs/repository-layout.md b/docs/repository-layout.md index 780926f..c1a7ab4 100644 --- a/docs/repository-layout.md +++ b/docs/repository-layout.md @@ -4,8 +4,8 @@ A Cattery repository is a plain directory tree of literal files. There is no manifest, no marker file, and no templating. Cattery copies ordinary files beneath `$HOME` and records what it installed in a local SQLite state database. -This document describes the layout Cattery's compiler accepts. It mirrors -PLAN.md Section 2 and is the contract every command validates against. +This document describes the layout Cattery's compiler accepts and is the +contract every command validates against. ## Root files diff --git a/docs/secrets.md b/docs/secrets.md index 5450ba0..4520f76 100644 --- a/docs/secrets.md +++ b/docs/secrets.md @@ -2,7 +2,7 @@ Cattery stores file-level secrets as SOPS-encrypted binary payloads and decrypts them only in memory, only when a command needs secret semantics. This -document mirrors PLAN.md Sections 4 and 8. Cattery does not embed or reimplement +document describes the storage and state model. Cattery does not embed or reimplement SOPS cryptography; it shells out to the installed `sops` executable. ## Setup diff --git a/go.mod b/go.mod index 78770c2..4675801 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/alyraffauf/cattery -go 1.25.0 +go 1.26.0 require ( github.com/adrg/xdg v0.5.3 diff --git a/integration/secrets_test.go b/integration/secrets_test.go index c66995d..329cfa4 100644 --- a/integration/secrets_test.go +++ b/integration/secrets_test.go @@ -165,8 +165,7 @@ func installIdentity(t *testing.T, home, keyHome string) { // testSecretsRealRoundTrip proves an actual binary-mode encrypt/decrypt // round trip through the pinned real sops and age tools with an ephemeral -// identity. It skips only when the real tools are absent, which never -// happens under just test-sops in the pinned shell. +// identity. It skips only when the real tools are absent. func testSecretsRealRoundTrip(t *testing.T) { env := newExecEnv(t) env.initRepository(t) diff --git a/internal/application/add/execute.go b/internal/application/add/execute.go index 16f78c3..e08c144 100644 --- a/internal/application/add/execute.go +++ b/internal/application/add/execute.go @@ -14,7 +14,7 @@ import ( // execute runs one batch sequentially in plan execution order, writing each // source, revalidating the target, and establishing the equal baseline. A // later failure leaves earlier adopted items accurately recorded; the batch -// is never rolled back (PLAN.md Section 11.6 step 10). +// is never rolled back. func (service *Service) execute(ctx context.Context, identity RepositoryIdentity, plan BatchPlan) (Result, error) { items := plan.Items() exec := &executor{service: service, identity: identity, records: make([]ItemResult, 0, len(items))} diff --git a/internal/application/add/plan.go b/internal/application/add/plan.go index 7cdb393..b094f5c 100644 --- a/internal/application/add/plan.go +++ b/internal/application/add/plan.go @@ -8,8 +8,7 @@ import ( // BuildPlan freezes the preflighted items into a BatchPlan. Items are sorted // by target path for display while execution proceeds in that same order -// (PLAN.md Section 11.6: sources and baselines are written sequentially in -// target-path order). +// Sources and baselines are written sequentially in target-path order. func BuildPlan(items []ItemPlanInput) (BatchPlan, error) { plans, err := buildItems(items) if err != nil { diff --git a/internal/application/add/service.go b/internal/application/add/service.go index a7efb95..5b55149 100644 --- a/internal/application/add/service.go +++ b/internal/application/add/service.go @@ -12,7 +12,7 @@ import ( ) // WriteDependencies carries the secret-specific ports required by add's -// execution phases. They remain separate from Task 75's frozen Dependencies +// execution phases. They remain separate from Dependencies // so the contract owner can finish without a shared-file change. type WriteDependencies struct { Secrets *secrets.Client @@ -42,7 +42,7 @@ func NewService(deps Dependencies) *Service { } // NewServiceWithWrites binds the additional secret execution ports while -// preserving the frozen Task 75 construction seam for ordinary callers. +// preserving the construction seam for ordinary callers. func NewServiceWithWrites(deps Dependencies, writes WriteDependencies) *Service { return newService(deps, writes) } @@ -61,8 +61,8 @@ func runtimeLayer() deployment.Layer { return layer } -// Add runs the full target-to-repository pipeline for one batch (PLAN.md -// Section 11.6): resolve the repository, compile the current platform, infer +// Add runs the full target-to-repository pipeline for one batch: resolve the +// repository, compile the current platform, infer // ownership, preflight the batch, and either report a dry-run plan or write // sources and baselines sequentially. func (service *Service) Add(ctx context.Context, request Request) (Result, error) { diff --git a/internal/application/add/types.go b/internal/application/add/types.go index 1422f67..154b547 100644 --- a/internal/application/add/types.go +++ b/internal/application/add/types.go @@ -1,4 +1,4 @@ -// Package add implements `cattery add` (PLAN.md Section 11.6): the sole +// Package add implements `cattery add`: the sole // target-to-repository content path. Each regular file beneath $HOME is // adopted as an ordinary or SOPS-encrypted source under the root scope or an // explicit group, and the repository recompiles to the same target. The diff --git a/internal/application/apply/decisions.go b/internal/application/apply/decisions.go index e2c3c18..a5c4f02 100644 --- a/internal/application/apply/decisions.go +++ b/internal/application/apply/decisions.go @@ -31,7 +31,7 @@ func (c CollectedDecisions) Specs() []reconcile.DecisionSpec { // CollectDecisions resolves every candidate that requires an explicit // decision, in bytewise target-path order, and validates each response -// before any hook or mutation (PLAN.md Section 11.5). An abort answer stops +// before any hook or mutation. An abort answer stops // the whole apply; a diff answer re-requests the adapter, which shows the // safe difference and asks again. func (service *Service) CollectDecisions(ctx context.Context, candidates Candidates) (CollectedDecisions, error) { diff --git a/internal/application/apply/dependencies.go b/internal/application/apply/dependencies.go index 2bc01e4..5f17e17 100644 --- a/internal/application/apply/dependencies.go +++ b/internal/application/apply/dependencies.go @@ -10,7 +10,7 @@ import ( // Preflight verifies the external dependencies the selected candidates // require: SOPS is probed only when a secret candidate needs on-demand -// decryption (PLAN.md Sections 9.1 and 11.5). No version probing, state +// decryption. No version probing, state // registration, prompt, hook, or mutation occurs. func (service *Service) Preflight(ctx context.Context, candidates Candidates) error { if err := ctx.Err(); err != nil { diff --git a/internal/application/apply/execute_aliases.go b/internal/application/apply/execute_aliases.go index 78e3e36..b58546f 100644 --- a/internal/application/apply/execute_aliases.go +++ b/internal/application/apply/execute_aliases.go @@ -15,7 +15,7 @@ import ( // ExecuteAliases runs the alias and retirement actions of one apply: each // alias is created or replaced, file-alias representation transitions // switch active state only after the durable write, and retirement tracks -// source removal without deleting targets (PLAN.md Section 11.5). +// source removal without deleting targets. func (service *Service) ExecuteAliases(ctx context.Context, plan PreparedPlan, candidates Candidates) ([]ItemResult, error) { if err := ctx.Err(); err != nil { return nil, err @@ -137,7 +137,7 @@ func aliasRecord(job aliasJob, status ItemStatus) ItemResult { } // aliasPayload derives the exact relative payload one alias link must -// carry, mirroring the routes activation contract (PLAN.md Section 5.4). +// carry, mirroring the routes activation contract. func aliasPayload(alias deployment.Alias) (string, error) { canonical, err := pathsafe.Segments(alias.CanonicalTargetRelativePath) if err != nil { diff --git a/internal/application/apply/execute_files.go b/internal/application/apply/execute_files.go index c9805cf..e83ac4c 100644 --- a/internal/application/apply/execute_files.go +++ b/internal/application/apply/execute_files.go @@ -13,7 +13,7 @@ import ( // ExecuteFiles runs the regular-file actions of one apply sequentially: // each target is re-frozen, written durably with its mode policy, and // baselined in a short state commit. A later failure preserves accurate -// earlier state and never imports target bytes (PLAN.md Section 11.5). +// earlier state and never imports target bytes. func (service *Service) ExecuteFiles(ctx context.Context, plan PreparedPlan, candidates Candidates) ([]ItemResult, error) { if err := ctx.Err(); err != nil { return nil, err diff --git a/internal/application/apply/hooks.go b/internal/application/apply/hooks.go index 4b29b38..fcafc85 100644 --- a/internal/application/apply/hooks.go +++ b/internal/application/apply/hooks.go @@ -25,7 +25,7 @@ type PipelineInput struct { // RunHookPipeline runs the hook-gated apply filesystem phase: before hooks // with CATTERY_RESULT=pending, the all-source guard and the file and alias // executors, then after hooks only when the phase completed, with -// CATTERY_RESULT=success or partial (PLAN.md Sections 10.4-10.5). A +// CATTERY_RESULT=success or partial. A // mid-filesystem operational failure skips every after hook, and after // failures never roll back completed writes. func (service *Service) RunHookPipeline(ctx context.Context, input PipelineInput) ([]ItemResult, error) { diff --git a/internal/application/apply/prepare.go b/internal/application/apply/prepare.go index 2c96da8..0cf484d 100644 --- a/internal/application/apply/prepare.go +++ b/internal/application/apply/prepare.go @@ -41,8 +41,8 @@ type PrepareInput struct { } // Prepare combines the resolved candidates and decisions into one immutable -// action plan with dry-run records and stable per-target kinds (PLAN.md -// Section 11.5). No hook or managed mutation occurs, and refusal paths +// action plan with dry-run records and stable per-target kinds. No hook or +// managed mutation occurs, and refusal paths // register nothing. func (service *Service) Prepare(ctx context.Context, input PrepareInput) (PreparedPlan, error) { if err := ctx.Err(); err != nil { diff --git a/internal/application/apply/service.go b/internal/application/apply/service.go index 8346921..a83a7a0 100644 --- a/internal/application/apply/service.go +++ b/internal/application/apply/service.go @@ -8,8 +8,8 @@ import ( // Apply performs one complete apply: evaluation, dependency preflight, // decision collection, plan preparation, the hook-gated filesystem phase -// with the all-source guard, and post-hook verification (PLAN.md Section -// 11.5). Dry runs return the planned records without any hook or write. +// with the all-source guard, and post-hook verification. Dry runs return the +// planned records without any hook or write. // The service contains no phase implementation; every step is a frozen // phase above. func (service *Service) Apply(ctx context.Context, request Request) (Result, error) { diff --git a/internal/application/apply/source_guard.go b/internal/application/apply/source_guard.go index 5092b68..8740f2b 100644 --- a/internal/application/apply/source_guard.go +++ b/internal/application/apply/source_guard.go @@ -9,8 +9,8 @@ import ( ) // Revalidate re-captures every selected source and target after before -// hooks and compares each against the evaluated facts (PLAN.md Section -// 11.5). Any mismatch stops the apply with zero executor or managed-row +// hooks and compares each against the evaluated facts. Any mismatch stops the +// apply with zero executor or managed-row // change. func (service *Service) Revalidate(ctx context.Context, candidates Candidates) error { if err := ctx.Err(); err != nil { diff --git a/internal/application/apply/types.go b/internal/application/apply/types.go index 51d815d..4722526 100644 --- a/internal/application/apply/types.go +++ b/internal/application/apply/types.go @@ -1,4 +1,4 @@ -// Package apply implements `cattery apply` (PLAN.md Section 11.5): the +// Package apply implements `cattery apply`: the // target-mutating deployment path with pre-decision collection, hook-gated // source revalidation, and per-target execution preconditions. The package // is Cobra-free; the CLI talks to the service through the frozen Request, diff --git a/internal/application/apply/verify.go b/internal/application/apply/verify.go index 6817a5e..c734c4c 100644 --- a/internal/application/apply/verify.go +++ b/internal/application/apply/verify.go @@ -10,7 +10,7 @@ import ( // Verify re-snapshots every selected source, target, and alias after hooks // and downgrades any record whose facts no longer match its source to -// partial (PLAN.md Section 11.5). Verification never rewrites drift and +// partial. Verification never rewrites drift and // performs no baseline or state commit; equality baselines were already // established per durable write. func (service *Service) Verify(ctx context.Context, records []ItemResult, candidates Candidates) ([]ItemResult, error) { diff --git a/internal/application/initialize/types.go b/internal/application/initialize/types.go index 858b2d1..01ca862 100644 --- a/internal/application/initialize/types.go +++ b/internal/application/initialize/types.go @@ -1,4 +1,4 @@ -// Package initialize implements `cattery init` (PLAN.md Section 11.1): it +// Package initialize implements `cattery init`: it // creates a missing repository directory, rejects repository/home/state // overlaps, and registers the canonical pair as the sole default of its home. // The package is Cobra-free: no CLI type appears here, and the CLI talks to diff --git a/internal/application/inspect/diff.go b/internal/application/inspect/diff.go index 46e70fb..47050c4 100644 --- a/internal/application/inspect/diff.go +++ b/internal/application/inspect/diff.go @@ -118,8 +118,8 @@ func (result DiffResult) Converged() bool { return result.converged } // Diff evaluates one request and translates the same evaluation as Status // into sorted safe diff/status records, counts, and convergence. A -// Difference failure accompanies the partial result whenever drift remains -// (PLAN.md Sections 9.6 and 11.4); no rendering, prompt, mutation, or +// Difference failure accompanies the partial result whenever drift remains; +// no rendering, prompt, mutation, or // second snapshot occurs. func (service *Service) Diff(ctx context.Context, request Request) (DiffResult, error) { evaluation, err := service.evaluate(ctx, request) diff --git a/internal/application/inspect/service.go b/internal/application/inspect/service.go index b962c08..b9736ff 100644 --- a/internal/application/inspect/service.go +++ b/internal/application/inspect/service.go @@ -25,8 +25,8 @@ func NewService(dependencies Dependencies) *Service { } // Evaluate performs one immutable selection, compile, snapshot, and -// classification evaluation with on-demand secret semantics (PLAN.md Section -// 9.1). No status/diff rendering, hook, prompt, registration, or mutation +// classification evaluation with on-demand secret semantics. No status/diff +// rendering, hook, prompt, registration, or mutation // occurs. func (service *Service) Evaluate(ctx context.Context, request Request) (Result, error) { return service.evaluate(ctx, request) diff --git a/internal/application/inspect/types.go b/internal/application/inspect/types.go index 5c13b46..682eab8 100644 --- a/internal/application/inspect/types.go +++ b/internal/application/inspect/types.go @@ -1,5 +1,5 @@ // Package inspect implements the `cattery status` and `cattery diff` -// evaluation pipeline (PLAN.md Sections 11.3 and 11.4): one immutable +// evaluation pipeline: one immutable // selection, compile, snapshot, and classification evaluation with on-demand // secret semantics. The package is Cobra-free: no CLI type appears here, and // the CLI talks to the service through the frozen Request and Result shapes diff --git a/internal/application/validate/types.go b/internal/application/validate/types.go index 7f767d2..fb92805 100644 --- a/internal/application/validate/types.go +++ b/internal/application/validate/types.go @@ -1,4 +1,4 @@ -// Package validate implements `cattery validate` (PLAN.md Section 11.2): it +// Package validate implements `cattery validate`: it // compiles and validates the full repository for Linux and Darwin, checks the // JSON storage shape of every secret, and reports deterministic counts of the // selected scopes. The package is Cobra-free: no CLI type appears here, and diff --git a/internal/application/version/types.go b/internal/application/version/types.go index 49c3eef..c6a9332 100644 --- a/internal/application/version/types.go +++ b/internal/application/version/types.go @@ -1,4 +1,4 @@ -// Package version implements `cattery version` (PLAN.md Section 11.7): it +// Package version implements `cattery version`: it // returns the linker-populated build identity and the current runtime // environment as typed fields. The package is Cobra-free: no CLI type // appears here, and the CLI talks to the service through the frozen Result diff --git a/internal/bootstrap/adapters.go b/internal/bootstrap/adapters.go index 7465b12..77f7b45 100644 --- a/internal/bootstrap/adapters.go +++ b/internal/bootstrap/adapters.go @@ -1,6 +1,6 @@ // Package bootstrap owns the composition root: lazy concrete adapters, -// per-application services, and the opaque CLI application (PLAN.md -// Section 12.1). Nothing here imports Cobra and nothing opens or probes +// per-application services, and the opaque CLI application. Nothing here +// imports Cobra and nothing opens or probes // backend resources at construction time. package bootstrap diff --git a/internal/cli/add.go b/internal/cli/add.go index caa8d99..0db17d8 100644 --- a/internal/cli/add.go +++ b/internal/cli/add.go @@ -15,7 +15,7 @@ type AddService interface { // newAddCommand declares the add syntax and mechanically maps the raw // targets, repository fields, and exact group/platform/secret presence -// bits into one add call (PLAN.md Section 11.6). No ownership inference or +// bits into one add call. No ownership inference or // filesystem access appears here. func newAddCommand(service AddService, runtime Runtime, options *Options) *cobra.Command { command := &cobra.Command{ diff --git a/internal/cli/apply.go b/internal/cli/apply.go index 14cbcd7..ce7310e 100644 --- a/internal/cli/apply.go +++ b/internal/cli/apply.go @@ -15,7 +15,7 @@ type ApplyService interface { // newApplyCommand declares the apply syntax and mechanically maps the raw // repository fields, group arguments, and dry-run/noninteractive/no-hooks -// policy into one apply call (PLAN.md Section 11.5). The service carries +// policy into one apply call. The service carries // the prompt resolver; no decision policy or hook order appears here. func newApplyCommand(service ApplyService, runtime Runtime, options *Options) *cobra.Command { command := &cobra.Command{ diff --git a/internal/cli/diff.go b/internal/cli/diff.go index 4e27f4a..62dd57c 100644 --- a/internal/cli/diff.go +++ b/internal/cli/diff.go @@ -14,8 +14,8 @@ type DiffService interface { } // newDiffCommand declares the diff syntax and mechanically maps the raw -// repository fields and group arguments into one diff call (PLAN.md -// Section 11.4). No diff calculation or formatter import appears here. +// repository fields and group arguments into one diff call. No diff calculation +// or formatter import appears here. func newDiffCommand(service DiffService, runtime Runtime, options *Options) *cobra.Command { command := &cobra.Command{ Use: "diff [GROUP ...]", diff --git a/internal/cli/init.go b/internal/cli/init.go index d14abba..90479f5 100644 --- a/internal/cli/init.go +++ b/internal/cli/init.go @@ -15,7 +15,7 @@ type InitializeService interface { // newInitCommand declares the init syntax and mechanically maps one raw // path or the injected working directory into a single initialize call -// (PLAN.md Section 11.1). No path resolution, registration, or backend +// No path resolution, registration, or backend // import appears here. func newInitCommand(service InitializeService, runtime Runtime) *cobra.Command { command := &cobra.Command{ diff --git a/internal/cli/prompt.go b/internal/cli/prompt.go index 8cd6a7b..de6da2f 100644 --- a/internal/cli/prompt.go +++ b/internal/cli/prompt.go @@ -23,7 +23,7 @@ type PromptInput struct { // DecisionPrompt is the interactive resolver of one decision request: it // renders the allowed choices, reads one answer, and maps it to a response, -// re-prompting on invalid input (PLAN.md Section 11.5). It imports only the +// re-prompting on invalid input. It imports only the // apply DTOs and failure categories; no Cobra command or backend adapter. type DecisionPrompt struct { stdin io.Reader diff --git a/internal/cli/render.go b/internal/cli/render.go index 53c405c..19666c2 100644 --- a/internal/cli/render.go +++ b/internal/cli/render.go @@ -12,7 +12,7 @@ import ( ) // renderAdd writes one line per item record and the summary line of one -// add result (PLAN.md Section 11.6). +// add result. func renderAdd(writer io.Writer, result add.Result) error { for _, item := range result.Items { if _, err := fmt.Fprintf(writer, "$HOME/%s %s %s\n", @@ -26,7 +26,7 @@ func renderAdd(writer io.Writer, result add.Result) error { } // renderApply writes one line per item record and the summary line of one -// apply result (PLAN.md Section 11.5). +// apply result. func renderApply(writer io.Writer, result apply.Result) error { for _, item := range result.Items { if _, err := fmt.Fprintf(writer, "$HOME/%s %s %s\n", @@ -40,7 +40,7 @@ func renderApply(writer io.Writer, result apply.Result) error { } // renderValidate writes the two deterministic platform count lines of one -// validate result (PLAN.md Section 11.2). +// validate result. func renderValidate(writer io.Writer, result validate.Result) error { for _, record := range result.Platforms { if _, err := fmt.Fprintf(writer, "%s files=%d secrets=%d aliases=%d groups=%d\n", @@ -52,7 +52,7 @@ func renderValidate(writer io.Writer, result validate.Result) error { } // renderStatus writes one line per pending record and the summary line of -// one status result (PLAN.md Sections 11.3 and 11.9). +// one status result. func renderStatus(writer io.Writer, result inspect.StatusResult) error { for _, record := range result.Records() { if _, err := fmt.Fprintf(writer, "$HOME/%s %s %s\n", @@ -66,7 +66,7 @@ func renderStatus(writer io.Writer, result inspect.StatusResult) error { } // renderDiff writes one line per tagged safe record plus the summary line -// of one diff result (PLAN.md Section 11.4). Secret records render the +// of one diff result. Secret records render the // marker only, with zero content, size, or hash fields. func renderDiff(writer io.Writer, result inspect.DiffResult) error { for _, record := range result.Records() { diff --git a/internal/cli/root.go b/internal/cli/root.go index 2ccd4d6..c7cbc46 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -8,7 +8,7 @@ import ( ) // Dependencies carries one explicitly named one-method service field per -// operational command (PLAN.md Section 12.1). +// operational command. type Dependencies struct { Initialize InitializeService Validate ValidateService diff --git a/internal/cli/runtime.go b/internal/cli/runtime.go index 0d76ba7..ad19361 100644 --- a/internal/cli/runtime.go +++ b/internal/cli/runtime.go @@ -1,5 +1,5 @@ -// Package cli implements the Cobra adapters of `cattery` (PLAN.md Section -// 11): every command mechanically maps raw values into one injected +// Package cli implements the Cobra adapters of `cattery`: every command +// mechanically maps raw values into one injected // application service call and renders its typed result. Cobra and x/term // stay confined to this package. package cli @@ -31,7 +31,7 @@ type RuntimeInput struct { // Runtime carries the injected process-boundary values of one application: // the streams, working directory, environment, terminal predicate, and the -// per-application verbosity callback (PLAN.md Section 12.1). Instances +// per-application verbosity callback. Instances // never share mutable state. type Runtime struct { stdin io.Reader diff --git a/internal/cli/status.go b/internal/cli/status.go index ab97475..9b8d5ab 100644 --- a/internal/cli/status.go +++ b/internal/cli/status.go @@ -14,8 +14,8 @@ type StatusService interface { } // newStatusCommand declares the status syntax and mechanically maps the -// raw repository fields and group arguments into one status call (PLAN.md -// Section 11.3). No classification or state import appears here. +// raw repository fields and group arguments into one status call. No +// classification or state import appears here. func newStatusCommand(service StatusService, runtime Runtime, options *Options) *cobra.Command { command := &cobra.Command{ Use: "status [GROUP ...]", diff --git a/internal/cli/validate.go b/internal/cli/validate.go index a7006ee..d3430d4 100644 --- a/internal/cli/validate.go +++ b/internal/cli/validate.go @@ -14,7 +14,7 @@ type ValidateService interface { // newValidateCommand declares the validate syntax and mechanically maps // the raw repository fields and group arguments into one validate call -// (PLAN.md Section 11.2). No group or repository semantics appear here. +// No group or repository semantics appear here. func newValidateCommand(service ValidateService, runtime Runtime, options *Options) *cobra.Command { command := &cobra.Command{ Use: "validate [GROUP ...]", diff --git a/internal/deployment/file.go b/internal/deployment/file.go index 5f029c3..c595d83 100644 --- a/internal/deployment/file.go +++ b/internal/deployment/file.go @@ -74,7 +74,7 @@ func validateFile(file ManagedFile) error { return nil } -// ExecutableBitMask is the POSIX executable-bit mask (PLAN.md Section 7.1). +// ExecutableBitMask is the POSIX executable-bit mask. // Source and target modes preserve read/write bits and reconcile only these bits. const ExecutableBitMask fs.FileMode = 0o111 diff --git a/internal/diff/safe.go b/internal/diff/safe.go index 547991d..e949489 100644 --- a/internal/diff/safe.go +++ b/internal/diff/safe.go @@ -1,4 +1,4 @@ -// Package diff owns the output-safe diff records of PLAN.md Section 9.6: +// Package diff owns the output-safe diff records: // tagged SafeRecord values carrying only precomputed printable unified-diff // lines, ordinary-file sizes and hashes, or secret classification. No ANSI // sequence, terminal width, writer, raw secret byte, or go-difflib type @@ -97,7 +97,7 @@ func NewSafeRecord(input SafeRecordInput) SafeRecord { // SafeRecord is one immutable, output-safe diff record for a destination. // Text records carry precomputed printable unified-diff lines, binary records // carry ordinary-file sizes and hashes, and secret records carry no payload -// at all (PLAN.md Sections 9.6 and 12.4). +// at all. type SafeRecord struct { targetPath string tag Tag @@ -125,7 +125,7 @@ func (r SafeRecord) TargetHash() deployment.Digest { } // maxTextBytes caps one diff side at 1 MiB; larger content is binary for -// output purposes (PLAN.md Section 9.6). +// output purposes. const maxTextBytes = 1 << 20 const unifiedContextLines = 3 @@ -153,8 +153,7 @@ func textEligible(data []byte) bool { // must be the exact bytes captured beside the target snapshot; the record // never retains them. Equal content yields TagNone, printable text at most // maxTextBytes per side yields a TagText unified diff with escaped labels, -// and every other content difference yields TagBinary or TagSecret facts -// (PLAN.md Section 9.6). +// and every other content difference yields TagBinary or TagSecret facts. func Build(evaluation reconcile.Evaluation, targetBytes []byte) (SafeRecord, error) { if evaluation.Entry != reconcile.PlanEntryFile { return SafeRecord{}, fmt.Errorf("diff: record requires a file plan entry at %q", evaluation.TargetPath) diff --git a/internal/diff/safe_test.go b/internal/diff/safe_test.go index 23df3ea..8a0b95a 100644 --- a/internal/diff/safe_test.go +++ b/internal/diff/safe_test.go @@ -14,7 +14,7 @@ import ( const textLimit = 1 << 20 -// TestSafeDiffRecord pins the PLAN.md Section 9.6 record contract: the four +// TestSafeDiffRecord pins the record contract: the four // tagged variants for printable text, binary/large ordinary files, // metadata-only changes, and secrets, with control/bidi/invalid-UTF-8 runes // demoted to binary, escaped labels, verified sizes and hashes, and zero diff --git a/internal/filesystem/alias.go b/internal/filesystem/alias.go index f0b9332..ca62e17 100644 --- a/internal/filesystem/alias.go +++ b/internal/filesystem/alias.go @@ -9,7 +9,7 @@ import ( ) // AliasSpec is the desired relative payload of one alias and whether the -// caller has confirmed replacing an occupied path (PLAN.md Section 5.4). +// caller has confirmed replacing an occupied path. type AliasSpec struct { Payload string Overwrite bool @@ -126,7 +126,7 @@ func (r *Replacer) replaceOccupied(ctx context.Context, precondition Preconditio // commitAlias prepares a uniquely named relative symlink, revalidates the // destination, renames it into place, and makes the parent directory // durable. Only the rename or a barrier failure can publish a partial -// result (PLAN.md Section 7.2 steps 10-11). +// result. func (r *Replacer) commitAlias(ctx context.Context, precondition Precondition, payload string) error { if err := r.prepareAliasParent(precondition); err != nil { return err diff --git a/internal/filesystem/mode.go b/internal/filesystem/mode.go index 899c32e..e8ea9e5 100644 --- a/internal/filesystem/mode.go +++ b/internal/filesystem/mode.go @@ -19,7 +19,7 @@ const ordinaryReadWriteBits fs.FileMode = 0o666 // OrdinaryTargetMode derives the mode for an ordinary target: read/write // bits are preserved from an existing entry or default to 0644 for a new -// one, and executable bits always come from the source (PLAN.md Section 4.6). +// one, and executable bits always come from the source. func OrdinaryTargetMode(existing fs.FileMode, sourceExec fs.FileMode, absent bool) fs.FileMode { if absent { return ordinaryNewFileMode | sourceExec @@ -28,7 +28,7 @@ func OrdinaryTargetMode(existing fs.FileMode, sourceExec fs.FileMode, absent boo } // SecretTargetMode derives the exact secret mode: 0600, or 0700 when the -// encrypted source is executable (PLAN.md Section 4.5). The policy itself +// encrypted source is executable. The policy itself // lives in deployment so file and reconcile layers cannot drift apart. func SecretTargetMode(sourceExec fs.FileMode) fs.FileMode { return deployment.SecretTargetMode(sourceExec) diff --git a/internal/filesystem/precondition.go b/internal/filesystem/precondition.go index 1d62ca6..54a86d7 100644 --- a/internal/filesystem/precondition.go +++ b/internal/filesystem/precondition.go @@ -1,6 +1,6 @@ // Package filesystem owns the atomic same-directory mutation primitives that // deploy files and aliases without ever publishing a partially written entry -// (PLAN.md Section 7): freeze, rewrite, revalidate, rename, directory sync. +// freeze, rewrite, revalidate, rename, directory sync. package filesystem import ( diff --git a/internal/filesystem/replace.go b/internal/filesystem/replace.go index c34a339..deea2b3 100644 --- a/internal/filesystem/replace.go +++ b/internal/filesystem/replace.go @@ -50,8 +50,7 @@ type temporaryFile struct { // prepare writes the exact validated bytes once, applies the final mode, // commits the entry (sync then close), and revalidates the on-disk result so -// no renamed entry can carry wrong bytes or mode (PLAN.md Section 7.2 steps -// 5-9). +// no renamed entry can carry wrong bytes or mode. func (file *temporaryFile) prepare(ctx context.Context, spec ReplacementSpec) error { if _, err := file.temp.Write(spec.Content); err != nil { return fmt.Errorf("filesystem: write temporary file: %w", err) @@ -110,7 +109,7 @@ func NewReplacer() *Replacer { } } -// Replace performs the atomic replacement (PLAN.md Section 7.2): the exact +// Replace performs the atomic replacement: the exact // validated bytes are written once to a same-directory temporary entry, // committed, revalidated, and renamed over the target, then the parent // directory is made durable. Failure before the rename leaves the old target diff --git a/internal/filesystem/sync.go b/internal/filesystem/sync.go index 3343caf..c31a437 100644 --- a/internal/filesystem/sync.go +++ b/internal/filesystem/sync.go @@ -27,7 +27,7 @@ type SyncResult struct { // CommitFile makes a still-open temporary file durable: sync while open so // bytes and final mode precede the barrier, then close. It always closes so // no descriptor leaks; a sync failure is reported because the write is not -// durable (PLAN.md Section 7.2 steps 7-8). +// durable. func CommitFile(ctx context.Context, handle SyncHandle) error { if err := ctx.Err(); err != nil { _ = handle.Close() @@ -45,8 +45,7 @@ func CommitFile(ctx context.Context, handle SyncHandle) error { // SyncError reports a failed directory durability barrier. Unsupported is // true when the filesystem refused directory sync itself, which callers -// report as a partial operation rather than a racing mutation -// (PLAN.md Section 7.2 step 11). +// report as a partial operation rather than a racing mutation. type SyncError struct { Result SyncResult Unsupported bool diff --git a/internal/pathsafe/ancestor.go b/internal/pathsafe/ancestor.go index 4735cd3..7600bdc 100644 --- a/internal/pathsafe/ancestor.go +++ b/internal/pathsafe/ancestor.go @@ -6,7 +6,7 @@ import ( ) // AncestorWalk validates that every existing component from root through the -// parent of relativePath is a real directory (PLAN.md Section 6.2). It uses +// parent of relativePath is a real directory. It uses // os.Lstat so a symlinked parent component is rejected even when it resolves // beneath the same root. The walk is read-only: no path is created. // diff --git a/internal/pathsafe/equivalence.go b/internal/pathsafe/equivalence.go index 3d9a30f..b15c398 100644 --- a/internal/pathsafe/equivalence.go +++ b/internal/pathsafe/equivalence.go @@ -7,7 +7,7 @@ import ( ) // SegmentsEquivalent reports whether two single path segments are portably -// equivalent for repository portability (PLAN.md Section 6.3). Each segment is +// equivalent for repository portability. Each segment is // normalized to Unicode NFC and then compared with strings.EqualFold. This // deliberately treats case-only and NFC/NFD distinctions as collisions even on // a host filesystem that could store both, preventing common APFS aliases from diff --git a/internal/pathsafe/path.go b/internal/pathsafe/path.go index f0735c6..cb43f7f 100644 --- a/internal/pathsafe/path.go +++ b/internal/pathsafe/path.go @@ -1,7 +1,7 @@ // Package pathsafe owns the lexical, canonical, and portable-equivalence // checks that keep every Cattery destination inside its allowed root. The -// rules come from PLAN.md Section 6: lexical validation (6.1), filesystem -// containment (6.2), and deployment collisions (6.3). +// rules cover lexical validation, filesystem containment, and deployment +// collisions. // // Validation never silently rewrites unsafe input. A rejected path is reported // verbatim alongside the reason it was refused, so a caller can surface the diff --git a/internal/pathsafe/protected.go b/internal/pathsafe/protected.go index 8960e93..a2e1ae1 100644 --- a/internal/pathsafe/protected.go +++ b/internal/pathsafe/protected.go @@ -7,8 +7,8 @@ import ( ) // ProtectedTree reports whether target equals or descends into the protected -// tree, checking the relation in both directions (PLAN.md Sections 6.1 and -// 6.3). Each relation is evaluated twice: once with canonical native absolute +// tree, checking the relation in both directions. Each relation is evaluated +// twice: once with canonical native absolute // path segments compared by string equality, and once with the portable // NFC-plus-EqualFold segment equivalence. The trees collide when either // comparison overlaps, so case-only or NFC/NFD aliases are rejected even on a diff --git a/internal/pathsafe/root.go b/internal/pathsafe/root.go index 5764f93..d4eca47 100644 --- a/internal/pathsafe/root.go +++ b/internal/pathsafe/root.go @@ -6,8 +6,8 @@ import ( "path/filepath" ) -// CanonicalRoot resolves path to its canonical absolute form (PLAN.md Section -// 6.2). Existing components are resolved with filepath.EvalSymlinks so symlinked +// CanonicalRoot resolves path to its canonical absolute form. Existing +// components are resolved with filepath.EvalSymlinks so symlinked // ancestors collapse to their real targets. When trailing components do not yet // exist, the nearest existing ancestor is resolved canonically and the missing // suffix is appended after validating each suffix segment. diff --git a/internal/reconcile/classify_alias.go b/internal/reconcile/classify_alias.go index 0629481..f86fd60 100644 --- a/internal/reconcile/classify_alias.go +++ b/internal/reconcile/classify_alias.go @@ -18,8 +18,8 @@ type AliasClassification struct { Convergence Convergence } -// ClassifyAlias purely classifies one alias or representation evaluation -// (PLAN.md Sections 5.4 and 9.5). A plan alias classifies against its exact +// ClassifyAlias purely classifies one alias or representation evaluation. A +// plan alias classifies against its exact // relative payload; a plan alias over an active file row, or a plan file over // an active alias row, classifies the representation transition. The current // target semantic fingerprint arrives precomputed because a secret file row @@ -95,7 +95,7 @@ func classifyAliasToFile(record Evaluation) AliasClassification { // representationIntact reports whether the current regular target provably // matches the retained file row: the baseline semantic fingerprint plus the // managed mode, which is executable bits for ordinary files and the exact -// forced 0600/0700 mode for secrets (PLAN.md Section 9.5). +// forced 0600/0700 mode for secrets. func representationIntact(record Evaluation, semantics FileSemantics) bool { row := record.FileState if record.Target.Kind() != KindFile || semantics.Target != row.BaselineContent() { @@ -130,7 +130,7 @@ func payloadFor(canonical, alias string) string { // relativePayload computes the exact relative symlink payload from the alias // destination's parent directory to the canonical target, mirroring the -// route-activation derivation of PLAN.md Section 5.4 without importing it. +// route-activation derivation without importing it. func relativePayload(canonical, alias string) (string, error) { canonicalSegments, err := pathsafe.Segments(canonical) if err != nil { diff --git a/internal/reconcile/classify_alias_test.go b/internal/reconcile/classify_alias_test.go index 5723126..039e78e 100644 --- a/internal/reconcile/classify_alias_test.go +++ b/internal/reconcile/classify_alias_test.go @@ -17,7 +17,6 @@ const ( // representation combination: fresh, baselined, and reactivated alias rows, // intact and drifted file-to-alias and alias-to-file transitions, unexpected // target types, and the records the alias classifier must leave untouched -// (PLAN.md Sections 5.4 and 9.5). func TestAliasClassification(t *testing.T) { for _, row := range aliasClassificationCases { t.Run(row.name, func(t *testing.T) { diff --git a/internal/reconcile/classify_file.go b/internal/reconcile/classify_file.go index fd783b2..e073044 100644 --- a/internal/reconcile/classify_file.go +++ b/internal/reconcile/classify_file.go @@ -6,7 +6,7 @@ import ( // FileSemantics carries the current semantic fingerprints of one file // evaluation: unkeyed BLAKE3 digests for ordinary content, keyed digests for -// secrets, computed by the caller on demand (PLAN.md Sections 8.3 and 9.1). +// secrets, computed by the caller on demand. // The classifier never decrypts and never touches the filesystem. type FileSemantics struct { Source deployment.Digest @@ -23,8 +23,8 @@ type FileClassification struct { } // ClassifyFile purely classifies one complete file evaluation against its -// persisted baseline: the five core matrix rows of PLAN.md Section 9.2, the -// unbaselined safety rows of Section 9.3, independent executable-bit +// persisted baseline: the five core matrix rows, the unbaselined safety rows, +// independent executable-bit // reconciliation per Section 7.1, and unexpected target types per Section // 7.3. A retired row with a current producer reconciles against its retained // baseline (Section 9.5 reactivation). Records without a file producer @@ -45,7 +45,7 @@ func ClassifyFile(record Evaluation, semantics FileSemantics) FileClassification // classifyUnexpectedType classifies a symlink, directory, or special entry // at a regular-file target: never written through, never replaced -// automatically (PLAN.md Section 7.3). A symlink is drift subject to an +// automatically. A symlink is drift subject to an // explicit decision; directories and special entries require manual // intervention and are rejected. func classifyUnexpectedType(record Evaluation) FileClassification { @@ -56,8 +56,8 @@ func classifyUnexpectedType(record Evaluation) FileClassification { return withPath(base, record) } -// classifyUnbaselined classifies the database-loss rows of PLAN.md Section -// 9.3: create from source, adopt equal content as an operational baseline, +// classifyUnbaselined classifies the database-loss rows: create from source, +// adopt equal content as an operational baseline, // or require an explicit decision for differing content. func classifyUnbaselined(record Evaluation, semantics FileSemantics) FileClassification { if record.Target.Kind() != KindFile { @@ -69,7 +69,7 @@ func classifyUnbaselined(record Evaluation, semantics FileSemantics) FileClassif return withPath(applyModeCorrection(outcome(ActionEstablishBaseline, ReasonUnbaselinedEqual, ConvergenceConverged), record), record) } -// classifyBaselined maps the five core matrix rows of PLAN.md Section 9.2. +// classifyBaselined maps the five core matrix rows. // For a secret source, raw storage equality with the baseline source proves // the source unchanged without any semantic digest; raw change is a semantic // source change only when the keyed plaintext digest also differs, so a @@ -97,8 +97,7 @@ func classifyBaselined(record Evaluation, row *FileState, semantics FileSemantic // applyModeCorrection upgrades a content-converged classification into a // mode-only correction when the target's executable bits differ from the // source's: automatic in both directions and independent of content drift -// (PLAN.md Section 7.1), with exact 0600/0700 forced for secrets per -// Section 4.5. +// drift, with exact 0600/0700 forced for secrets. func applyModeCorrection(candidate FileClassification, record Evaluation) FileClassification { if candidate.Action != ActionNoOp && candidate.Action != ActionEstablishBaseline { return candidate diff --git a/internal/reconcile/classify_file_test.go b/internal/reconcile/classify_file_test.go index 30933a0..7aa3d32 100644 --- a/internal/reconcile/classify_file_test.go +++ b/internal/reconcile/classify_file_test.go @@ -9,7 +9,7 @@ import ( // TestFileClassification exhaustively classifies the complete // source/target/baseline matrix for regular files, modes, and unbaselined -// safety (PLAN.md Sections 7.1, 7.3, 9.2, and 9.3), including database loss +// safety, including database loss // and source-only and target-only changes. func TestFileClassification(t *testing.T) { for _, row := range fileClassificationCases { diff --git a/internal/reconcile/classify_retirement.go b/internal/reconcile/classify_retirement.go index 5ede6bd..9108651 100644 --- a/internal/reconcile/classify_retirement.go +++ b/internal/reconcile/classify_retirement.go @@ -12,7 +12,7 @@ type RetirementClassification struct { } // ClassifyRetirement purely classifies one state-only evaluation record -// (PLAN.md Sections 8.5 and 9.5): an active row whose target has no producer +// An active row whose target has no producer // anywhere in the complete current platform plan retires tracking only, never // the target. Rows on an inactive platform layer stay active for this // platform; rows already retired stay put. Records with a plan producer or diff --git a/internal/reconcile/classify_retirement_test.go b/internal/reconcile/classify_retirement_test.go index e7153ab..27944a4 100644 --- a/internal/reconcile/classify_retirement_test.go +++ b/internal/reconcile/classify_retirement_test.go @@ -10,8 +10,8 @@ import ( // TestRetirementClassification classifies every state-only row shape: source // removal of files and aliases, whole deleted scopes, cross-scope ownership // moves that preserve the baseline, inactive platform layers, and rows that -// are already retired, without any target action (PLAN.md Sections 8.5 and -// 9.5). Snapshot-level scenarios verify complete-plan producer checks and +// are already retired, without any target action. Snapshot-level scenarios +// verify complete-plan producer checks and // selected state subsets. func TestRetirementClassification(t *testing.T) { scenarios := []struct { diff --git a/internal/reconcile/decisions.go b/internal/reconcile/decisions.go index 9ebe44a..7c121fc 100644 --- a/internal/reconcile/decisions.go +++ b/internal/reconcile/decisions.go @@ -10,8 +10,7 @@ import ( // decisionReason reports whether reason names a row that requires an // explicit user decision: file drift, unbaselined mismatch, and conflict // rows never apply silently, and unexpected target types, alias occupation, -// and representation transitions always prompt (PLAN.md Sections 9.2, 9.3, -// and 9.5). +// and representation transitions always prompt. func decisionReason(reason Reason) bool { switch reason { case ReasonTargetDrift, ReasonConflict, ReasonUnbaselinedDiffer, ReasonUnexpectedTargetType, @@ -23,8 +22,7 @@ func decisionReason(reason Reason) bool { // diffEligible reports whether a decision may compare source and target // bytes: only the ordinary file drift rows do. Secrets never expose a diff -// prompt, and an unexpected-type symlink target has no bytes to compare -// (PLAN.md Sections 9.4 and 9.6). +// prompt, and an unexpected-type symlink target has no bytes to compare. func diffEligible(reason Reason, kind deployment.FileKind) bool { if kind != deployment.FileOrdinary { return false @@ -55,7 +53,7 @@ func AllowedChoices(action Action, reason Reason, kind deployment.FileKind) []De // decision into an immutable spec carrying exactly the choices allowed for // its action, reason, and source kind. Classifications that do not require // a decision are rejected: converged, pending, and rejected outcomes never -// prompt (PLAN.md Sections 9.2 and 9.3). +// prompt. func DecisionSpecForFile(classification FileClassification, kind deployment.FileKind) (DecisionSpec, error) { if classification.Convergence != ConvergenceDecisionRequired { return DecisionSpec{}, fmt.Errorf("reconcile: file %q does not require a decision", classification.TargetPath) @@ -69,8 +67,8 @@ func DecisionSpecForFile(classification FileClassification, kind deployment.File // DecisionSpecForAlias freezes one alias or representation classification // that requires a decision into an immutable spec. Alias prompts never offer -// diff: an occupied path or drifted representation compares no target bytes -// (PLAN.md Sections 5.4 and 9.5). Alias reasons never qualify for diff, so +// diff: an occupied path or drifted representation compares no target bytes. +// Alias reasons never qualify for diff, so // the kind argument is irrelevant to the eligibility call. func DecisionSpecForAlias(classification AliasClassification) (DecisionSpec, error) { if classification.Convergence != ConvergenceDecisionRequired { @@ -85,7 +83,7 @@ func DecisionSpecForAlias(classification AliasClassification) (DecisionSpec, err // ValidateDecisionSpec rejects any spec whose action and reason cannot // prompt or whose choices are not exactly the allowed set for its action, -// reason, and source kind (PLAN.md Section 9.4). +// reason, and source kind. func ValidateDecisionSpec(spec DecisionSpec, kind deployment.FileKind) error { allowed := AllowedChoices(spec.Action(), spec.Reason(), kind) if len(allowed) == 0 { @@ -112,8 +110,7 @@ func equalChoices(actual, allowed []DecisionChoice) bool { } // OrderedDecisionSpecs returns a defensive copy of the specs sorted bytewise -// by target path, so prompts always follow normalized target-path order -// (PLAN.md Section 9.4). +// by target path, so prompts always follow normalized target-path order. func OrderedDecisionSpecs(specs []DecisionSpec) []DecisionSpec { ordered := append([]DecisionSpec(nil), specs...) sort.SliceStable(ordered, func(first, second int) bool { diff --git a/internal/reconcile/decisions_test.go b/internal/reconcile/decisions_test.go index 9256af8..84a2e5a 100644 --- a/internal/reconcile/decisions_test.go +++ b/internal/reconcile/decisions_test.go @@ -17,8 +17,8 @@ var ( diffOverwriteSkipAbort = []DecisionChoice{ChoiceDiff, ChoiceOverwrite, ChoiceSkip, ChoiceAbort} ) -// TestDecisionSpecification pins the decision-spec contract of PLAN.md -// Section 9.4: overwrite/skip/abort/diff eligibility by action, reason, and +// TestDecisionSpecification pins the decision-spec contract: +// overwrite/skip/abort/diff eligibility by action, reason, and // source kind, bytewise target-path ordering of produced specs, and // rejection of specs whose choices are not exactly the allowed set. func TestDecisionSpecification(t *testing.T) { diff --git a/internal/reconcile/types.go b/internal/reconcile/types.go index 3d68c50..7c630cb 100644 --- a/internal/reconcile/types.go +++ b/internal/reconcile/types.go @@ -1,5 +1,5 @@ // Package reconcile owns the immutable evaluation records and precondition -// vocabulary of the snapshot pipeline (PLAN.md Sections 9 and 12.4); no +// vocabulary of the snapshot pipeline; no // provider-owned interface lives in this package. package reconcile @@ -181,7 +181,7 @@ func (snapshot SourceSnapshot) Storage() deployment.Digest { return snapshot.sto func (snapshot SourceSnapshot) Executable() fs.FileMode { return snapshot.executable } // TargetSnapshot freezes the immutable facts of one destination observation -// and doubles as the immutable target precondition (PLAN.md Section 12.4). +// and doubles as the immutable target precondition. type TargetSnapshot struct { destination Destination parent pathsafe.Identity diff --git a/internal/repository/collisions.go b/internal/repository/collisions.go index 22148c5..8f5e52f 100644 --- a/internal/repository/collisions.go +++ b/internal/repository/collisions.go @@ -178,7 +178,7 @@ func aliasPairError(first, second deployment.Alias) error { } // protectedTreeCollisions rejects file targets and alias destinations that -// equal or descend into a protected tree beneath HOME (PLAN.md Section 6.1). +// equal or descend into a protected tree beneath HOME. func protectedTreeCollisions(files []deployment.ManagedFile, aliases []deployment.Alias, scope CollisionScope) error { if scope.HomeRoot == "" { return nil diff --git a/internal/routes/activate.go b/internal/routes/activate.go index 89ef884..60d94db 100644 --- a/internal/routes/activate.go +++ b/internal/routes/activate.go @@ -89,7 +89,7 @@ func canonicalSet(canonical []string) map[string]bool { } // AliasPayload computes the exact relative symlink payload for the alias at -// destination pointing at canonical (PLAN.md Section 5.4): the payload is +// destination pointing at canonical: the payload is // relative from the alias destination's parent directory, never absolute, // and never needs to climb above the home root. Both paths must be valid // HOME-relative paths. diff --git a/internal/selection/repository.go b/internal/selection/repository.go index 164c4ce..a121c2b 100644 --- a/internal/selection/repository.go +++ b/internal/selection/repository.go @@ -1,5 +1,5 @@ // Package selection owns repository and group resolution for the -// repository-using commands (PLAN.md Section 8.2): explicit --repo path and +// repository-using commands: explicit --repo path and // presence, raw CATTERY_REPO and presence, then the default repository of // the canonical home. Resolution never registers a repository and never // imports CLI concepts. diff --git a/internal/state/database.go b/internal/state/database.go index cfad549..cdfdfd8 100644 --- a/internal/state/database.go +++ b/internal/state/database.go @@ -16,7 +16,7 @@ import ( ) // Filesystem placement and required modes for the Cattery state directory -// (PLAN.md Section 8.1). The directory is private to the owning user; the +// The directory is private to the owning user; the // database and lock files are read-write but never searchable by others. const ( catteryDirectoryName = "cattery" diff --git a/internal/state/files.go b/internal/state/files.go index b78487f..425507a 100644 --- a/internal/state/files.go +++ b/internal/state/files.go @@ -38,7 +38,7 @@ FROM files f JOIN repositories r ON r.id = f.repository_id WHERE r.root_path = ? AND r.home_path = ? ORDER BY f.target_path` // dualActiveByPairSQL lists paths active in both representations of a pair, -// which the schema cannot express as a constraint (PLAN.md Section 8.4). +// which the schema cannot express as a constraint. const dualActiveByPairSQL = ` SELECT f.target_path FROM files f JOIN repositories r ON r.id = f.repository_id @@ -65,7 +65,7 @@ type fileBatch struct { // UpsertFileBaseline registers the canonical pair if needed and upserts one // active file row in a short transaction, stamping applied_at from the clock. // For a secret row it also commits the hash-key identifier in the same -// transaction, per PLAN.md Section 8.1. +// transaction. func (store *Store) UpsertFileBaseline(root, home string, baseline FileBaseline) (FileBaseline, error) { root, home, err := prepareFileBaseline(root, home, baseline) if err != nil { diff --git a/internal/state/keyfile.go b/internal/state/keyfile.go index b4f05dd..d38ed6b 100644 --- a/internal/state/keyfile.go +++ b/internal/state/keyfile.go @@ -10,7 +10,7 @@ import ( ) // stateKeyFileName is the 32-byte keyed-hash secret beside the database -// (PLAN.md Section 8.1). It is created on demand, never during Acquire. +// It is created on demand, never during Acquire. const stateKeyFileName = "hash.key" // keyByteLength is the fixed size of the keyed-hash secret. diff --git a/internal/state/keyid.go b/internal/state/keyid.go index 8fa288b..43dc02e 100644 --- a/internal/state/keyid.go +++ b/internal/state/keyid.go @@ -9,7 +9,7 @@ import ( ) // hashKeyIDMetadataKey names the metadata row holding the derived identifier -// of hash.key (PLAN.md Section 8.1), so replacement can be detected without +// of hash.key, so replacement can be detected without // storing the key itself. const hashKeyIDMetadataKey = "hash_key_id" diff --git a/internal/state/recovery.go b/internal/state/recovery.go index 566605a..593309b 100644 --- a/internal/state/recovery.go +++ b/internal/state/recovery.go @@ -11,7 +11,7 @@ import ( const secretBaselinesSQL = "SELECT COUNT(*) FROM files WHERE source_kind = 'secret'" // RecoverHashKey guarantees a usable 32-byte hash key exists and matches its -// committed identifier (PLAN.md Section 8.1), returning the key. It fails +// committed identifier, returning the key. It fails // safely whenever the stored key cannot be proven correct: it never guesses, // and it never silently replaces a key that old baselines depend on. func (store *Store) RecoverHashKey() ([32]byte, error) { diff --git a/internal/state/transitions.go b/internal/state/transitions.go index 564647d..a7dc17e 100644 --- a/internal/state/transitions.go +++ b/internal/state/transitions.go @@ -50,7 +50,7 @@ type fileTransition struct { } // TransitionToAlias replaces one active file row with an active alias row at -// the same target path in a single transaction, per PLAN.md Section 9.5. +// the same target path in a single transaction. func (store *Store) TransitionToAlias(root, home string, baseline AliasBaseline) (AliasBaseline, error) { root, home, err := prepareAliasBaseline(root, home, baseline) if err != nil { @@ -92,7 +92,7 @@ func (store *Store) applyAliasTransition(transaction *sql.Tx, transition aliasTr } // TransitionToFile replaces one active alias row with an active file row at -// the same target path in a single transaction, per PLAN.md Section 9.5. +// the same target path in a single transaction. func (store *Store) TransitionToFile(root, home string, baseline FileBaseline) (FileBaseline, error) { root, home, err := prepareFileBaseline(root, home, baseline) if err != nil { diff --git a/internal/testfixture/database/store.go b/internal/testfixture/database/store.go index b7a3ae7..113cab7 100644 --- a/internal/testfixture/database/store.go +++ b/internal/testfixture/database/store.go @@ -14,7 +14,7 @@ import ( "github.com/alyraffauf/cattery/internal/state" ) -// Directory and file names mirror PLAN.md Section 8.1. The fixture duplicates +// Directory and file names mirror the state layout. The fixture duplicates // them so tests can reach the concrete state paths without widening state's // public surface. const ( diff --git a/justfile b/justfile index e2e4943..ee571ed 100644 --- a/justfile +++ b/justfile @@ -1,4 +1,4 @@ -# Cattery development recipes. Frozen by Task 4; no later card edits this file. +# Cattery development recipes. # default: show available recipes. default: -- 2.51.2