Plan the compatibility purge

This commit is contained in:
2026-09-02 03:53:36 +00:00
parent d67a0389e0
commit 9679117e94

View File

@@ -1,671 +1,416 @@
# Workstream 2 Implementation Plan # Workstream 3 Implementation Plan
## Purpose And Status ## Purpose And Status
This document is the executable implementation plan for This is the executable plan for [`refactoring.md`](refactoring.md), Workstream
[`refactoring.md`](refactoring.md), Workstream 2: **Filesystem simplification 3: **Canonical contracts and compatibility purge**. Finding 1 of
and restore correctness**. The accepted [`File Operations Simplification`](fileops.md) [`contract-audit.md`](contract-audit.md) and rows C01/H43 of
roadmap owns the filesystem threat model, policy choices, compatibility intent, [`audit-decisions.md`](audit-decisions.md) own the approved decisions.
and target state. The [`Audit Decision Register`](audit-decisions.md) records
the controlling audit dispositions. This plan translates that settled direction
into eight bounded stages suitable for one `gpt-5.6-terra` coding prompt apiece.
All stages are pending and must be implemented in numeric order. Workstream 1 All seven stages are pending and must be implemented in order. Each is bounded
is complete. Workstreams 3 and later are out of scope: in particular, do not for one `gpt-5.6-terra` prompt. Workstreams 1 and 2 are complete. Workstreams
purge deprecated state/configuration readers, eliminate run manifests, change 4 and later are out of scope: do not remove the run manifest, redesign runner
analyze checkpointing, decompose publish, or add the final cleanup stage while lifecycle, change analyze checkpoint timing, replace locks, decompose publish,
performing this plan. or add cleanup while following this plan.
## Settled Implementation Decisions ## Settled Implementation Decisions
The following decisions are final for this implementation: - Delete deprecated representations rather than renaming, wrapping, or
preserving them behind an internal compatibility facade. Historical releases
- Narratio operates in a trusted, session-locked, group-owned workspace. It and completed audits remain unchanged.
does not defend ordinary filesystem access against a hostile concurrent - Give both currently persisted local JSON manifest types an explicit required
mutator. API-key material remains sensitive, and destructive cleanup and `schema_version`: `narratio.session-manifest.v1` for `Manifest` and
restore remain explicitly root-scoped. `narratio.run-manifest.v1` for `RunManifest`, with discoverable constants
- Use only the Go 1.26 standard library for the filesystem simplification. Do in package `manifest`. Constructors set it; save and load validate it;
not add a filesystem, atomic-write, copy, or path-safety dependency. Keep the neither path fills in a missing or unsupported value. Workstream 4 may later
retained helpers internal to Narratio; the library audit explicitly rejected delete the versioned run-manifest model.
extraction of FileOps. - Loaded current state may not acquire required schema, stage identity, stage
- Preserve package ownership and the existing acyclic dependency direction. records, or analyze authority through normalization. Representation-
Ordinary `os`, `io`, and `path/filepath` operations belong beside their domain preserving normalization may remain.
callers. `internal/fileops` may retain only shared product policy: workspace - Every newly created `analyze` stage record in either manifest uses
modes, minimal same-directory atomic file replacement, bounded reads, mode `AnalyzeStateContractVersion`. An empty collection is valid current state.
normalization for an externally produced tree, and root-scoped deletion. Version zero, an unknown version, or aggregate-only authority is rejected.
- Retain `fileops.WorkspaceDirectoryMode` (`02775` on POSIX) and - The only party input is a strict `narratio.party.v1` document selected by
`fileops.WorkspaceFileMode` (`0664` on POSIX) as the single discoverable mode required, campaign-owned `campaign.inputs.party_file`. Remove unversioned
definitions. `EnsureWorkspaceDirectory` must create missing components and parties, `campaign.inputs.players_file`, `session.inputs.players_file`,
explicitly normalize every component it creates plus the requested directory, and `session.inputs.party_file`; strict YAML decoding rejects those keys.
so a restrictive umask cannot defeat group operation. It must not change - Players remain the deterministic `narratio.players.v1` projection generated
ownership. POSIX mode bits remain non-authoritative on Windows. from the party. Keep that prepared input, semantic identity, Notarius
- Retain atomic visibility for corruption-sensitive single files. The minimal reference role, and operator visibility; it is never separately configured.
replacement sequence is: require an existing destination parent, create a - Simplify party runtime data to a canonical party and source provenance.
sibling temporary file, stream/write bytes, apply the final mode, close, Remove `PartyMode`, both mode constants, the compatibility-only
perform any caller-supplied pre-rename cancellation check, and replace with `PartyDocument` shell, and `party_legacy.go`. `ParseParty` returns the
`os.Rename`. Remove the temporary file on every pre-commit failure. Do not canonical representation directly.
call `File.Sync`, sync a directory, probe sync capabilities, preserve - The only remote current selector is versioned `current/commit.json` and its
`ErrDirectorySyncUnsupported`, or claim survival across power loss. verified immutable commit. Pointer absence returns a typed
- Preserve the useful atomic replacement entry points used by multiple domains: current-commit-pointer-missing error with publish/republish guidance. Do not
byte replacement, write-with-parent-creation, reader/copy replacement with a inspect `current/run_id.txt` or `current/manifest.json`.
SHA-256 result, and validated sibling-temp installation. Rename or consolidate - Successful current-state loads always contain a validated non-nil pointer,
them where that makes ownership clearer, but do not retain two implementations commit, and committed session manifest. Keep `CurrentPointerKey` and the
of the same replacement sequence or an exported compatibility wrapper with no immutable manifest key; remove legacy key names and duplicated fields.
production caller. - Restore uses only objects declared by the selected commit and never lists a
- Replace `ReadRegularFile` and the general confined-read facade with one small session prefix to reconstruct a snapshot. Preserve committed verification,
`ReadFileBounded`-style helper. It uses `os.Open`, a limit-plus-one read, and manifest-last installation, conflicts, and root-scoped safety.
returns a clear limit error; it follows ordinary trusted-workspace symlink - Previous-session hydration resolves each requested source exactly once from
semantics. A consumer may inspect `File.Stat`, size, checksum, or decoded the commit's unique `published_output` declaration. Derive its relative
structure when its own external contract requires those properties. Do not destination from that declaration's immutable destination key. Do not consult
reconstruct a package-wide regular-file/no-follow rule in callers. current config or previous-manifest outputs for alternate paths and do not
- Remove `OpenFileConfined`, `OpenConfinedRegularFile`, issue candidate `Exists` calls.
`ValidateConfinedDirectory`, and the general root-walking implementation after - A built-in artifact is authoritative only when its succeeded producer stage
their callers migrate. Configuration imports and ordinary artifacts may use has exactly one output matching registered source and kind, with safe path,
standard path semantics. The API-key loader is the exception: it keeps its producer run ID, and checksum. Verify checksum and content. Remove canonical-
8-KiB limit, restrictive POSIX mode checks, regular-file requirement, path and filename fallback; disk bytes alone never establish availability.
symlink rejection, environment precedence, and non-disclosure behavior in - Analyze authority comes only from the current versioned `AnalyzeArtifacts`
app-owned credential helpers. collection. Preserve fingerprints, dependencies, selection, force behavior,
- Remote content must be fully written to a temporary file and validated before and existing session/run projections until their later workstreams; remove
it becomes a canonical destination. Domain owners decide what validation is only aggregate-only and incidental-file compatibility here.
meaningful: expected size/generation/checksum, non-empty or format parsing, - Extraction availability continues to require current input and receipt
committed-object identity, or audio/cache identity. Use centralized existing identity, canonical immutable paths, a complete configured source set,
limits for bounded configuration/control objects and adapter results. Do not checksums, contracts, and Notarius provenance. The manifest schema boundary
invent a low global media-size cap; a transfer with an authoritative declared rejects older records; add no extraction migration reader.
size must reject a size mismatch. Any retained safety-limit error must name - Unsupported state errors identify the missing/received and supported version
the enforcing adapter/category and the effective numeric limit. and provide concise migration guidance. Tests assert error categories and
- Keep a single minimal sibling-temp installation operation so audio, restore, small stable fragments, not full prose.
and previous-artifact flows do not duplicate replacement policy. Remove - Current docs must direct operators to replace legacy party config, move aside
`DownloadedTempFile`, `DownloadAndInstall`, `DownloadToSiblingTemp`, and any and regenerate unsupported local state, restore a current committed snapshot
standalone install surface left without a production caller once their flows when authoritative, or republish healthy local state. Do not rewrite
are migrated. historical documents.
- Cleanup accepts an explicit root and a proper-descendant target, converts the - Mark only Workstream 3 `Complete`, and only after all stages and final
target to a normalized root-relative name, opens the root with `os.OpenRoot`, validation succeed.
and delegates recursive deletion to `Root.RemoveAll`. A missing target is
success. Exact-file deletion uses `Root.Lstat` and `Root.Remove`, remains
idempotent, and rejects a non-regular leaf. Root deletion, traversal, an
absolute relative name, or an out-of-root target remains an error. Do not keep
custom recursive traversal, sorting, handle chains, or a universal rule that
refuses a symlink which `os.Root` can safely contain.
- Restore planning opens the selected session root and classifies targets by
their normalized session-relative paths. Walk each existing ancestor with
`Root.Lstat`/`Root.OpenRoot`; any ancestor or leaf symlink is a non-regular
conflict. A directory remains a directory conflict. Hash only an opened,
verified regular target beneath that root. `--force` may turn a differing
regular file into a download, but must never turn an ancestor symlink, leaf
symlink, directory, or other non-regular entry into an overwrite.
- Restore execution must repeat the root-relative parent/leaf safety check at
installation time, create missing destination parents with workspace modes,
download to a sibling temporary file, validate the complete object, and only
then install it. Preserve manifest-last ordering, manifest rebasing and
validation, committed size/checksum/generation checks, audio cache identity,
incomplete-restore markers, reports, and safe retry. Do not add hostile-race
inode rechecks.
- Extract keeps Notarius integration validation in the Notarius adapter and
stage. The adapter-reported bundle must remain a proper descendant of the
unique run-local output root and satisfy the current v0.6 bundle contract.
Normalize the validated tree to workspace directory/file modes, require the
unique durable bundle destination to be absent, then use one `os.Rename`.
Do not copy the bundle, retain the pre-promotion tree, or provide a custom
no-replace race guarantee. A destination collision is an ordinary explicit
error.
- The run-local Notarius output root is owned by the extract invocation. After
verifying that scope, remove failed staging content; after success, remove the
now-empty staging root. Receipt and stderr paths remain the diagnostic record.
Never delete an adapter-reported path merely because the adapter returned it.
- Retire tests for syscall ordering, sync failures, directory-promotion hooks,
inode identity races, universal no-follow behavior, and platform capability
errors with the corresponding contract. Retain or add the smallest durable
behavior tests for atomic visibility/cleanup, bounded reads, modes, validated
downloads, cleanup scope, restore symlink conflicts, Notarius rename, and
credential protection.
- Current-behavior documentation changes in the same stage as code. Volatile
modes, paths, limits, and procedures remain in their canonical owners; other
documents summarize and link. Historical audits and release notes remain
historical and must not be rewritten.
- Mark Workstream 2 `Complete` in `refactoring.md` only after all eight stages
and final validation succeed. Do not change another workstream's status.
## Instructions For Every Stage ## Instructions For Every Stage
Before changing files in each stage: Before each stage:
1. Read `docs/development.md`, every document under `docs/policy/`, this plan, 1. Read `docs/development.md`, every file under `docs/policy/`, this plan,
the whole accepted [`fileops.md`](fileops.md) roadmap, and Workstream 2 plus Workstream 3 plus dependency/target sections of
the dependency and final-target sections of [`refactoring.md`](refactoring.md). [`refactoring.md`](refactoring.md), Finding 1 of
2. Consult only the concise decision-register rows H02, H06, H07, H18, H20, [`contract-audit.md`](contract-audit.md), and rows C01/H43 of
H21, H29, H45, H46, C04, L02, L05a, and L05c in [`audit-decisions.md`](audit-decisions.md).
[`audit-decisions.md`](audit-decisions.md). The source audits are historical 2. Inspect named implementation and focused tests. Prefer the codebase graph for
evidence and need not be reread or edited for implementation. Go discovery and text search for fields, fixtures, examples, and docs.
3. Inspect the current implementation and focused tests named by the stage. 3. Confirm worktree state and preserve unrelated changes. Do not enter a later
Prefer the codebase knowledge graph for Go discovery and use text search for workstream because adjacent code would also benefit.
documentation, literals, build constraints, and stale contract language.
4. Confirm the worktree state and preserve unrelated changes. Do not begin a
later workstream merely because adjacent compatibility, manifest, publish,
cleanup-stage, or configuration code would also benefit from refactoring.
During every stage: During every stage:
- Keep the repository building and the focused tests passing at the end of the - Keep the repository building and focused tests passing. Temporary current-only
stage. Temporary internal migration surfaces are permitted only when a later migration structure is allowed only when a later stage explicitly removes
numbered stage in this plan explicitly removes them; add no deprecation or it; add no compatibility shim or automatic migration reader.
compatibility layer. - Preserve package ownership, strict deterministic decoding, group workspace
- Use direct standard-library operations for trusted ordinary data. Keep behavior, bounded I/O, secret redaction, remote verification, restore safety,
adapter contract validation with adapters, stage policy with stages, restore and Linux/macOS/Windows support.
policy with the application, and canonical path construction with artifacts - Keep tests offline and behavior-focused. Delete tests only with their retired
and `pathsafe`. contract. Update canonical current-behavior docs with the code change.
- Preserve group-owned workspace operation. Never put API-key values in command - Run `gofmt` on changed Go, focused tests, and `git diff --check`. Mark a
arguments, configuration, logs, errors, fixtures, manifests, reports, or stage `Completed` only after all exit criteria pass.
ordinary workspace files. Never include secret contents in a test failure.
- Preserve Linux, macOS, and Windows behavior. Platform-specific code is
allowed only if the retained single-file replacement behavior demonstrably
needs it. Cross-compilation establishes compilation, not native runtime
evidence.
- Keep tests deterministic, offline, and behavior-focused. Use `t.TempDir()`
and real filesystem operations. Prefer semantic error fragments or typed
causes over exact complete wording, and delete obsolete mechanism tests in
the same stage that retires their contract.
- Update canonical current-behavior documentation in the stage that changes the
behavior. Roadmap documents may describe future state; canonical docs must
not describe a partially implemented later stage as current.
- Run `gofmt` on changed Go files, focused tests while iterating, and
`git diff --check` before completing the stage. Mark a stage `Completed` only
after all of its exit criteria pass.
## Stage 1 — Reduce Atomic Replacement, Modes, And Bounded Reads ## Stage 1 — Establish Explicit Current Manifest Schemas
**Status: Completed** **Status: Pending**
### Goal ### Goal
Establish the small shared filesystem foundation that later stages will use, Create an unambiguous local-state boundary before removing downstream legacy
while removing power-loss durability and test-hook machinery from single-file branches.
replacement.
### Required Work ### Required Work
1. Rewrite `internal/fileops/fileops.go` around one private reader-to-sibling- 1. Add the settled schema constants and required `schema_version` fields in
temp replacement implementation. Preserve `ReplaceFileOptions.Mode` and `internal/manifest/manifest.go` and `run_manifest.go`; set them in
`BeforeRename`, context/caller error wrapping, cleanup-error joining, checksum `New` and `NewRun`.
calculation, destination-parent ownership, and replacement of an existing 2. Require exact versions in `LocalStore.Save`, `LoadReader`, `SaveRun`,
regular destination. Remove `temporaryFile`, `atomicReplacementOperations`, and `LoadRunReader`. Missing/unknown versions return a clear migration
system operation tables, injected operation variants, `Sync` calls, and error. Saving a hand-built zero-value manifest fails rather than backfilling.
directory-sync calls from the file replacement path. 3. Require a non-nil stage map; reject nil stage entries and embedded stage
2. Make the final sequence explicit in code and package documentation: create names that are empty or differ from their map keys. Remove load normalization
sibling temp, stream bytes, apply mode, close, run the optional pre-rename that synthesizes these required markers, retaining only harmless ordering or
check, and `os.Rename`. If creation, writing, chmod, close, or the pre-rename cloning.
check fails, remove the temp and retain the old destination. If rename fails, 4. Make session and run `ensureStage` initialize the analyze contract version
retain the old destination and remove the temp. A successful rename returns and an empty collection for `analyze`. Make
success without an `fsync` claim. `validateAnalyzeStageState` require that version for every present analyze
3. Consolidate the public internal entry points so byte writes, reader writes, stage regardless of lifecycle status. Delete version-zero acceptance.
copies, checksum-returning copies, and sibling-temp installation share that 5. Update current test construction to use constructors or explicit markers.
implementation. Introduce the final clearly named temp-install function and Cover missing/supported/unknown versions, nil/mismatched stages, newly
migrate existing callers of `InstallDownloadedTempFile` as needed within the created analyze state, empty current collections, and aggregate-only
package. Do not preserve an alias after all callers migrate in Stage 3. rejection.
4. Rewrite `EnsureWorkspaceDirectory` in `modes.go` with ordinary `os` and 6. Update `docs/internal/manifest.md`, `docs/internal/artifacts.md`,
`filepath` operations. Find the nearest existing ancestor, create the missing `docs/operations.md`, and `docs/troubleshooting.md` with schema and local
suffix, and explicitly apply `WorkspaceDirectoryMode` to each directory that recovery guidance. Leave party/remote removal for their stages.
was created and to the requested directory. Reject an existing non-directory
at the requested path; do not add symlink/race identity machinery.
5. Add a small `ReadFileBounded` implementation that opens the path normally,
reads through `io.LimitReader(limit+1)`, and reports limit overflow without
returning partial content. Reject invalid limits and empty paths. Retain the
limit in the error so adapter/category wrappers can provide unmistakable
context. Leave the old confined read names only until Stage 2 migrates their
callers.
6. Rewrite `internal/fileops/fileops_test.go`, `modes_posix_test.go`, and the
bounded-read tests around observable outcomes: old-or-new content only,
destination replacement, pre-rename cancellation, temp cleanup after each
meaningful pre-commit failure category, checksum correctness, exact/plus-one
bounds, and group modes under a restrictive umask. Delete operation-order,
sync, race-hook, symlink, and private-operation-table assertions whose
guarantees are retired.
7. Update `docs/internal/fileops.md`, the atomic-save section of
`docs/internal/manifest.md`, and the narrow durability wording in
`docs/policy/architecture.md` to describe atomic visibility rather than
crash/power-loss durability. Do not yet describe directory promotion or
cleanup as removed; those change in later stages.
### Exit Criteria ### Exit Criteria
- `go test ./internal/fileops ./internal/manifest ./internal/artifacts` succeeds. - `go test ./internal/manifest ./internal/artifacts ./internal/stage ./internal/app`
- Atomic replacement leaves the prior destination intact and no sibling temp succeeds.
after a pre-commit failure, and produces the intended mode/content on success. - Both manifest kinds round-trip only with exact supported versions.
- The bounded helper accepts exactly the configured limit and rejects limit+1. - A present analyze stage cannot round-trip without current per-artifact
- No single-file replacement path calls `Sync` or reports a directory-sync versioning; an empty current collection can.
capability error; directory-promotion-only code may remain until Stage 7. - Load does not synthesize required stage identity or authority.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 2 — Migrate Ordinary I/O And Isolate Credential Handling ## Stage 2 — Reject Unversioned Party Documents
**Status: Completed** **Status: Pending**
### Goal ### Goal
Remove universal confined/no-follow access from ordinary trusted-workspace End interpretation of unversioned party files before removing the configuration
callers without weakening the one sensitive filesystem boundary: API-key fields and mode shell that supported them.
loading.
### Required Work ### Required Work
1. Migrate bounded local-result readers in the Audita, Notarius, Seriatim, and 1. Make `ParseParty` require exactly `PartySchemaVersion` in the first and
Scriptorium adapter packages, `internal/artifacts`, `internal/contracts`, only YAML document. Missing/unknown versions identify the supported schema;
`internal/config/party_resolution.go`, and `internal/stage` to the Stage 1 retain strict malformed, duplicate, ambiguity, and trailing-document checks.
bounded helper. Preserve each owner/category's existing centralized limit, 2. Delete `classifyLegacyPartyDocument`, `resolveLegacyParty`, and
semantic decoding, non-empty checks, checksum rules, and clear adapter-named `party_legacy.go`. For this stage only, the resolved mode shell may remain
error context. Do not centralize adapter-specific limits in FileOps. so all packages compile, but production cannot construct legacy mode.
2. Replace ordinary uses of `OpenConfinedRegularFile` with direct `os.Open` and 3. Simplify `resolvePartyInput` to return parsed canonical data and source
the minimum domain check actually required. Publish sources, artifact directly, with no classification branch.
evidence, prepared inputs, and extract references must still be opened before 4. Replace legacy parser/resolution tests with required-v1, missing/unknown
hashing/copying and checked as regular when their declared artifact contract version, multi-document, strict-field, alias, identity/order, and
requires it; remove `SameFile` and pathname reinspection. Configuration deterministic players-projection tests.
imports/overlays use normal `os.Open` semantics after existing lexical 5. Update `docs/integrations/party.md`, the party section of
normalization, extension, duplicate, and composition checks. `docs/config.md`, `docs/internal/configuration.md`, and troubleshooting.
3. Replace `OpenFileConfined` at diagnostic-log and session-lock sites with Do not yet claim removed config keys are unknown; Stage 3 changes structs.
explicit parent creation plus `os.OpenFile` and mode normalization owned by
those packages. Preserve lock behavior and subprocess diagnostic bounds,
redaction, close/flush behavior, and workspace modes. Do not change process
supervision.
4. Move credential-only path handling into `internal/app/secrets_env*.go`.
Open and validate the configured secret directory and each candidate without
following a symlink; require regular files, preserve restrictive POSIX mode
validation (and documented Windows ACL responsibility), read at most
`APIKeyFileByteLimit`, and keep existing-environment precedence. A simple
inspection/open sequence is sufficient; remove adversarial replacement hooks
and inode identity rechecks.
5. Remove `ValidateConfinedDirectory`, `ReadRegularFileUnderRoot`,
`ReadRegularFile`, `OpenConfinedRegularFile`, `OpenFileConfined`, and their
tests once production callers are gone. Delete the general confined-open and
confined-read files if no later stage depends on them; keep only narrowly
identified cleanup/download code needed temporarily by Stages 3 and 4.
6. Update `docs/config.md`, `docs/internal/configuration.md`, adapter/integration
documents, `docs/internal/artifacts.md`, `docs/internal/stage-analyze.md`,
`docs/internal/stage-publish.md`, and `docs/internal/workspace.md` where they
currently present universal no-follow/confined reads as application-wide
behavior. Preserve and clearly isolate the stricter API-key contract in
`docs/config.md` and `docs/operations.md`.
7. Adjust focused tests to protect owner-level outcomes rather than the deleted
helper choreography. Retain adapter limit tests, artifact checksum/contract
tests, configuration composition tests, publish-source validation, lock
behavior, subprocess redaction, and credential symlink/mode/limit tests.
Delete tests whose only purpose is universal no-follow or identity-race
resistance.
### Exit Criteria ### Exit Criteria
- `go test ./internal/adapters/... ./internal/artifacts ./internal/contracts ./internal/config ./internal/stage ./internal/app` succeeds. - `go test ./internal/config ./internal/stage ./internal/app` succeeds.
- Ordinary bounded reads follow standard path semantics and retain their - No production code classifies or resolves an unversioned party, and
category-specific limits and useful errors. `party_legacy.go` is absent.
- API-key files remain bounded, regular, non-symlink, private on POSIX, and - Unsupported party schema fails before family expansion or preparation.
absent from errors and logs. - Canonical parsing and derived players output remain deterministic.
- No production caller uses the general confined open/read APIs.
- Canonical docs distinguish ordinary trusted-workspace access from credential
and destructive/restore safety boundaries.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 3 — Validate Downloads Before Canonical Installation ## Stage 3 — Remove Legacy Party And Players Configuration
**Status: Completed** **Status: Pending**
### Goal ### Goal
Consolidate temporary-download behavior so incomplete or invalid remote content Make one campaign party the only configured roster authority and players an
never becomes a canonical local file, then remove the overbuilt FileOps download explicitly derived value.
object model.
### Required Work ### Required Work
1. Define one straightforward sibling-temp lifecycle used by the affected 1. Remove `PlayersFile` from `CampaignInputsConfig` and both `PlayersFile`
domain owners: ensure the destination parent with workspace modes, create a and `PartyFile` from `SessionInputsConfig`. Strict decode must reject all
uniquely named sibling temp, stream through `storage.DownloadTo` or verified three retired YAML paths as unknown unsupported fields.
in-memory content, close, validate the complete temp, install through the 2. Keep campaign `PartyFile` in resolved stable inputs. Represent generated
Stage 1 temp-install replacement, and remove the temp on every failure. players as derived data, not a selectable `ResolvedInputFile`; introduce a
Keep transport mechanics in `internal/adapters/storage`; keep validation and clearly named derived value if prepared-input consumers need one.
destination policy with audio, restore, configuration, or stages. 3. Remove `PartyMode`, both constants, `PartyDocument`, `IsCanonical`,
2. Rework `internal/audio/s3_audio.go` and `cache_identity.go` so a remote audio and all mode branches. Make `ResolvedParty` hold non-nil
object is downloaded to temp and checked against authoritative declared size `CanonicalParty` plus source provenance, and make `ParseParty` return
plus the existing local-audio requirements before it becomes the spool file. `*CanonicalParty`.
Reuse cached audio only after its typed remote identity, regular opened file, 4. Simplify `mergeCampaignSession`, campaign-party loaders, resolved
exact size, and SHA-256 match; install it from temp only after validation. validation, family expansion, effective digest/source reporting, and config
Preserve cache miss/hit accounting, checksum records, context cancellation, commands. Party selection always comes from campaign config.
and group modes. 5. Delete `internal/stage/prepare_legacy.go`. Simplify prepare/resume,
3. Rework previous-session hydration in `internal/stage/prepare_previous.go` so extraction references, and semantic contracts to copy canonical party and
`VerifiedContent` or streamed remote content is checked against every render players. Preserve prepared paths, checksums, input kinds, Notarius
available declared size/checksum and the existing non-empty artifact rule selectors, and meaningful `derived_from_party` provenance.
before installation. Keep the current legacy/canonical resolution branches; 6. Make operator/source rendering show campaign party and derived players, with
Workstream 3, not this stage, removes compatibility behavior. no `legacy_player` role or separate player path.
4. Audit `storage.DownloadObjectToTemp` callers used for remote session config, 7. Rewrite affected config, stage, app, template, profile, and command tests.
restore planning, and current-state discovery. Where a control/config object Add strict rejection for all retired keys and an integrated config/prepare
already has a centralized limit, use `ReadObjectBounded` and parse/validate test proving one party produces both prepared documents.
bytes directly or enforce that limit while writing temp. A temporary file 8. Remove legacy roster text/fixtures from `docs/config.md`, `docs/cli.md`,
used only for comparison may remain system-temp state, but it must be removed `docs/internal/configuration.md`, `docs/internal/stage-prepare.md`,
reliably and must not be described as canonical installation. `docs/internal/stage-extract.md`, and `examples/`.
5. Keep restore's committed-object temp validation behavior working, but defer
its final root-scoped installation redesign to Stage 6. It may use the new
minimal temp lifecycle as an interim caller.
6. Remove `DownloadedTempFile`, `DownloadAndInstall`,
`DownloadToSiblingTemp`, the old confined download installer, redundant
exported install aliases, and their mechanism tests when no production
caller remains. Keep only the single temp-install replacement primitive
selected in Stage 1.
7. Update `docs/internal/storage.md`, `docs/internal/artifacts.md`,
`docs/internal/command-restore.md`, and `docs/operations.md` to state that
transfer and validation precede canonical installation, without duplicating
owner-specific numeric limits.
8. Add or retain focused tests for failed/partial download cleanup, declared
size mismatch, checksum/identity mismatch, invalid configuration or manifest
content, cache corruption, successful install modes, and preservation of an
existing canonical destination after any validation failure.
### Exit Criteria ### Exit Criteria
- `go test ./internal/adapters/storage ./internal/audio ./internal/stage ./internal/app ./internal/artifacts` succeeds. - `go test ./internal/config ./internal/stage ./internal/app` succeeds.
- Audio, previous-session, remote configuration/control, and restore download - No production YAML tag accepts `players_file` or session `party_file`.
paths validate the complete temp or bounded bytes before canonical install. - No production `PartyMode`, `PartyDocument`, or legacy prepare symbol
- A failed transfer or validation does not replace an existing destination and remains.
leaves no owned sibling temp. - One campaign v1 party supplies both prepared party and derived players.
- The old downloaded-temp object model and unused install surfaces have no - Current docs/examples contain no legacy roster workflow.
production callers and are deleted.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 4 — Replace Custom Cleanup Traversal With `os.Root` ## Stage 4 — Make Remote Current State Commit-Only
**Status: Completed** **Status: Pending**
### Goal ### Goal
Preserve accidental out-of-root deletion protection while deleting custom Make the versioned commit pointer the sole remote authority for status, restore,
recursive traversal, no-follow handle chains, and sorting machinery. and reuse.
### Required Work ### Required Work
1. Rewrite `RemoveAllUnderRoot` to validate non-empty paths, resolve absolute 1. Replace legacy missing errors with typed
clean root and target paths, require the target to be a proper descendant, `CurrentCommitPointerMissingError` keyed to
derive and normalize one root-relative name, open the explicit root with `S3CurrentCommitPointerKey`. `LoadCurrentState` returns it with
`os.OpenRoot`, and call `Root.RemoveAll`. Missing targets remain success. publish/republish guidance when absent and otherwise loads only committed
Never pass an absolute path, `.`, `..`, or traversal to a root method. state.
2. Rewrite `RemoveFileUnderRoot` with the same root/relative validation, 2. Delete `current_state_legacy.go`, legacy limits,
`Root.Lstat`, regular-leaf classification, and `Root.Remove`. Missing is `ResolveCurrentStateKeys`, and retired key helpers once callers are gone.
success; directories, symlinks, and other non-regular leaves are errors. Retain current pointer/commit/session-manifest bounds.
Keep this exact-file rule because callers intend to clear one managed file, 3. Require non-nil pointer, commit, and manifest in `CurrentState`; keep
not because it is a universal workspace policy. `CurrentPointerKey` and committed manifest key; remove
3. Delete `openConfinedCleanupTarget`, filesystem-root walking, `CurrentRunIDKey` and legacy-oriented duplication. Update identity errors.
`removeConfinedEntry`, `removeConfinedChildren`, deterministic traversal 4. Mirror this in `app.RemoteCurrentState`, discovery, status, lock
sorting, and any general confined-directory code left solely for cleanup. inspection, restore, and helpers. Status may report missing pointer as not
4. Keep existing application eligibility checks in `cleanup_targets.go`, published; malformed or unsupported current data remains an error.
`clean.go`, post-publish cleanup, prepare's managed previous-state reset, 5. Delete `buildLegacyRestoreActions`, legacy key mapping, prefix listing, and
remote-session-config temp cleanup, and restore-marker cleanup. Do not move checksum inference. Require committed restore planning while preserving
cleanup out of publish, add a pipeline stage, change cleanup obligations, or audio scope, verification, conflicts, reports, manifest-last order, and
change cache-selection policy; those belong to Workstream 9. installation safety.
5. Rewrite `internal/fileops/cleanup_test.go` around proper-descendant 6. Replace legacy fixtures with complete pointer/commit/manifest fixtures.
rejection, root/self/traversal/outside refusal, nested removal, missing Cover missing pointer, bad versions/identity, undeclared/missing objects,
idempotence, root-contained symlink behavior, and exact-file non-regular checksum/size/generation mismatch, and proof that restore neither lists nor
refusal. Retain app-level tests for policy-owned target eligibility. reads retired keys.
6. Update the cleanup/confinement sections of `docs/internal/fileops.md`, 7. Remove legacy remote language from `docs/operations.md`,
`docs/internal/workspace.md`, `docs/operations.md`, and `docs/troubleshooting.md`, `docs/internal/manifest.md`,
`docs/troubleshooting.md`. Describe root confinement and eligibility, not a `docs/internal/artifacts.md`, `docs/internal/command-restore.md`, and
custom no-follow recursive algorithm. `docs/internal/stage-publish.md`; document republishing.
### Exit Criteria ### Exit Criteria
- `go test ./internal/fileops ./internal/app ./internal/stage` succeeds. - `go test ./internal/artifacts ./internal/app ./internal/stage` succeeds.
- Root, out-of-root, traversal, and overly broad targets are rejected; an - Production never reads `current/manifest.json` or `current/run_id.txt`.
eligible nested target is removed idempotently through `os.Root.RemoveAll`. - Every successful current load has a verified pointer, commit, and manifest.
- No custom recursive cleanup walker or ancestor-handle chain remains. - Restore derives all remote scope from the commit without prefix listing.
- Current explicit and post-publish cleanup behavior is otherwise unchanged.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 5 — Make Restore Planning Symlink-Safe And Root-Relative ## Stage 5 — Resolve Previous Artifacts From Commit Declarations
**Status: Completed** **Status: Pending**
### Goal ### Goal
Fix the restore correctness gap so planning can never follow a leaf or ancestor Use one deterministic source-to-object lookup per previous requirement without
symlink and classify its target as an already matching regular file. path candidates or remote probing.
### Required Work ### Required Work
1. Open the selected local session root once for restore planning. If it does 1. Rewrite `previouscache.Resolve` around guaranteed committed state. Require
not exist, create it with workspace directory modes before opening it. Pass the unique session-manifest declaration and always copy its checksum, size,
normalized session-relative names—not trusted absolute producer paths—to the generation, and verified content into the plan.
local classifier. Continue deriving display/canonical absolute paths only 2. For each sorted requirement, derive its canonical source ID and select its
through `artifacts.SessionPaths` and `joinWithinSessionRoot`. unique `published_output`. Validate and strip the selected run's immutable
2. Add a private app-owned target inspection routine that walks each existing published-output prefix from `DestinationKey` to derive the local relative
relative ancestor with `Root.Lstat` and `Root.OpenRoot`, without following a path; never use current config or previous-manifest outputs for it.
symlink. Return a small semantic state: missing, regular opened file, 3. Preserve required/optional behavior, deterministic ordering, safe local
directory conflict, or non-regular conflict. A missing ancestor means the paths, and checksum/size/generation metadata.
target is missing; an ancestor symlink, leaf symlink, or other special entry 4. Delete legacy candidate/basename matching, manifest/config fallbacks,
means non-regular conflict. Do not add inode snapshots, `SameFile` checks, candidate `Exists` calls, probing ambiguity errors, and unused derivation
race hooks, or a new generic FileOps abstraction. helpers. Delete the test-only `BuildPlan` alias; call `Resolve`.
3. Refactor `classifyRestoreAction` to consume that semantic state. Hash only 5. Test exact declarations, no candidate `Exists` or list calls,
the already opened regular file. Preserve committed-checksum comparison, required/optional outcomes, unsafe destinations, duplicate-source rejection,
legacy remote-content comparison, audio identity rules, size comparison, and verified metadata through prepare/restore integration.
deterministic reasons/counts/order, and typed conflict categories. 6. Update `docs/internal/stage-prepare.md`,
4. Ensure `--force` changes only a content mismatch on a regular file into `docs/internal/command-restore.md`, `docs/internal/artifacts.md`,
`RestoreActionDownload`. Directory and non-regular conflicts, including `docs/operations.md`, and troubleshooting.
every symlink case, remain conflicts with force and non-force. Dry-run must
report the same classification without local mutation.
5. Apply the classifier to committed current artifacts, retained legacy current
state, and previous-cache actions. Do not remove legacy restore planning;
that is Workstream 3.
6. Add focused planner and command tests covering a matching leaf symlink, a
differing leaf symlink, an ancestor symlink, directory/special-file conflict,
missing ancestor, matching regular content, differing regular content with
and without force, and unchanged dry-run behavior. Assert classification and
safe outcomes rather than complete reason prose.
7. Update `docs/internal/command-restore.md`, `docs/operations.md`, and the
relevant restore troubleshooting guidance to make the implemented
root-relative symlink-conflict behavior explicit.
### Exit Criteria ### Exit Criteria
- `go test ./internal/app -run 'Restore|restore'` and - `go test ./internal/previouscache ./internal/stage ./internal/app` succeeds.
`go test ./internal/artifacts ./internal/previouscache` succeed. - Resolution contains no candidate, basename/config fallback, ambiguity probe,
- Leaf and ancestor symlinks are non-regular conflicts even when their targets or compatibility alias.
have the expected bytes and even with `--force`. - Every hydration traces to one verified commit declaration.
- Only an opened regular target beneath the selected session root is hashed. - Optional absence skips and required absence fails without probing.
- Restore order, remote authority, legacy compatibility, and dry-run output
remain otherwise intact.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 6 — Install Restore Results Through The Session Root ## Stage 6 — Make Local Artifact Evidence Manifest-Only
**Status: Completed** **Status: Pending**
### Goal ### Goal
Make execution preserve the planner's root and conflict decisions while keeping Remove aggregate and incidental paths so transcript, extraction, and analyze
complete validation and manifest-last recovery semantics. consumers require current manifest evidence.
### Required Work ### Required Work
1. Rework `executeRestorePlan` and `executeRestoreDownloadAction` around an 1. Rewrite `ResolveSessionArtifact` and its private candidate logic to require
explicitly opened session `os.Root`. Normalize every action's relative path, a succeeded registered producer and exactly one output matching both
re-derive its canonical absolute path, and reject a plan whose recorded `Kind` and `SourceID`. Require safe path, producer run ID, and valid
absolute path disagrees. Keep the manifest action last. checksum; verify checksum and registered content before returning. Reject
2. Before writing, walk the destination parent under the root. Reject a ambiguous/malformed evidence.
symlink/non-directory ancestor; create a missing component one at a time and 2. Delete `fallback.canonical_path`, canonical-path probing, filename
apply `WorkspaceDirectoryMode`. Immediately before install, reject a leaf fallback, and old-candidate deduplication. Missing evidence remains typed
symlink, directory, or special file. Permit replacement only of an eligible unavailable even when expected bytes exist.
regular file when the plan selected download/force, or installation into an 3. Update render, extract input identity, transcript resolution, and analyze
absent leaf. built-in resolution to use this path while retaining stage context, content
3. Create each temporary download as a sibling inside the validated parent. checks, and semantic fingerprints.
Stream remote or verified in-memory content, close it, and validate all 4. Remove `analyzeReconciliationLegacy` and the version-zero reconciliation
applicable size, SHA-256, generation, non-empty, audio, and format contracts branch. Unsupported in-memory state is non-resumable/corrupt, not legacy
before replacement. Install with workspace file mode and remove temp files state eligible for recovery.
on all failures. 5. Audit catalog hydration, analyze inputs, publish discovery, operator
4. Preserve `verifyRestoredObject`'s committed identity revalidation immediately inspection, and projections: configured analyze output authority comes only
before install. Preserve audio materialization/cache identity, but ensure the from a current `AnalyzeArtifacts` record and its output evidence, never
final destination also passed root-relative parent/leaf validation. aggregate outputs or configured paths. Do not implement Workstream 6
5. For session and previous manifests, parse, validate, and rebase the complete checkpointing.
temporary content first, then serialize/install the validated manifest 6. Retain only strict extraction proof in `InspectExtractionEvidence`:
atomically at its root-approved destination. Preserve current-session succeeded stage, matching input, canonical bundle, receipt, exact lane set
identity checks and install the selected session manifest after all other plus index, verified JSON/checksums, contracts, and Notarius provenance.
durable files. Delete any acceptance of missing contract/provenance; do not redesign the
6. Preserve the incomplete-restore marker, shared session lock, report status, adapter.
failure attribution, retry behavior, and prior successful installs. A failed 7. Test that incidental files, aggregate-only analyze outputs,
action must leave any prior canonical destination intact and keep the marker missing/failed/stale/unselected records, mismatched checksums, and incomplete
blocking normal runner reuse. extraction evidence remain unavailable. Retain positive coverage for all
7. Add execution-level regression tests for a parent or leaf becoming a built-in transcript sources, configured extraction lanes, and analyze
symlink between planning and execution, force refusal of non-regular targets, outputs.
validation failure preserving old bytes, temp cleanup, group modes, manifest- 8. Update `docs/internal/artifacts.md`, `docs/internal/stage-analyze.md`,
last ordering, partial failure/retry, and successful regular replacement. `docs/internal/stage-extract.md`, `docs/internal/stage-publish.md`,
Use a small invocation-scoped seam only if needed to trigger the plan/execute `docs/internal/manifest.md`, `docs/config.md`, operations, and
state change; do not add a package global race hook. troubleshooting without legacy/fallback language.
8. Update `docs/internal/command-restore.md`, `docs/internal/workspace.md`,
`docs/operations.md`, and `docs/troubleshooting.md` with the root-scoped
installation and recovery behavior. Avoid repeating path names owned by the
operations document.
### Exit Criteria ### Exit Criteria
- `go test ./internal/app -run 'Restore|restore'`, - `go test ./internal/artifacts ./internal/manifest ./internal/stage ./internal/app`
`go test ./internal/audio ./internal/manifest`, and representative restore- succeeds.
then-run workflow tests succeed. - Disk files cannot establish transcript or analyze availability without
- Execution cannot install through an ancestor symlink or over any non-regular current evidence; aggregate analyze outputs cannot substitute.
leaf, including under `--force`. - Extraction lacking receipt, contract, provenance, complete sources, or
- Failed transfer, identity, format, or manifest validation preserves the old verified bytes cannot hydrate the catalog.
destination, removes temp state, and leaves recovery state accurate. - Positive current transcript/extraction/analyze/publish/operator flows work.
- Successful restore retains manifest-last ordering, workspace modes, and safe
retry.
- `git diff --check` succeeds. - `git diff --check` succeeds.
## Stage 7 — Replace Notarius Tree Copy With One Validated Rename ## Stage 7 — Complete The Purge And Validate The Repository
**Status: Completed** **Status: Pending**
### Goal ### Goal
Eliminate duplicate Notarius artifact trees and all custom atomic no-replace Remove residual compatibility material, validate the current-only architecture,
directory-promotion machinery. and close Workstream 3 without entering later work.
### Required Work ### Required Work
1. Keep `internal/adapters/notarius` responsible for v0.6 CLI construction, 1. Search active code, tests, fixtures, examples, and current docs for retired
receipt/index/management-document decoding, bounded JSON, bundle and lane remote keys/readers, previous candidates, unversioned party modes,
contract validation, and reference arguments. Do not move Notarius-specific `players_file`, session party overrides, aggregate analyze authority,
schema or path interpretation into FileOps. incidental artifact fallback, missing manifest schemas, and extraction
2. In `internal/stage/extract.go`, establish one unique run-local Notarius output evidence without contract/provenance.
root and the existing unique durable bundle destination. After the adapter 2. Delete unused constants, fields, types, errors, helpers, aliases, files,
returns, require its bundle root to be a proper descendant of the requested fixtures, and imports belonging to those paths. Do not remove unrelated
output root, reject an unsafe/empty result, verify the receipt identity, adapter defaults, optional-input behavior, or later-workstream seams merely
reference snapshots, management documents, configured lanes, relative paths, because they use “fallback.”
and staging checksums before installation. 3. Review canonical docs under the documentation policy for consistent current
3. Add the small shared workspace-tree mode normalizer decided by the roadmap. behavior and actionable migration. Leave `docs/releases/`, audits, and
Walk the validated bundle without following symlink entries; apply decision history unchanged.
`WorkspaceDirectoryMode` to directories and `WorkspaceFileMode` to regular 4. Run `go test ./...`, `go test -race ./...`, `go vet ./...`, documented
files. Reject symlinks and special entries because the Notarius bundle docs/example validation, and supported Linux/macOS/Windows release builds.
contract—not general workspace policy—requires a regular tree. Cross-builds prove compilation only.
4. Inspect the durable destination with `os.Lstat`; require it to be absent and 5. Review tests against testing policy: retain rejection and positive tests at
return a clear collision error otherwise. Ensure its parent has workspace every authority boundary without recreating deleted algorithms in tests.
mode, then move the validated bundle with one `os.Rename`. Do not copy files, 6. Mark only Workstream 3 `Complete` in `refactoring.md` and all stages here
create a second temp tree, sync either directory, or attempt an atomic `Completed`. Accurately record unavailable native-platform evidence.
no-replace syscall.
5. Resolve promoted paths from the previously validated relative paths and
retain the existing post-install checksum/contract checks before returning
stage outputs. Preserve artifact IDs, contracts, provenance, selection,
metadata, manifest records, resume evidence, and all ten default D&D lanes.
6. Arrange scoped staging cleanup: derive cleanup authority from the canonical
run-local output root, not `adapterResult.BundleRoot`; on failure remove only
that verified invocation-owned staging root, and on success remove the empty
root after rename. Keep receipt and stderr diagnostics. A cleanup error should
be joined with the primary stage failure without obscuring it.
7. Delete `internal/fileops/directory.go`, `rename_noreplace_*.go`,
`sync_directory_*.go`, `ErrDirectorySyncUnsupported`, directory-promotion
hooks, custom tree-copy functions, and all mechanism/platform tests that now
have no contract. Remove remaining confined implementation files if no
retained helper uses them.
8. Rewrite extract tests around observable behavior: successful single rename,
all expected lanes immediately reusable, source tree absent, destination
collision, invalid/out-of-root adapter result, contract/checksum rejection
before install, mode normalization, failed-staging cleanup, receipt/log
retention, and no partial stage outputs. Do not assert a private rename
helper or syscall sequence.
9. Update `docs/integrations/notarius.md`, `docs/internal/stage-extract.md`,
`docs/internal/fileops.md`, `docs/internal/workspace.md`,
`docs/internal/overview.md`, `docs/operations.md`, and
`docs/troubleshooting.md`. Remove the Windows unsupported-promotion section,
duplicate pre-promotion-tree guidance, and atomic-no-replace claims. Describe
one validated run-local staging bundle moved to its immutable durable path.
### Exit Criteria ### Exit Criteria
- `go test ./internal/adapters/notarius ./internal/stage ./internal/artifacts ./internal/fileops` succeeds. - Full tests, race tests, vet, docs/examples, and release cross-builds succeed.
- A successful extract retains exactly one complete durable bundle and no - No runtime branch accepts or infers a retired Workstream 3 representation.
pre-promotion copy, while all configured/default D&D outputs remain valid and - Each current manifest, party, remote snapshot, previous input, transcript,
consumable. extraction artifact, and analyze artifact has one documented authority.
- Invalid bundles and destination collisions install nothing; scoped staging is - Only Workstream 3 is newly complete and `git diff --check` succeeds.
cleaned and receipt/log diagnostics remain.
- Directory copy/promotion, no-replace platform code, directory syncing,
capability errors, and their mechanism tests are absent.
- Linux, macOS, and Windows builds compile without a platform-specific directory
promotion fallback.
- `git diff --check` succeeds.
## Stage 8 — Finish The Filesystem Contract And Validate The Workstream
**Status: Completed**
### Goal
Remove residual obsolete surfaces and claims, prove the reduced contract across
the repository, and close Workstream 2 without entering later refactors.
### Required Work
1. Inventory `internal/fileops` and every production caller. The final package
may contain only the settled atomic-file, bounded-read, workspace-mode/tree-
mode, and root-deletion helpers. Inline or delete a wrapper with one trivial
caller when direct standard-library code is clearer. Remove dead types,
sentinels, build-tag files, compatibility aliases, injection hooks, and
tests that protect retired behavior.
2. Search maintained source and canonical documentation for stale claims or
names involving durable/crash-safe replacement, directory sync, atomic
no-replace promotion, duplicate Notarius trees, universal confinement,
universal no-follow reads, and unsupported promotion platforms. Update only
current-behavior owners. Do not rewrite the audits, roadmaps, ADR history,
or historical release notes.
3. Review retained numeric limits and mode definitions for discoverability.
Keep each limit as a named package-level constant near its enforcing owner;
keep it deliberately above ordinary expected data where it is a disk/memory
safety guardrail; and ensure its error identifies the responsible adapter or
category and numeric limit. Do not add configuration knobs in this workstream.
4. Review API-key ingress end to end. Confirm secret values enter only through
existing environment/secret-file contracts, retain restrictive file handling,
and cannot appear in ordinary workspace files, subprocess arguments,
generated configuration, logs, manifests, reports, errors, examples, or test
diagnostics.
5. Consolidate redundant filesystem test fixtures within affected packages and
delete tests with no remaining plausible failure mode. Ensure one clear test
owner for atomic replacement, bounded reads, modes, cleanup root scope,
restore symlink classification/install, downloads, and Notarius rename.
6. Update `docs/internal/overview.md` and every affected focused internal link or
test inventory to the final file/package shape. Run the documentation link
and example checks after deleting files.
7. Mark Workstream 2 `Complete` in `docs/roadmap/refactoring.md`. Leave
`fileops.md`, the audit decision register, and the three source audits as the
durable decision record. Leave Workstream 3 as the next `Pending` workstream.
### Exit Criteria
- `go test ./...`, `go test -race ./...`, `go vet ./...`, and
`go build ./...` succeed.
- `go test ./internal/doccheck` and
`go test ./internal/config -run '^TestExamplesLoadAndValidate$'` succeed.
- `scripts/build-release-assets.sh v0.0.0-test ABSOLUTE_EMPTY_TEMP_DIR` succeeds
for all six supported CGO-disabled release targets, and its host binary
version smoke check succeeds. Remove only that known temporary directory
afterward.
- `git diff --check` succeeds and the worktree contains no generated build
artifacts.
- Production no longer contains directory promotion/copy, directory sync,
hostile-race identity checks, or a general confined-filesystem facade.
- The retained filesystem code and canonical documentation match the accepted
`fileops.md` target state, Workstream 2 is `Complete`, and Workstreams 3–10
remain unimplemented.
## Open Questions ## Open Questions
None. The accepted feature roadmap, consolidated refactoring roadmap, audit None. The approved audits, roadmap, policies, and current implementation make
decision register, and decisions above are sufficient to implement all stages this plan decision-complete.
without additional product or architecture choices.