@6reduk/workspace-pipeline 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/docs/collaboration.md +59 -0
  4. package/docs/config-fields.md +65 -0
  5. package/docs/contracts.md +149 -0
  6. package/docs/doctor.md +165 -0
  7. package/docs/launch.md +50 -0
  8. package/docs/lifecycle-cli.md +141 -0
  9. package/docs/lifecycle.md +42 -0
  10. package/docs/migrations/unity.md +288 -0
  11. package/docs/native-provider-format.md +149 -0
  12. package/docs/provider-bundles.md +81 -0
  13. package/docs/provider-resources.md +37 -0
  14. package/docs/release.md +25 -0
  15. package/docs/remove.md +70 -0
  16. package/docs/repair.md +80 -0
  17. package/docs/repositories.md +209 -0
  18. package/docs/repository-manual-recovery.md +102 -0
  19. package/docs/repository-observations.md +29 -0
  20. package/docs/repository-recovery.md +204 -0
  21. package/docs/repository-retention.md +46 -0
  22. package/docs/repository-transport-budgets.md +26 -0
  23. package/docs/retention.md +237 -0
  24. package/docs/source.md +50 -0
  25. package/docs/switch.md +412 -0
  26. package/package.json +39 -0
  27. package/schemas/common.schema.json +251 -0
  28. package/schemas/inventory.schema.json +15 -0
  29. package/schemas/operation.schema.json +286 -0
  30. package/schemas/pipeline.schema.json +317 -0
  31. package/schemas/state.schema.json +302 -0
  32. package/schemas/workspace.schema.json +67 -0
  33. package/src/cli.js +7 -0
  34. package/src/commands/adopt.js +2 -0
  35. package/src/commands/bootstrap-recovery.js +90 -0
  36. package/src/commands/dispatch.js +394 -0
  37. package/src/commands/init.js +84 -0
  38. package/src/commands/launch.js +69 -0
  39. package/src/commands/migration-apply.js +53 -0
  40. package/src/commands/migration.js +50 -0
  41. package/src/commands/repositories.js +61 -0
  42. package/src/commands/repository-abandon.js +16 -0
  43. package/src/commands/repository-ancestors.js +21 -0
  44. package/src/commands/repository-locks.js +77 -0
  45. package/src/contracts/parse.js +57 -0
  46. package/src/contracts/semantic.js +240 -0
  47. package/src/contracts/validate.js +21 -0
  48. package/src/launch/grok.js +20 -0
  49. package/src/migrations/legacy-unity-begin.js +35 -0
  50. package/src/migrations/legacy-unity-compensate.js +82 -0
  51. package/src/migrations/legacy-unity-deactivate.js +60 -0
  52. package/src/migrations/legacy-unity-deactivation-resume-apply.js +62 -0
  53. package/src/migrations/legacy-unity-deactivation-resume.js +81 -0
  54. package/src/migrations/legacy-unity-finalize.js +88 -0
  55. package/src/migrations/legacy-unity-install-recovery.js +76 -0
  56. package/src/migrations/legacy-unity-install-resume.js +60 -0
  57. package/src/migrations/legacy-unity-install.js +88 -0
  58. package/src/migrations/legacy-unity-lease.js +136 -0
  59. package/src/migrations/legacy-unity-preflight.js +67 -0
  60. package/src/migrations/legacy-unity-preview.js +91 -0
  61. package/src/migrations/legacy-unity-resume-apply.js +39 -0
  62. package/src/migrations/legacy-unity-resume.js +41 -0
  63. package/src/migrations/legacy-unity-resumed-evidence.js +88 -0
  64. package/src/migrations/legacy-unity.js +99 -0
  65. package/src/operations/apply.js +576 -0
  66. package/src/operations/backup.js +94 -0
  67. package/src/operations/bootstrap-lock.js +87 -0
  68. package/src/operations/bootstrap-owner-retirement.js +121 -0
  69. package/src/operations/bundle-update.js +54 -0
  70. package/src/operations/config-fields.js +24 -0
  71. package/src/operations/continuation-lifecycle.js +77 -0
  72. package/src/operations/doctor.js +214 -0
  73. package/src/operations/history.js +170 -0
  74. package/src/operations/installer-identity.js +49 -0
  75. package/src/operations/journal.js +142 -0
  76. package/src/operations/lifecycle.js +133 -0
  77. package/src/operations/lineage-guard.js +20 -0
  78. package/src/operations/lock.js +109 -0
  79. package/src/operations/maintenance.js +62 -0
  80. package/src/operations/migration-pending.js +22 -0
  81. package/src/operations/ownership.js +122 -0
  82. package/src/operations/plan.js +278 -0
  83. package/src/operations/reconciliation.js +112 -0
  84. package/src/operations/recovery-lease.js +66 -0
  85. package/src/operations/remove.js +140 -0
  86. package/src/operations/repair.js +188 -0
  87. package/src/operations/repository-abandon.js +192 -0
  88. package/src/operations/repository-ancestors.js +158 -0
  89. package/src/operations/repository-apply.js +122 -0
  90. package/src/operations/repository-authorization.js +48 -0
  91. package/src/operations/repository-bootstrap-continuation.js +190 -0
  92. package/src/operations/repository-bootstrap-reconcile.js +106 -0
  93. package/src/operations/repository-bootstrap-recover.js +114 -0
  94. package/src/operations/repository-bootstrap.js +82 -0
  95. package/src/operations/repository-clone.js +63 -0
  96. package/src/operations/repository-history.js +108 -0
  97. package/src/operations/repository-inputs.js +41 -0
  98. package/src/operations/repository-journal.js +127 -0
  99. package/src/operations/repository-lock-reconcile.js +401 -0
  100. package/src/operations/repository-pending.js +29 -0
  101. package/src/operations/repository-reconcile.js +182 -0
  102. package/src/operations/repository-resumption-approvals.js +77 -0
  103. package/src/operations/repository-retention-apply.js +75 -0
  104. package/src/operations/repository-retention.js +137 -0
  105. package/src/operations/repository-workspace.js +78 -0
  106. package/src/operations/retention-apply.js +133 -0
  107. package/src/operations/retention-combined-scan.js +30 -0
  108. package/src/operations/retention-combined.js +41 -0
  109. package/src/operations/retention-policy.js +65 -0
  110. package/src/operations/retention-receipts.js +126 -0
  111. package/src/operations/retention-scan.js +86 -0
  112. package/src/operations/retention.js +56 -0
  113. package/src/operations/state.js +210 -0
  114. package/src/operations/switch-activate.js +64 -0
  115. package/src/operations/switch-backups.js +29 -0
  116. package/src/operations/switch-continuation-journal.js +94 -0
  117. package/src/operations/switch-continuation-pending.js +60 -0
  118. package/src/operations/switch-continuation-records.js +109 -0
  119. package/src/operations/switch-continuation-recovery.js +91 -0
  120. package/src/operations/switch-continuation-runtime.js +135 -0
  121. package/src/operations/switch-continuation-store.js +120 -0
  122. package/src/operations/switch-continuation.js +76 -0
  123. package/src/operations/switch-execute.js +68 -0
  124. package/src/operations/switch-inspect.js +45 -0
  125. package/src/operations/switch-journal-store.js +109 -0
  126. package/src/operations/switch-journal.js +71 -0
  127. package/src/operations/switch-lifecycle.js +72 -0
  128. package/src/operations/switch-pending.js +43 -0
  129. package/src/operations/switch-preflight.js +73 -0
  130. package/src/operations/switch-prepare.js +75 -0
  131. package/src/operations/switch-records.js +60 -0
  132. package/src/operations/switch-recovery-store.js +74 -0
  133. package/src/operations/switch.js +52 -0
  134. package/src/operations/toml-fields.js +133 -0
  135. package/src/providers/bundles.js +42 -0
  136. package/src/providers/common-entry.js +16 -0
  137. package/src/providers/grok.js +26 -0
  138. package/src/providers/interface.js +25 -0
  139. package/src/providers/kimi.js +26 -0
  140. package/src/providers/native.js +155 -0
  141. package/src/providers/registry.js +10 -0
  142. package/src/providers/shared.js +51 -0
  143. package/src/providers/source.js +30 -0
  144. package/src/source/git.js +303 -0
  145. package/src/source/inventory.js +87 -0
  146. package/src/source/repository-budget.js +18 -0
  147. package/src/source/snapshot.js +37 -0
  148. package/src/workspace/paths.js +54 -0
  149. package/src/workspace/profiles.js +8 -0
  150. package/src/workspace/repositories.js +57 -0
  151. package/src/workspace/repository-inventory.js +77 -0
  152. package/src/workspace/repository-observation.js +38 -0
  153. package/src/workspace/repository-preflight.js +129 -0
  154. package/src/workspace/repository-preview.js +196 -0
  155. package/src/workspace/repository-tree.js +57 -0
  156. package/src/workspace/reserved.js +11 -0
  157. package/src/workspace/resolve.js +53 -0
@@ -0,0 +1,204 @@
1
+ # Repository recovery — development checkpoint
2
+
3
+ Repository recovery does not install providers or automatically replay/undo effects.
4
+ Use `finalize` for verified completed operations, `recover-locks` for native-verified
5
+ stopped-owner retirement, and `abandon` for an explicit decision to preserve the
6
+ observed partial state without claiming completion. See the sections below and
7
+ [manual decision boundaries](repository-manual-recovery.md) for unbound/torn inputs.
8
+ Author checks and independent S7 acceptance remain separate.
9
+
10
+ Run from any directory, naming the wrapper explicitly:
11
+
12
+ ```powershell
13
+ workspace-pipeline repositories status --workspace "C:\Work\MyWorkspace"
14
+ workspace-pipeline repositories finalize --workspace "C:\Work\MyWorkspace"
15
+ ```
16
+
17
+ Both commands are read-only and print JSON. Save the complete finalize JSON to a
18
+ private UTF-8 file, inspect its operation and blockers, then explicitly apply:
19
+
20
+ ```powershell
21
+ workspace-pipeline repositories finalize --workspace "C:\Work\MyWorkspace" --apply --preview "C:\Private\finalize-preview.json"
22
+ ```
23
+
24
+ The preview must still match the workspace, retained inputs, evidence and observed
25
+ repository contents. Apply does not refresh an outdated preview or fetch source.
26
+ If a previous attempt already wrote the exact completion receipt but did not move
27
+ the pending marker, the preview selects receipt resumption. No repository effect
28
+ is repeated. Missing/damaged historical inputs or conflicting evidence are errors,
29
+ not permission to guess. Old installations without retained inputs are not silently
30
+ migrated by these commands.
31
+
32
+ New operations save execution inputs under `.pipeline/repository-inputs/`, before
33
+ repository effects. Keep this directory private and do not remove its files while
34
+ an operation is unresolved. `logs list` shows the locations. S7 history is currently
35
+ protected when pending or referenced; `logs clean --repositories` can select only
36
+ native-verified unreferenced completed groups with explicit age/count/run limits.
37
+ Ordinary `logs clean` keeps its existing separate scope. Do not manually
38
+ delete pending markers or lock directories to make `doctor` report success.
39
+
40
+ `no-pending-marker` means exactly that; it does not certify historical completion,
41
+ provider readiness or game runtime. A blocker means further recovery is needed;
42
+ this command never grants itself authority to resolve it. `--json` is redundant
43
+ but accepted because JSON is already the default output.
44
+
45
+ ## Local recovery ownership
46
+
47
+ Recovery writers use an ephemeral local IPC lease: a Windows named pipe, or a
48
+ Linux abstract socket. This is not TCP and does not expose a data protocol. Process
49
+ exit releases the kernel-owned endpoint; progress remains in existing durable
50
+ records. Different recoverers cannot simply adopt a dead owner's directory.
51
+ Persistent pending guards continue to protect ordinary lifecycle writers.
52
+
53
+ The current implementation supports this primitive on Windows/Linux only; other
54
+ platforms fail explicitly. Windows synthetic tests exist; Linux runtime validation
55
+ is still pending. It is not a distributed lock for shared/network filesystems and
56
+ does not isolate arbitrary editors or hostile processes running as the same user.
57
+ See [Node IPC documentation](https://nodejs.org/api/net.html#ipc-support).
58
+
59
+ Never interpret an old approval or a stopped PID alone as permission to continue.
60
+ Repeated continuation requires a new exact observed-state approval. Remaining
61
+ bootstrap and partial-effect routes are described below. The Windows ownership checkpoint
62
+ was independently reviewed; this is not full S7 acceptance.
63
+
64
+ Existing wrapper roots and ancestors must not be links/junctions/reparse aliases;
65
+ they are rejected, not assigned a second lease identity. Persistent record names
66
+ retain their existing spelling; this change does not rename historical records.
67
+
68
+ `recovery-lease.busy-or-access-denied` means the OS returned EACCES: contention
69
+ and a permission problem cannot be distinguished safely here. No lock is stolen.
70
+ Errors ending in `status-required` identify lease loss or release-phase failure,
71
+ possibly after durable effects completed. Inspect status and retained records before
72
+ retrying; the error does not mean rollback occurred. A simultaneous callback and
73
+ release failure is reported separately, without leaking raw OS error text.
74
+
75
+ ## Stopped-owner lock recovery
76
+
77
+ For an evidenced, completed repository operation whose locks were retained by a
78
+ stopped process, select the exact journal UUID from its pending/completion record
79
+ or `logs list`. Do not choose an operation by timestamp or assume the newest UUID.
80
+
81
+ ```text
82
+ workspace-pipeline repositories recover-locks --workspace <absolute-wrapper> --journal <journal-uuid>
83
+ workspace-pipeline repositories recover-locks --workspace <absolute-wrapper> --journal <journal-uuid> --apply --preview <absolute-preview.json>
84
+ ```
85
+
86
+ The first command is read-only. Save its complete JSON privately in UTF-8 and
87
+ inspect it before the second command. The preview identifies initial retirement,
88
+ interrupted retirement or repeated continuation, binds retained inputs and exact
89
+ observed state, and blocks live/unknown owners. Apply rechecks under native kernel
90
+ ownership. Original locks/owner records are retained, not recursively deleted.
91
+ The command neither replays repository effects nor configures providers. A pending
92
+ finalization remains a separate `repositories finalize` action after locks retire.
93
+
94
+ Repeated continuation now writes `continuation-NNNNNN.json` inside the existing
95
+ resumption directory **before** remaining recovery actions. These are immutable
96
+ authorization records, not additional locks. Each binds the fresh observation and
97
+ the hashes of preceding records. Completion retains the directory under the original
98
+ lock-recovery archive; `logs list` reports that protected archive. An already-complete
99
+ native continuation is read-only and does not append another approval. Active chains
100
+ are bounded to 128 records / 32 MiB and individual records to the normal input cap;
101
+ limit errors stop before another append, without deleting history.
102
+
103
+ This route is deliberately limited to evidenced completed repository effects.
104
+ Incomplete bootstrap creation, missing old retained inputs, uncertain effects and
105
+ partial authorization-file writes still require separate handling. An error never
106
+ authorizes blind lock removal or replay. S7 retention and those recovery routes
107
+ are not declared complete.
108
+
109
+ ## Interrupted bootstrap recovery
110
+
111
+ New bootstrap recovery attempts preserve the complete original request beside the
112
+ recovery owner, before relocating the original lock. If that recovery process dies,
113
+ the following command reconstructs the remaining actions without source acquisition:
114
+
115
+ ```text
116
+ workspace-pipeline repositories continue-bootstrap --workspace <absolute-wrapper>
117
+ workspace-pipeline repositories continue-bootstrap --workspace <absolute-wrapper> --apply --preview <absolute-preview.json>
118
+ ```
119
+
120
+ Save and inspect the first command's JSON privately in UTF-8. The second command
121
+ rechecks that exact preview under exclusive recovery ownership. Only a stopped,
122
+ local owner is eligible. Continuation does not recreate the wrapper or replay a
123
+ repository operation. It may finish moving the original intent/lock, persist the
124
+ exact recovery receipt, and archive the recovery gate. It preserves foreign data
125
+ and refuses inconsistent, missing, duplicated or damaged evidence.
126
+
127
+ The original request and subsequent approvals are retained in the sibling directory
128
+ `.wpc-bootstrap-recovery-<initial-reconciliation-hash>`. `logs list` shows its path
129
+ as protected recovery evidence, outside completed-transaction cleanup. To inspect completed
130
+ history, add `--initial sha256:<initial-reconciliation-hash>` explicitly. Completed
131
+ verification performs no write; subsequent legitimate wrapper changes may invalidate
132
+ the old completion observation and are not silently ignored.
133
+
134
+ The active guard admits at most 64 continuation approvals and a bounded 16 MiB
135
+ inventory; each input is still subject to the normal per-record size cap. Reaching
136
+ a limit does not authorize deleting active history. A crash before the original
137
+ request is fully published, legacy owner-only guards, torn approvals, or wrapper
138
+ creation without a consistent receipt are still unsupported recovery cases. They
139
+ remain fail-closed, not guessed or auto-replayed. See the manual decision boundary.
140
+
141
+ ## Explicit abandonment of an unfinished repository operation
142
+
143
+ `repositories abandon --workspace <absolute-wrapper>` produces a private exact
144
+ preview for **preserve current state, abandon the old request**. Save and inspect
145
+ the preview, then use the same command with `--apply --preview <file>`. It records
146
+ target/source trees, all retained metadata, marker/input hashes and stopped lock
147
+ owners. A changed tree requires a new preview. Missing/torn owner information or
148
+ unknown/live owners cannot be approved through this command.
149
+
150
+ The operation does not retry clone/init/move, delete repository data, restore the
151
+ old state, or claim completion. It archives the pending marker and any known stopped
152
+ locks in `.wpc-repository-resolution-<uuid>` beside the wrapper. The archive retains
153
+ the exact decision and a receipt with `completed:false`. An ephemeral recovery lease
154
+ and durable recovery gate protect the remaining moves. A new init/adopt preview is
155
+ required afterwards; choose `keep` for retained usable repositories or separately
156
+ resolve unwanted partial data. A partial clone is not converted into a verified clone.
157
+
158
+ After interruption, run `repositories continue-abandon --workspace <wrapper>
159
+ --attempt <uuid>` for a fresh preview, then add `--apply --preview <file>`. The old
160
+ request and subsequent approvals remain immutable. Completed-history verification
161
+ is not a certification of later changes to the repository. If publication stopped
162
+ before a complete guard/request exists, consult the manual decision boundary; no
163
+ automatic deletion or request reconstruction is attempted.
164
+
165
+ ## Shared execution and its ownership boundary
166
+
167
+ Initial recovery and continuation share the same remaining-action engine within
168
+ each domain (bootstrap archival and paired repository-lock retirement). Approval
169
+ and ownership acquisition remain distinct: the initial call verifies its own live
170
+ gate owner; continuation requires the prior recorded owner to have stopped and
171
+ retains fresh approval. Both use the kernel lease and recheck evidence between
172
+ effects. A shared engine does not authorize replay of uncertain effects or repair
173
+ of incomplete records. Paired retirement rechecks the repository subject before
174
+ moving the second lock, not only after both moves.
175
+
176
+ ## Stopped owner before the bootstrap request
177
+
178
+ One early state has a deliberately narrow archival route: the wrapper is absent,
179
+ the bootstrap lock contains only a valid `owner.json`, and its local process is
180
+ confirmed stopped. No operation request or intent may be present.
181
+
182
+ ```text
183
+ workspace-pipeline repositories retire-bootstrap --workspace <absolute-wrapper>
184
+ workspace-pipeline repositories retire-bootstrap --workspace <absolute-wrapper> --apply --preview <absolute-preview.json>
185
+ ```
186
+
187
+ Save/inspect the first command's private UTF-8 JSON before apply. It names the exact
188
+ history destination `.wpc-bootstrap-abandoned-<attempt-uuid>` next to the wrapper.
189
+ Apply retains that preview before moving the entire old lock into `history/lock`.
190
+ The original lock blocks ordinary creators until the single final rename; the
191
+ existing kernel lease excludes competing recovery writers. No extra recovery lock,
192
+ repository replay, wrapper creation, deletion or claim of historical success occurs.
193
+
194
+ If interrupted before that rename, the old lock still blocks writers. Inspect a
195
+ **new preview** to attempt archival again; it chooses a new history location and
196
+ does not overwrite the earlier incomplete history. After the rename, only the
197
+ selected original lock's preserved history can be verified. This does not certify
198
+ the current wrapper state: another legitimate creator may already have started.
199
+
200
+ `logs list` reports attributable history as protected; unreadable/empty abandoned
201
+ history appears as a diagnostic with its location. Do not delete active records to
202
+ clear a blocker. These records remain protected recovery evidence. Empty, torn, foreign,
203
+ live or unknown owner records, recovery gates, existing wrappers and any intent or
204
+ extra lock files are outside this route and remain blocked.
@@ -0,0 +1,46 @@
1
+ # Repository execution history cleanup
2
+
3
+ Repository setup records are a separate cleanup domain from configuration journals.
4
+ List locations with `workspace-pipeline logs list --workspace <absolute-wrapper>`.
5
+ Record contents may contain private paths/configuration; keep previews and receipts
6
+ private. Listing never deletes anything.
7
+
8
+ ```text
9
+ workspace-pipeline logs clean --repositories --workspace <absolute-wrapper> --max-age-days 30 --keep-last 20 --max-delete 5
10
+ workspace-pipeline logs clean --repositories --workspace <absolute-wrapper> --apply --preview <private-saved-preview.json>
11
+ ```
12
+
13
+ Numbers above are examples, not defaults. All three limits are required; age OR
14
+ count makes an otherwise eligible group a candidate, and `max-delete` caps complete
15
+ groups per invocation. Local latest file modification time is the conservative age
16
+ anchor, not an invented execution date. A future timestamp protects the group.
17
+ The flag is required for both invocations; it cannot be mixed with receipt cleanup
18
+ options or policy commands. Existing startup policy remains configuration-journal
19
+ policy, not implicit repository cleanup authorization. There is no background job.
20
+
21
+ Only internally consistent completed transaction groups (journal, inputs,
22
+ authorization when present, result evidence and completion records) are eligible.
23
+ Referenced, active, pending, uncertain, malformed and current-run records are
24
+ protected. The scan checks bounded metadata and retained recovery references,
25
+ including escaped JSON UUIDs and hashes; it does not reread current game content
26
+ or assume it still equals an old completed operation. Unknown transaction formats
27
+ and uninspectable metadata fail closed. A pending configuration transaction also
28
+ blocks cleanup. Opaque retained records are reference roots, not deletion targets.
29
+
30
+ Bootstrap, ancestor, abandonment and lock-recovery histories remain protected
31
+ evidence. Repository cleanup receipts are also retained; this command does not
32
+ erase its own audit trail or unrelated configuration receipts. Age/count limits
33
+ may remain exceeded when records are protected. Inspect protection reasons rather
34
+ than deleting locks or inputs to force eligibility.
35
+
36
+ Apply holds the workspace lock and recovery lease, rechecks the exact selection
37
+ and references, records an execution receipt under `.pipeline/repository-cleanup/`,
38
+ and removes only listed files and empty group directories. It prints the receipt
39
+ path before deletion and reports partial failures. Deletion is not recoverable.
40
+ An interrupted cleanup is not auto-replayed: preserve its receipt and inspect the
41
+ reported current file/remaining groups before making a new decision.
42
+
43
+ Current scan bounds: 20,000 metadata entries, 128 MiB total, 8 MiB per file plus
44
+ the normal JSON input cap. Bound failures are explicit blockers, never partial
45
+ successful scans. No repository/code/assets, provider configs, installed snapshots
46
+ or backups are cleanup targets.
@@ -0,0 +1,26 @@
1
+ # Repository clone transport budgets
2
+
3
+ S7 repository acquisition is separate from S2 pipeline-package acquisition. Every
4
+ local and remote clone preview binds `packLimit`, `gitMs`, and `acquisitionMs`.
5
+ Current defaults and upper ceilings are 100 GiB of stored Git objects, 30 minutes
6
+ per Git command, and two hours for each acquisition/materialization Git sequence.
7
+ Smaller internal budgets are validated and bound in the same preview. They cannot
8
+ be increased during apply without a fresh preview. These are ceilings, not disk
9
+ reservations, download-size predictions, throughput guarantees, or total-workspace
10
+ execution deadlines. A remote preparation and subsequent local materialization
11
+ are distinct sequences and each can retain its object store after failure.
12
+
13
+ The live object monitor and final size check enforce the pack budget, with possible
14
+ monitor-interval overshoot. Timeout termination remains owned-process-only and an
15
+ unconfirmed termination remains an error. No retry or full-history fallback is
16
+ added. Source records and repository output are retained for explicit recovery.
17
+
18
+ Committed-tree listing has a separate 256 MiB bounded output allowance, followed
19
+ by the existing 200,000-entry and 100 GiB content checks. This does not permit
20
+ unbounded buffering or weaken path, Git mode, LFS, filter, or inventory checks.
21
+ Tests at/over transport ceilings use numeric boundaries and synthetic subprocess
22
+ results; they do not claim a real 100 GiB download or two-hour runtime exercise.
23
+
24
+ S2 remains 256 MiB stored objects, 120 seconds per Git command, 300 seconds total
25
+ acquisition, and its original package blob/tree/output limits. Supplying repository
26
+ budgets to the S2 runner does not enable the S7 transport profile.
@@ -0,0 +1,237 @@
1
+ # Technical journal retention
2
+
3
+ The internal `scanRetention(workspace, {policy, now, currentRuns})` API produces
4
+ a read-only filesystem preview. Explicit CLI cleanup is available; automatic
5
+ startup deletion is not enabled.
6
+ Policy requires explicit `maxAgeDays`, `maxJournals`, `maxDeletesPerRun`; no defaults
7
+ are silently selected by a pipeline package.
8
+
9
+ The scan reports absolute journal/recovery paths, status, byte counts, exact file
10
+ hash/mtime inventory and protection reasons without returning record contents.
11
+ Age uses local metadata modification time, not an inferred activation timestamp.
12
+ The active transaction is always selected by explicit state/record bindings.
13
+
14
+ Current state/current run, incomplete records, corrupt/unknown/orphan evidence
15
+ and retained references are protected. References are conservatively collected
16
+ from record paths: even an eligible record protects the history it references.
17
+ Ambiguous inventory or state protects all groups. Only unreferenced completed
18
+ groups can be selected by age OR count, oldest first, subject to the per-run cap.
19
+ Protected entries still contribute to totals and unmet limits.
20
+
21
+ The scanner inspects exact UUID groups, permits only sequential JSON event names
22
+ and recovery.json, refuses links/foreign files, bounds reads to 10,000 files and
23
+ 64 MiB, and repeats inventory, hashes, timestamps, history and state checks.
24
+ This is not an atomic filesystem snapshot. The locked cleanup executor revalidates
25
+ before mutation and preserves diagnostics for partial deletion.
26
+
27
+ Backups, snapshots, repositories, provider configs and project documentation are
28
+ outside deletion scope. Do not remove `.pipeline` wholesale. The preview's
29
+ `applySupported` and `automaticActions` remain false: it is a proposal, not an
30
+ execution capability. `applyRetention` performs its own locked validation.
31
+
32
+ ## CLI usage (development checkout)
33
+
34
+ `logs list` also includes a `repositories` section for the S7 repository journals,
35
+ evidence, authorization chains, completion records and bootstrap/recovery locations.
36
+ This includes `.pipeline/repository-lock-recoveries/<digest>/` archives; their
37
+ original owner records and recovery receipts remain protected from cleanup.
38
+ It reports absolute paths only, not record contents. `validation: locations-only`
39
+ means directory discovery, NOT semantic validation of journal chains or proof of a
40
+ completed operation. Its `complete` flag describes this bounded listing only.
41
+
42
+ S7 entries have `deletionEligible: false` and are not selected by `logs clean` yet.
43
+ Do not manually clear a pending marker or bootstrap/recovery lock to bypass an
44
+ unfinished operation. S7 age/count/per-run retention remains unimplemented.
45
+ Sibling `.wpc-bootstrap-history-*` entries are attributed using the exact workspace
46
+ in their owner record; malformed associations are reported as incomplete. Enumeration
47
+ is bounded to 1,000 relevant entries/candidates and 100,000 parent directory names;
48
+ exceeding a limit is incomplete, not an empty successful listing. Links are not followed.
49
+
50
+ Run from the CLI repository, replacing `C:/Work/Game` with your wrapper:
51
+
52
+ ```powershell
53
+ node ./src/cli.js logs list --workspace "C:/Work/Game"
54
+ node ./src/cli.js logs clean --workspace "C:/Work/Game" --max-age-days 30 --keep-last 20 --max-delete 5 | Set-Content -Encoding utf8 "C:/Work/cleanup-preview.json"
55
+ ```
56
+
57
+ The numbers are examples, not defaults. List chooses no retention policy and
58
+ never deletes. Inspect the saved preview: selected IDs, absolute paths, byte
59
+ counts and protection reasons. Then explicitly apply that same file:
60
+
61
+ ```powershell
62
+ node ./src/cli.js logs clean --workspace "C:/Work/Game" --apply --preview "C:/Work/cleanup-preview.json"
63
+ ```
64
+
65
+ Changed state, records, timestamps or inventory invalidate the preview. Generate
66
+ and inspect a fresh one rather than forcing it. `--apply` cannot be mixed with
67
+ policy flags and does not silently substitute a newly computed selection.
68
+
69
+ ## Deletion and failure reports
70
+
71
+ Only enumerated event/recovery files and then empty group directories are
72
+ removed. No recursive deletion is used. Each remaining recovery record and state
73
+ is checked before every file deletion to detect new references; this conservative
74
+ check has cost proportional to selected files times retained records, bounded by
75
+ scan limits and the user deletion cap. It is not a linear-cost guarantee or an
76
+ OS-wide defense against hostile concurrent filesystem writers.
77
+
78
+ Progress is emitted as JSON on stderr; final JSON is on stdout. The receipt path
79
+ is under `.pipeline/cleanup/<run-id>.json`. Receipts record selected file hashes,
80
+ confirmed deletions, reclaimed bytes, current intent and failures. These receipts
81
+ describe attempted/deleted paths; unlike operational recovery records, they are
82
+ not dependencies requiring deleted file contents to remain. Deletion is not
83
+ recoverable by this CLI. A crash may leave an intent without confirmation;
84
+ do not treat that as successful cleanup. Partial groups are protected by later
85
+ scans; locks are never stolen and cleanup does not auto-resume or roll back.
86
+
87
+ Exit 0 means the requested cleanup completed, 1 means an incomplete/failed result,
88
+ 2 means invocation/preflight failure. Output transport can fail independently of
89
+ filesystem completion; check the receipt rather than blindly retrying. Cleanup
90
+ receipts from actual deletions are retained under v1; only an explicitly selected combined v2 policy
91
+ or manual combined preview may rotate eligible completed receipts.
92
+
93
+ ## Setup/update startup integration (internal coordinator)
94
+
95
+ The trusted caller may pass `retention: {preview, approval}` alongside the
96
+ `report` option to `applyLifecycle`. The approval is the separate exact cleanup
97
+ approval `{decision: 'approve', previewDigest: preview.digest}`; approving the
98
+ installation alone does not authorize deleting history. The saved preview shows
99
+ all three user limits. No policy is loaded from the pipeline supplier and there
100
+ are no implicit numeric defaults. A separately enabled local automatic policy is
101
+ described below; combining it with this manual startup option is rejected.
102
+
103
+ The coordinator holds one workspace lock, preflights the main operation before
104
+ cleanup, announces the validated policy, applies the exact cleanup, then repeats
105
+ history and installation checks before any setup/update target writes. The new
106
+ operation's journal is created only after cleanup; existing active/pending
107
+ history is protected by the scanner. Read-only commands never enter this path.
108
+
109
+ Results contain a separate `cleanup` result including its receipt path when
110
+ available. Cleanup failure or a failed cleanup report prevents the main operation
111
+ from starting (`status: 'not-started'`). If cleanup succeeds but setup/update
112
+ subsequently fails, the completed cleanup remains reported separately; it is not
113
+ rolled back or presented as successful installation. Invalid main approval fails
114
+ before cleanup. Locks are released normally; abandoned locks are not reclaimed.
115
+
116
+ Public setup/update use this coordinator and the separately configured local
117
+ retention policy. They do not accept implicit cleanup defaults.
118
+
119
+ ## Local automatic policy
120
+
121
+ `.pipeline/retention.json` is user-owned wrapper metadata, not pipeline content.
122
+ Missing means disabled, with no limits invented. Invalid existing bytes block
123
+ mutating startup instead of falling back to defaults. A strict v1 record contains
124
+ `schemaVersion: 1`, `kind: "workspace-retention-policy"`, the absolute `workspace`,
125
+ `mode` (`automatic` or `disabled`), and `journals` with all three existing limits.
126
+ Unknown fields, duplicates and unsafe numeric values are rejected. Copying the
127
+ file to a different workspace does not grant cleanup there. Schema version is
128
+ data compatibility, not a project-task gate.
129
+
130
+ Development-checkout commands (example numbers are not defaults):
131
+
132
+ ```powershell
133
+ node ./src/cli.js logs policy show --workspace "C:/Work/Game"
134
+ node ./src/cli.js logs policy set --workspace "C:/Work/Game" --mode automatic --max-age-days 30 --keep-last 20 --max-delete 5 | Set-Content -Encoding utf8 "C:/Work/policy-preview.json"
135
+ node ./src/cli.js logs policy set --workspace "C:/Work/Game" --apply --preview "C:/Work/policy-preview.json"
136
+ node ./src/cli.js logs policy disable --workspace "C:/Work/Game" | Set-Content -Encoding utf8 "C:/Work/disable-preview.json"
137
+ node ./src/cli.js logs policy disable --workspace "C:/Work/Game" --apply --preview "C:/Work/disable-preview.json"
138
+ ```
139
+
140
+ Inspect the preview before apply. Enabling automatic mode authorizes **future**
141
+ eligible journal deletions within the chosen limits. These configuration commands
142
+ do not delete history or install a pipeline. Writes verify exact before bytes
143
+ under lock. Disabling preserves limits; absent policy is already disabled.
144
+
145
+ The internal setup/update coordinator reads this file after main preflight and
146
+ selects eligible groups afresh. Policy path/hash, limits, protected count and
147
+ unmet totals are reported before removal. Receipts distinguish `workspace-policy`
148
+ authority (exact policy hash) from `approve` (exact manual preview digest).
149
+ The executor checks policy bytes at deletion boundaries; drift stops cleanup.
150
+ There is one bounded pass per invocation, with no automatic + manual double budget.
151
+ Read-only commands never run it. Supplier configuration is not read as user policy.
152
+ The file/digest is not tamper-proof against a process already able to write the
153
+ user’s filesystem. Policy itself is not part of immutable deployment approval.
154
+
155
+ ## Combined receipt retention (explicit v2 opt-in)
156
+
157
+ `planCombinedRetention` independently computes age/count candidates for journals
158
+ and receipts, then merges them oldest-first under one deletion cap. Typed identities
159
+ keep equal UUIDs across classes distinct. This pure output is not deletion permission.
160
+ `scanCleanupReceipts` inspects bounded local receipts and operational references,
161
+ then rechecks hashes, inventory and history. Only known, internally consistent
162
+ completed receipts qualify; current, incomplete, unknown and linked records stay
163
+ protected. Missing reference evidence prevents usable cleanup inventory.
164
+
165
+ `scanCombinedRetention` binds both inventories. The common executor deletes the
166
+ selected typed items under one lock and one cap, retaining its own current receipt.
167
+ Receipt descriptors describe deleted bytes and do not create immortal dependencies.
168
+ Failed/partial/unknown records remain protected. Drift, including policy changes,
169
+ stops the pass. No recursive deletion, implicit retry or budget reset is used.
170
+
171
+ Add BOTH `--receipt-max-age-days N` and `--keep-receipts N` to a `logs clean`
172
+ preview or `logs policy set` preview. `--max-delete N` is then the combined cap:
173
+ one journal/recovery group or one receipt counts as one item. Apply still uses
174
+ only `--apply --preview <file>`. `logs list` shows both inventories read-only.
175
+
176
+ V2 local policy keeps `journals: {maxAgeDays, maxJournals}` and adds
177
+ `cleanupReceipts: {maxAgeDays, maxReceipts}` plus top-level `maxDeletesPerRun`.
178
+ The schemaVersion is 2; v1 is not rewritten or expanded automatically. Disable
179
+ preserves the schema/limits; an explicit v1 set-preview can withdraw receipt
180
+ authority. These versions are CLI metadata, not project governance gates.
181
+
182
+ Both v1 and v2 create no cleanup receipt if nothing is selected, returning null
183
+ receiptPath/runId and zero counts. This deliberately changes v1 no-work output:
184
+ callers must respect receiptCreated:false rather than assuming a receipt path.
185
+ A successful combined pass creates a schema-2 receipt; later scans validate its
186
+ typed inventory and completion before it can become eligible. Current-run and
187
+ operationally referenced receipts remain protected. Full release certification
188
+ still requires independent review and packaged/live-provider tests.
189
+
190
+ ## Receipt scan bound and manual recovery
191
+
192
+ The receipt scanner accepts at most 1,000 directory entries (also bounded by
193
+ 64 MiB). Above that, retention-receipts.limit blocks logs list and combined v2
194
+ cleanup previews; it does not certify a partial inventory. V1 can still accumulate
195
+ receipts from real deletions. Use explicit v2 receipt rotation before reaching
196
+ this bound. Skipping empty v1 receipts does not cure an already oversized directory.
197
+
198
+ There is no automatic overflow recovery or force-clean command. Manual recovery
199
+ is an administrator operation, not permission to delete files by age/name:
200
+
201
+ 1. Stop CLI/harness writers for this workspace and establish that no writer is
202
+ alive. An existing lock requires its documented owner/liveness investigation;
203
+ do not remove it merely to get past this limit.
204
+ 2. Back up the workspace metadata securely. Receipts and scratch bytes can contain
205
+ private paths/configuration; do not upload them. Record exact paths and hashes.
206
+ 3. Inspect candidate receipt JSON individually using the current strict completion
207
+ validation, and inspect ALL state, transaction/recovery, journal and retained
208
+ receipt references. Only known, valid, completed, unreferenced receipts qualify.
209
+ Empty legacy v1 receipts still require those checks. Unknown, partial, linked,
210
+ referenced or corrupt entries remain in place. If reference independence cannot
211
+ be established, stop and obtain expert repair; the scan limit is not a waiver.
212
+ 4. With explicit approval of an exact path/hash list, move only those qualifying
213
+ receipt files to a private quarantine OUTSIDE the workspace until the directory
214
+ has at most 1,000 entries. Recheck hashes and references immediately before
215
+ moving. Use exact literal paths, never wildcards, recursive deletion or moving
216
+ the whole .pipeline directory. Keep the quarantine and move manifest recoverable.
217
+ 5. Rerun doctor and logs list. Resolve diagnostics before writes, then preview and
218
+ approve a combined v2 policy with explicit receipt limits. Do not blindly restore
219
+ quarantined files into a now changed workspace or delete the quarantine as part
220
+ of this procedure.
221
+
222
+ ## Scratch files left by a crash
223
+
224
+ Replacement writes may leave .wpc-<uuid>.tmp beside a target; pending-state writes
225
+ may leave .pipeline/.switch-state-<uuid>.tmp or .pipeline/.continuation-state-<uuid>.tmp.
226
+ They may contain partial or complete private configuration bytes. They are not
227
+ deployment state, not eligible journal receipts, and are intentionally not removed
228
+ by logs clean or pipeline removal. A name match or old timestamp proves nothing.
229
+
230
+ For manual cleanup, first stop writers and resolve pending recovery/locks. Establish
231
+ the exact file's provenance from the interrupted operation, verify it is a regular
232
+ non-linked file, ensure no live writer or recovery step needs it, and compare the
233
+ actual target/state against the approved recovery result. If uncertain, leave it.
234
+ After exact path/hash approval, move only verified leftovers to secure quarantine
235
+ outside the workspace and rerun doctor. Preserve unknown files, provider settings,
236
+ backups and snapshots. No blanket .tmp deletion is safe, and this CLI does not
237
+ currently automate scratch inventory or reclamation.
package/docs/source.md ADDED
@@ -0,0 +1,50 @@
1
+ # Git pipeline sources (library and CLI)
2
+
3
+ The installer reads committed pipeline bytes, not working-tree changes. Local
4
+ refs are interpreted in isolated preparation storage; final snapshots contain
5
+ independent files. Source repositories, their refs/index/config and user credentials
6
+ are not modified. No checkout, smudge filter, automatic history fallback or pipeline
7
+ script execution is performed.
8
+
9
+ ## Remote commit pins
10
+
11
+ A full lowercase commit ID is a valid source selector, but the remote server must
12
+ permit fetching that object. Some servers reject a SHA that is not an advertised
13
+ branch/tag tip, even if it is an ancestor of a visible branch. This is a server
14
+ capability/access boundary, not permission to choose a different commit.
15
+
16
+ `source.unadvertised-commit` means Git emitted the recognized refusal to request an
17
+ unadvertised object. Use an advertised branch/tag pointing to the intended commit,
18
+ a local repository containing that commit, or ask the repository administrator
19
+ about the server policy. The installer does not change server settings, retry with
20
+ full history, or silently substitute the latest revision.
21
+
22
+ Unknown/localized server errors, connection and authorization failures remain
23
+ `source.git-failed`; they must not be diagnosed as an unadvertised-commit refusal
24
+ without the specific diagnostic. Raw Git stderr is never returned. Both successful
25
+ tip pins and permitted non-tip pins remain supported. Live host/account behavior
26
+ is separate from synthetic transport tests.
27
+
28
+ ## Errors and local recovery metadata
29
+
30
+ - `source.missing-repository`: local source realpath failed with ENOENT.
31
+ - `source.repository-unavailable`: another local source resolution failure.
32
+ - `source.io` / `snapshot.io`: other unclassified preparation I/O failure, without
33
+ embedding raw filesystem error messages or the source path.
34
+ - After creation, failures expose `error.preparation` and/or `error.snapshotPath`
35
+ as exact generated temporary roots. A root not yet created has no such field.
36
+ These paths are machine-local recovery metadata, not portable manifests; keep
37
+ them out of shared/public diagnostics. They do not authorize recursive deletion
38
+ without ownership and current path checks. No automatic cleanup is performed.
39
+ - `source.termination-unconfirmed` preserves the owned root `processId` and does
40
+ not claim all descendants stopped. Do not retry or activate the result; inspect
41
+ process identity before later cleanup because a PID may have been reused.
42
+
43
+ 256 MiB is a stop/reject threshold on fetched object storage, not a disk quota.
44
+ Transient overshoot between measurements is possible; oversized results are never
45
+ returned for installation. Raw file, inventory, manifest and path limits also apply.
46
+
47
+ The optional fourth `runGit` argument is a trusted code-only runtime seam used by
48
+ unit tests to simulate process-control failures. It is not manifest configuration
49
+ and must not be populated from pipeline/user data. Tests use simulated processes
50
+ for kill-failure branches, avoiding real unkillable helpers.