@logicseek/hello-cc 0.1.9 → 1.0.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.
- package/CHANGELOG.md +59 -0
- package/README.md +18 -3
- package/README.zh-CN.md +17 -3
- package/bin/hcc.mjs +3 -5656
- package/docs/README.md +8 -0
- package/docs/README.zh-CN.md +6 -0
- package/docs/architecture.md +29 -18
- package/docs/architecture.zh-CN.md +20 -10
- package/docs/commands.md +13 -4
- package/docs/commands.zh-CN.md +13 -4
- package/docs/current-state-review-2026-08.md +341 -0
- package/docs/defect-review-2026-07.md +382 -0
- package/docs/guide.md +39 -3
- package/docs/guide.zh-CN.md +34 -2
- package/docs/superpowers/plans/2026-08-03-runtime-safety-plan.md +294 -0
- package/docs/superpowers/plans/2026-08-03-v1-data-identity-plan.md +400 -0
- package/docs/superpowers/plans/2026-08-03-v1-release-verification-plan.md +284 -0
- package/docs/superpowers/plans/2026-08-03-web-api-hardening-plan.md +303 -0
- package/docs/superpowers/plans/2026-08-04-runtime-buffer-gc-two-phase.md +71 -0
- package/docs/superpowers/specs/2026-08-03-runtime-safety-design.md +117 -0
- package/docs/superpowers/specs/2026-08-03-v1-data-identity-design.md +120 -0
- package/docs/superpowers/specs/2026-08-03-v1-release-verification-design.md +117 -0
- package/docs/superpowers/specs/2026-08-03-web-api-hardening-design.md +104 -0
- package/lib/cli/app.mjs +642 -0
- package/lib/cli/commands/coordination.mjs +247 -0
- package/lib/cli/commands/doctor.mjs +81 -0
- package/lib/cli/commands/gc.mjs +852 -0
- package/lib/cli/commands/hook.mjs +205 -0
- package/lib/cli/commands/install.mjs +294 -0
- package/lib/cli/commands/lifecycle.mjs +101 -0
- package/lib/cli/commands/lock.mjs +197 -0
- package/lib/cli/commands/misc.mjs +61 -0
- package/lib/cli/commands/msg.mjs +152 -0
- package/lib/cli/commands/peer.mjs +233 -0
- package/lib/cli/commands/query.mjs +96 -0
- package/lib/cli/commands/run.mjs +463 -0
- package/lib/cli/commands/task.mjs +405 -0
- package/lib/cli/commands/team.mjs +131 -0
- package/lib/cli/commands/tmux.mjs +838 -0
- package/lib/cli/commands/up.mjs +135 -0
- package/lib/cli-args.mjs +16 -2
- package/lib/coordination-state.mjs +112 -51
- package/lib/core/coordination/automation.mjs +5 -8
- package/lib/core/coordination/clock-safety.mjs +323 -0
- package/lib/core/coordination/event-retention.mjs +17 -0
- package/lib/core/coordination/gc-plan.mjs +614 -0
- package/lib/core/coordination/lease-renewal.mjs +102 -0
- package/lib/core/coordination/lock-evidence.mjs +77 -0
- package/lib/core/coordination/optimistic-evidence.mjs +26 -0
- package/lib/core/coordination/tasks.mjs +156 -36
- package/lib/core/peers/evidence-runtime.mjs +312 -0
- package/lib/core/peers/evidence.mjs +103 -0
- package/lib/core/peers/hook-owner.mjs +32 -0
- package/lib/core/peers/liveness.mjs +44 -4
- package/lib/core/peers/peer-helpers.mjs +121 -0
- package/lib/core/peers/session.mjs +8 -2
- package/lib/core/peers/tmux-safety.mjs +290 -0
- package/lib/core/sessions/launch.mjs +13 -1
- package/lib/db/connection.mjs +104 -0
- package/lib/db/events.mjs +34 -0
- package/lib/db/migration-backup.mjs +225 -0
- package/lib/db/schema.mjs +102 -5
- package/lib/db/stores/peers.mjs +34 -0
- package/lib/integrations/peers/identity.mjs +62 -16
- package/lib/integrations/shims/script.mjs +111 -9
- package/lib/process/identity.mjs +192 -0
- package/lib/process/pty-lifecycle.mjs +149 -0
- package/lib/runtime/buffer-directory-lease.mjs +47 -0
- package/lib/runtime/buffer-evidence.mjs +336 -0
- package/lib/runtime/buffer-gc-protocol.mjs +167 -0
- package/lib/runtime/buffer-gc.mjs +270 -0
- package/lib/runtime/client.mjs +60 -7
- package/lib/runtime/fatal-shutdown.mjs +54 -0
- package/lib/runtime/project-path.mjs +139 -0
- package/lib/runtime/projects.mjs +320 -40
- package/lib/runtime/state.mjs +227 -27
- package/lib/shared/cleanup-error.mjs +54 -0
- package/lib/shared/clock-grace.mjs +106 -0
- package/lib/shared/errors.mjs +9 -0
- package/lib/shared/file-lock.mjs +370 -0
- package/lib/shared/json-file.mjs +6 -2
- package/lib/shared/redact.mjs +60 -0
- package/lib/shared/socket-lock-worker.mjs +201 -0
- package/lib/terminal/tmux.mjs +78 -4
- package/lib/tmux.mjs +4 -0
- package/lib/ui/help.mjs +53 -15
- package/lib/web/api-version.mjs +42 -0
- package/lib/web/auto-attach.mjs +185 -0
- package/lib/web/buffer-gc-runtime.mjs +175 -0
- package/lib/web/cookie-auth.mjs +119 -0
- package/lib/web/external-sessions.mjs +254 -0
- package/lib/web/http-routes.mjs +542 -0
- package/lib/web/http.mjs +135 -6
- package/lib/web/liveness-reaper.mjs +145 -0
- package/lib/web/peer-actions.mjs +132 -23
- package/lib/web/project-contexts.mjs +178 -0
- package/lib/web/pty-sessions.mjs +203 -0
- package/lib/web/runtime-main.mjs +714 -0
- package/lib/web/runtime.mjs +91 -36
- package/lib/web/session-serialize.mjs +0 -0
- package/lib/web/startup.mjs +333 -0
- package/lib/web/tls.mjs +365 -0
- package/lib/web/tmux-sessions.mjs +1361 -0
- package/lib/web/tmux-stream.mjs +166 -0
- package/lib/web/ui-template.mjs +152 -20
- package/package.json +5 -2
- package/scripts/check-free-identifiers.mjs +290 -0
- package/scripts/regression.mjs +5397 -263
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# hello-cc v1 Release and Verification Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Convert the completed hardening into a coherent 1.0.0 release with controlled fatal shutdown, complete packaging, accurate documentation, stable tests, and fresh cross-platform evidence.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A focused fatal-shutdown helper owns process-level failure behavior. Release metadata and documentation declare breaking changes and accepted risks. Verification audits the entire local delta against `origin/master`, not only the last patch.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js 24 ESM, npm packaging, Docker/BuildKit, tmux, SQLite, HTTP/HTTPS/WebSocket regression suite.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Map
|
|
14
|
+
|
|
15
|
+
- Create `lib/runtime/fatal-shutdown.mjs` and `test/fatal-shutdown.test.mjs`.
|
|
16
|
+
- Modify `bin/hcc.mjs` fatal handlers and shutdown orchestration.
|
|
17
|
+
- Modify `package.json`, `package-lock.json`, `CHANGELOG.md`.
|
|
18
|
+
- Modify root and `docs/` English/Chinese README, commands, and guide files.
|
|
19
|
+
- Modify `docs/defect-review-2026-07.md` status evidence.
|
|
20
|
+
- Track/modify `.dockerignore`, `Dockerfile`, `lib/shared/clock-grace.mjs`, `lib/web/tls.mjs`.
|
|
21
|
+
- Modify `scripts/regression.mjs` for deterministic TTL evidence and package audits.
|
|
22
|
+
|
|
23
|
+
### Task 1: Replace unknown-error survival with idempotent fatal shutdown
|
|
24
|
+
|
|
25
|
+
**Files:**
|
|
26
|
+
- Create: `lib/runtime/fatal-shutdown.mjs`
|
|
27
|
+
- Create: `test/fatal-shutdown.test.mjs`
|
|
28
|
+
- Modify: `bin/hcc.mjs:5190-5335`
|
|
29
|
+
|
|
30
|
+
- [ ] **Step 1: Write failing fatal-state tests**
|
|
31
|
+
|
|
32
|
+
Use a real `EventEmitter` as the process signal source and real promises for asynchronous cleanup. Do not mock cleanup behavior; provide complete callbacks that append observable events.
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
const events = [];
|
|
36
|
+
const controller = createFatalShutdownController({
|
|
37
|
+
cleanup: async (reason) => { events.push(`cleanup:${reason}`); },
|
|
38
|
+
exit: (code) => { events.push(`exit:${code}`); },
|
|
39
|
+
forceExit: (code) => { events.push(`force:${code}`); },
|
|
40
|
+
log: (value) => { events.push(`log:${value.code}`); }
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await controller.fatal(new Error('first'));
|
|
44
|
+
await controller.fatal(new Error('second'));
|
|
45
|
+
assert.deepEqual(events, ['log:FATAL_RUNTIME_ERROR', 'cleanup:first', 'exit:1', 'force:1']);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Also assert cleanup failure still exits nonzero and its log is redacted.
|
|
49
|
+
|
|
50
|
+
- [ ] **Step 2: Run to verify RED**
|
|
51
|
+
|
|
52
|
+
Run: `node --test test/fatal-shutdown.test.mjs`.
|
|
53
|
+
|
|
54
|
+
Expected: missing-module failure.
|
|
55
|
+
|
|
56
|
+
- [ ] **Step 3: Implement the controller and wire Web shutdown**
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
export function createFatalShutdownController({ cleanup, exit, forceExit, log }) {
|
|
60
|
+
let state = 'idle';
|
|
61
|
+
return {
|
|
62
|
+
async fatal(error) {
|
|
63
|
+
if (state !== 'idle') {
|
|
64
|
+
forceExit(1);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
state = 'stopping';
|
|
68
|
+
log({ code: 'FATAL_RUNTIME_ERROR', error });
|
|
69
|
+
try { await cleanup(error?.message || 'fatal runtime error'); }
|
|
70
|
+
catch (cleanupError) { log({ code: 'FATAL_CLEANUP_ERROR', error: cleanupError }); }
|
|
71
|
+
state = 'stopped';
|
|
72
|
+
exit(1);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In `cmdWeb`, create one controller after all owned resources exist. `uncaughtException` and `unhandledRejection` call it. Cleanup clears timers/watchers, closes WebSockets/server, detaches or exits sessions by existing semantics, closes DB resources, and clears only runtime pointers matching the current process identity. Remove the log-and-continue handlers.
|
|
79
|
+
|
|
80
|
+
- [ ] **Step 4: Verify GREEN at unit and child-process boundaries**
|
|
81
|
+
|
|
82
|
+
Run unit tests. Add a child-process fixture that imports the helper, opens a temporary server/resource, triggers an unhandled rejection, and asserts cleanup marker, closed listener, and exit status 1.
|
|
83
|
+
|
|
84
|
+
Run: `npm run test:unit`.
|
|
85
|
+
|
|
86
|
+
Run: `npm run test:regression`.
|
|
87
|
+
|
|
88
|
+
- [ ] **Step 5: Commit**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git add lib/runtime/fatal-shutdown.mjs test/fatal-shutdown.test.mjs bin/hcc.mjs scripts/regression.mjs
|
|
92
|
+
git commit -m "fix: terminate cleanly after fatal runtime errors"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Task 2: Publish the breaking 1.0.0 contract in metadata and docs
|
|
96
|
+
|
|
97
|
+
**Files:**
|
|
98
|
+
- Modify: `package.json:3,24-32`
|
|
99
|
+
- Modify: `package-lock.json`
|
|
100
|
+
- Modify: `CHANGELOG.md`
|
|
101
|
+
- Modify: `README.md`, `README.zh-CN.md`
|
|
102
|
+
- Modify: `docs/README.md`, `docs/README.zh-CN.md`
|
|
103
|
+
- Modify: `docs/commands.md`, `docs/commands.zh-CN.md`
|
|
104
|
+
- Modify: `docs/guide.md`, `docs/guide.zh-CN.md`
|
|
105
|
+
- Modify: `docs/defect-review-2026-07.md`
|
|
106
|
+
- Modify: `lib/ui/help.mjs`
|
|
107
|
+
|
|
108
|
+
- [ ] **Step 1: Add failing release-document assertions**
|
|
109
|
+
|
|
110
|
+
Extend `syntaxAndHelp` and release checks to require all of these concepts in
|
|
111
|
+
`README.md`, `README.zh-CN.md`, `docs/commands.md`,
|
|
112
|
+
`docs/commands.zh-CN.md`, `docs/guide.md`, `docs/guide.zh-CN.md`, the changelog,
|
|
113
|
+
and CLI help:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
1.0.0
|
|
117
|
+
schema v7 / no downgrade
|
|
118
|
+
pre-migration backup
|
|
119
|
+
Runtime API v2
|
|
120
|
+
provider peer IDs changed
|
|
121
|
+
gc --history
|
|
122
|
+
process evidence / unknown-only grace
|
|
123
|
+
--tls and --trust-proxy
|
|
124
|
+
plaintext LAN accepted risk
|
|
125
|
+
arbitrary authenticated project root accepted risk
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Assert the defect report uses the actual current branch dynamically or avoids naming a branch, and that fixed/partial/deferred counts add up to the listed findings.
|
|
129
|
+
|
|
130
|
+
- [ ] **Step 2: Run to verify RED**
|
|
131
|
+
|
|
132
|
+
Run: `npm run test:regression`.
|
|
133
|
+
|
|
134
|
+
Run: `npm run release:check`.
|
|
135
|
+
|
|
136
|
+
Expected: FAIL on version and missing release/help documentation.
|
|
137
|
+
|
|
138
|
+
- [ ] **Step 3: Update release metadata and documentation**
|
|
139
|
+
|
|
140
|
+
Set `package.json` version to `1.0.0`, then run:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm install --package-lock-only --ignore-scripts --no-audit --no-fund
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Add one coherent 1.0.0 changelog section with Breaking, Security, Reliability, GC, and Upgrade headings. Document exact backup naming and recovery inspection, no downgrade, old/new peer ID examples, API v2 negotiation, safe GC flags, supported transport modes, and the two accepted risks.
|
|
147
|
+
|
|
148
|
+
Update help text without claiming plaintext confidentiality or project isolation. Correct defect statuses from current tests and code evidence.
|
|
149
|
+
|
|
150
|
+
- [ ] **Step 4: Verify GREEN**
|
|
151
|
+
|
|
152
|
+
Run: `npm run test:regression`.
|
|
153
|
+
|
|
154
|
+
Run: `npm run release:check`.
|
|
155
|
+
|
|
156
|
+
Run: `node ./bin/hcc.mjs --help`.
|
|
157
|
+
|
|
158
|
+
Expected: all release assertions PASS and version/help output is 1.0.0.
|
|
159
|
+
|
|
160
|
+
- [ ] **Step 5: Commit**
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git add package.json package-lock.json CHANGELOG.md README.md README.zh-CN.md docs/README.md docs/README.zh-CN.md docs/commands.md docs/commands.zh-CN.md docs/guide.md docs/guide.zh-CN.md docs/defect-review-2026-07.md lib/ui/help.mjs scripts/regression.mjs
|
|
164
|
+
git commit -m "docs: publish hello-cc 1.0.0 contract"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Task 3: Make packaging complete and TTL regression deterministic
|
|
168
|
+
|
|
169
|
+
**Files:**
|
|
170
|
+
- Track/modify: `.dockerignore`
|
|
171
|
+
- Track/modify: `Dockerfile`
|
|
172
|
+
- Track: `lib/shared/clock-grace.mjs`
|
|
173
|
+
- Track: `lib/web/tls.mjs`
|
|
174
|
+
- Modify: `scripts/regression.mjs:164-185,2730-2820,7072-7115`
|
|
175
|
+
- Modify: `package.json:10-22`
|
|
176
|
+
|
|
177
|
+
- [ ] **Step 1: Add failing package-completeness and TTL tests**
|
|
178
|
+
|
|
179
|
+
Add a package audit that runs `npm pack --dry-run --json`, parses the file list, resolves every relative import reachable from `bin/hcc.mjs`, and asserts every production dependency is tracked and included in the package.
|
|
180
|
+
|
|
181
|
+
Change hook renewal event payload assertions to require a `renewed_at` timestamp. Seed a fixture where parent `before` is one second greater than child `renewed_at` and assert expiration equals `renewed_at + renewalSec`, not a cross-process before/after window.
|
|
182
|
+
|
|
183
|
+
- [ ] **Step 2: Run to verify RED**
|
|
184
|
+
|
|
185
|
+
Run: `npm run test:regression`.
|
|
186
|
+
|
|
187
|
+
Expected: FAIL because renewal events lack `renewed_at` and current package audit sees untracked production modules.
|
|
188
|
+
|
|
189
|
+
- [ ] **Step 3: Record operation time and complete packaging**
|
|
190
|
+
|
|
191
|
+
Include `renewed_at: hookNow` in `lock.renewed_by_hook` payload and assert exact TTL from that value. Keep created time and `ttl_sec` invariants.
|
|
192
|
+
|
|
193
|
+
Track all imported production modules. Ensure package `files` includes every `lib/` module. Change Docker dependency install to:
|
|
194
|
+
|
|
195
|
+
```dockerfile
|
|
196
|
+
RUN npm ci --no-audit --no-fund
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
At test startup print `node --version` and `tmux -V`. Keep `.git`, runtime state, tokens, DBs, WAL files, logs, and local buffers out of the build context; do not exclude production source.
|
|
200
|
+
|
|
201
|
+
- [ ] **Step 4: Verify GREEN and repeatability**
|
|
202
|
+
|
|
203
|
+
Run: `npm run test:regression` twice locally.
|
|
204
|
+
|
|
205
|
+
Run: `npm pack --dry-run --json` and inspect the parsed audit result.
|
|
206
|
+
|
|
207
|
+
Build and run the container twice at this task boundary. Both runs must reach `FULL_REGRESSION_OK`.
|
|
208
|
+
|
|
209
|
+
- [ ] **Step 5: Commit**
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git add .dockerignore Dockerfile lib/shared/clock-grace.mjs lib/web/tls.mjs scripts/regression.mjs package.json
|
|
213
|
+
git commit -m "test: make v1 package and regression deterministic"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Task 4: Execute the final ordered verification gates
|
|
217
|
+
|
|
218
|
+
**Files:**
|
|
219
|
+
- Review: every path in `git diff --name-only origin/master`
|
|
220
|
+
- Modify only when a failed gate identifies a defect, using a new RED-GREEN cycle.
|
|
221
|
+
|
|
222
|
+
- [ ] **Step 1: Audit final scope and production imports**
|
|
223
|
+
|
|
224
|
+
Run:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
git status --short
|
|
228
|
+
git diff --check origin/master
|
|
229
|
+
git diff --stat origin/master
|
|
230
|
+
git diff --name-status origin/master
|
|
231
|
+
npm pack --dry-run --json
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Expected: no untracked production dependency; no whitespace errors; package audit lists every reachable module.
|
|
235
|
+
|
|
236
|
+
- [ ] **Step 2: Run syntax, unit, and focused security checks**
|
|
237
|
+
|
|
238
|
+
Run:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
node --check bin/hcc.mjs
|
|
242
|
+
node --check scripts/regression.mjs
|
|
243
|
+
npm run test:unit
|
|
244
|
+
npm run smoke
|
|
245
|
+
npm run release:check
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Expected: exit 0 for every command with zero test failures.
|
|
249
|
+
|
|
250
|
+
- [ ] **Step 3: Re-run original vulnerable paths and bypass controls**
|
|
251
|
+
|
|
252
|
+
Run focused selectors or fixtures for negative GC, live-buffer manual GC, CLI-only clock gap, concurrent registry writers, PID reuse, API v1/missing version, cross-session tokens, symlink escape, secret log scanning, and fatal shutdown.
|
|
253
|
+
|
|
254
|
+
Expected: original unsafe behavior no longer reproduces; legitimate live/dead/admin/arbitrary-root/plaintext-LAN controls remain intact.
|
|
255
|
+
|
|
256
|
+
- [ ] **Step 4: Run complete local regression**
|
|
257
|
+
|
|
258
|
+
Run: `npm test`.
|
|
259
|
+
|
|
260
|
+
Expected: unit tests PASS and regression ends with `FULL_REGRESSION_OK`.
|
|
261
|
+
|
|
262
|
+
- [ ] **Step 5: Build the exact current Node 24/Linux image**
|
|
263
|
+
|
|
264
|
+
Run:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
docker build --no-cache -t hello-cc-v1-verify:node24 .
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Expected: successful build using `npm ci`, with all current source copied.
|
|
271
|
+
|
|
272
|
+
- [ ] **Step 6: Run three consecutive container regressions**
|
|
273
|
+
|
|
274
|
+
Run `docker run --rm hello-cc-v1-verify:node24` three separate times. Do not combine them in a shell loop; preserve each output independently.
|
|
275
|
+
|
|
276
|
+
Expected for each run: `FULL_REGRESSION_OK`, no TTL clock-window failure, no leaked tmux session, exit 0.
|
|
277
|
+
|
|
278
|
+
- [ ] **Step 7: Review the complete delta and requirements**
|
|
279
|
+
|
|
280
|
+
Read all four design specs and map every acceptance criterion to a passing command or test. Review `git diff origin/master` for alternate unsafe branches, duplicated policy logic, accidental user-change loss, secrets, stale documentation, and unrelated churn.
|
|
281
|
+
|
|
282
|
+
- [ ] **Step 8: Commit only evidence-required final corrections**
|
|
283
|
+
|
|
284
|
+
If every gate passed without a correction, do not create an empty commit. If a gate required a fix, use a focused commit containing its failing test and minimal production correction.
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Web and Runtime API v2 Hardening Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Ship an explicit Runtime API v2 with bounded browser sessions, session-scoped terminal input, CSP, secret redaction, and symlink-safe project databases.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Central helpers own API negotiation, redaction, token comparison, and canonical path validation. The HTTP and WebSocket entry points enforce these helpers before routing while preserving the accepted plaintext-LAN and arbitrary-root behaviors.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js 24 HTTP/HTTPS, `ws`, browser WebSocket/fetch, `node:test`, real filesystem integration tests.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Map
|
|
14
|
+
|
|
15
|
+
- Create `lib/web/api-version.mjs` and `test/web-api-version.test.mjs`.
|
|
16
|
+
- Create `lib/shared/redact.mjs` and `test/redact.test.mjs`.
|
|
17
|
+
- Create `lib/runtime/project-path.mjs` and `test/project-path.test.mjs`.
|
|
18
|
+
- Modify `lib/web/http.mjs`, `lib/web/runtime.mjs`, `lib/runtime/client.mjs`.
|
|
19
|
+
- Modify `bin/hcc.mjs` HTTP/WS routing, cookie lifecycle, logging, and TLS use.
|
|
20
|
+
- Modify `lib/web/ui-template.mjs` for API headers, WS version query, and CSP nonce.
|
|
21
|
+
- Modify `lib/web/tls.mjs` only where focused tests show a contract gap.
|
|
22
|
+
- Modify `scripts/regression.mjs` for real HTTP/HTTPS/WS tests.
|
|
23
|
+
|
|
24
|
+
### Task 1: Add Runtime API v2 negotiation
|
|
25
|
+
|
|
26
|
+
**Files:**
|
|
27
|
+
- Create: `lib/web/api-version.mjs`
|
|
28
|
+
- Create: `test/web-api-version.test.mjs`
|
|
29
|
+
- Modify: `lib/runtime/client.mjs:7-35`
|
|
30
|
+
- Modify: `lib/web/runtime.mjs:11-185`
|
|
31
|
+
- Modify: `bin/hcc.mjs:4675-5190`
|
|
32
|
+
- Modify: `lib/web/ui-template.mjs:1-2100`
|
|
33
|
+
|
|
34
|
+
- [ ] **Step 1: Write failing negotiation tests**
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import test from 'node:test';
|
|
38
|
+
import assert from 'node:assert/strict';
|
|
39
|
+
import { API_VERSION, readHttpApiVersion, readWebSocketApiVersion } from '../lib/web/api-version.mjs';
|
|
40
|
+
|
|
41
|
+
test('reads API v2 from the HTTP header', () => {
|
|
42
|
+
assert.equal(API_VERSION, 2);
|
|
43
|
+
assert.deepEqual(readHttpApiVersion({ headers: { 'x-hcc-api-version': '2' } }), { ok: true, version: 2 });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('rejects missing and old protected API versions', () => {
|
|
47
|
+
assert.deepEqual(readHttpApiVersion({ headers: {} }), { ok: false, version: null });
|
|
48
|
+
assert.deepEqual(readHttpApiVersion({ headers: { 'x-hcc-api-version': '1' } }), { ok: false, version: 1 });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('reads browser WebSocket version from query', () => {
|
|
52
|
+
assert.deepEqual(readWebSocketApiVersion(new URL('http://localhost/ws?api_version=2')), { ok: true, version: 2 });
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- [ ] **Step 2: Run to verify RED**
|
|
57
|
+
|
|
58
|
+
Run: `node --test test/web-api-version.test.mjs`.
|
|
59
|
+
|
|
60
|
+
Expected: missing-module failure.
|
|
61
|
+
|
|
62
|
+
- [ ] **Step 3: Implement and enforce v2**
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
export const API_VERSION = 2;
|
|
66
|
+
|
|
67
|
+
function result(value) {
|
|
68
|
+
const version = Number.parseInt(String(value ?? ''), 10);
|
|
69
|
+
return { ok: version === API_VERSION, version: Number.isFinite(version) ? version : null };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function readHttpApiVersion(req) {
|
|
73
|
+
return result(req?.headers?.['x-hcc-api-version']);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function readWebSocketApiVersion(url) {
|
|
77
|
+
return result(url?.searchParams?.get('api_version'));
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Before every protected `/api/*` route, return status 426 and:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{"ok":false,"error":{"code":"API_VERSION_UNSUPPORTED","message":"Runtime API version 2 is required","supported_version":2}}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Expose `api_version: 2` from runtime metadata. Add `X-HCC-API-Version: 2` in `runtimeRequest` and every browser fetch wrapper. Add `api_version=2` to terminal WebSocket URLs and reject old/missing WS versions before authentication/session lookup.
|
|
88
|
+
|
|
89
|
+
- [ ] **Step 4: Verify GREEN and real compatibility rejection**
|
|
90
|
+
|
|
91
|
+
Run unit tests. Add regression requests with missing/1/2 headers plus WS query 1/2. Public `/`, `/login`, and static assets must remain exempt.
|
|
92
|
+
|
|
93
|
+
Run: `npm run test:regression`.
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 5: Commit**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git add lib/web/api-version.mjs test/web-api-version.test.mjs lib/runtime/client.mjs lib/web/runtime.mjs lib/web/ui-template.mjs bin/hcc.mjs scripts/regression.mjs
|
|
99
|
+
git commit -m "feat: require Runtime API v2"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Task 2: Enforce administrator and session-action token semantics
|
|
103
|
+
|
|
104
|
+
**Files:**
|
|
105
|
+
- Modify: `lib/web/http.mjs:62-122`
|
|
106
|
+
- Modify: `bin/hcc.mjs:2382-2465,2475-2580,4675-4955,5100-5185`
|
|
107
|
+
- Modify: `lib/web/ui-template.mjs:1570-1760,2050-2110`
|
|
108
|
+
- Modify: `scripts/regression.mjs:1193-1355,1974-2115,2866-3300`
|
|
109
|
+
|
|
110
|
+
- [ ] **Step 1: Add failing privilege-boundary tests**
|
|
111
|
+
|
|
112
|
+
Through real HTTP and WebSocket connections assert:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
admin bearer -> create/stop/inject allowed
|
|
116
|
+
admin cookie + same Origin -> create/stop/inject allowed
|
|
117
|
+
admin cookie + foreign Origin -> mutation denied
|
|
118
|
+
session action token absent from GET /api/sessions
|
|
119
|
+
wrong/missing session action token -> WS input denied
|
|
120
|
+
correct token for another session -> WS input denied
|
|
121
|
+
logout/expiry/eviction -> existing cookie WS closed and future input denied
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Also assert tokenless non-loopback startup fails while tokenless loopback still works.
|
|
125
|
+
Because constant-time behavior is not safely distinguishable with a timing test,
|
|
126
|
+
add a static security assertion that extracts `resolveWebActionSession` from
|
|
127
|
+
`bin/hcc.mjs`, requires `tokenMatches(provided, expected)`, and rejects direct
|
|
128
|
+
`provided !== expected` comparison. This is the repeatable validation artifact
|
|
129
|
+
for the timing boundary; the integration cases remain the behavior proof.
|
|
130
|
+
|
|
131
|
+
- [ ] **Step 2: Run to verify RED**
|
|
132
|
+
|
|
133
|
+
Run: `npm run test:regression`.
|
|
134
|
+
|
|
135
|
+
Expected: FAIL on the static constant-time assertion while the current code uses
|
|
136
|
+
direct inequality. Any additional integration failure is fixed through the same
|
|
137
|
+
boundary, but is not required to establish RED.
|
|
138
|
+
|
|
139
|
+
- [ ] **Step 3: Apply one shared token comparator and explicit roles**
|
|
140
|
+
|
|
141
|
+
Use `tokenMatches(provided, expected)` for administrator and session action tokens. Keep `webAuthMode` results `token` and `cookie`, document both as administrator roles, and keep same-origin enforcement on every cookie mutation. Track each cookie-authenticated WS in its server session record and validate the record before inbound and outbound frames.
|
|
142
|
+
|
|
143
|
+
Do not put action tokens in serialized session lists. Deliver them only in the corresponding terminal snapshot frame. Preserve CLI injection through administrator bearer authentication without requiring the session token.
|
|
144
|
+
|
|
145
|
+
- [ ] **Step 4: Verify GREEN and alternate bypasses**
|
|
146
|
+
|
|
147
|
+
Run the regression suite, then repeat token tests with same-length wrong tokens, URL-encoded peer IDs, revoked cookies, and a token from a sibling project session.
|
|
148
|
+
|
|
149
|
+
Expected: legitimate admin and matching-session behavior remains; all bypasses fail.
|
|
150
|
+
|
|
151
|
+
- [ ] **Step 5: Commit**
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
git add lib/web/http.mjs bin/hcc.mjs lib/web/ui-template.mjs scripts/regression.mjs
|
|
155
|
+
git commit -m "fix: enforce Web administrator and session tokens"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Task 3: Add structured secret redaction and CSP
|
|
159
|
+
|
|
160
|
+
**Files:**
|
|
161
|
+
- Create: `lib/shared/redact.mjs`
|
|
162
|
+
- Create: `test/redact.test.mjs`
|
|
163
|
+
- Modify: `lib/web/http.mjs:33-59`
|
|
164
|
+
- Modify: `lib/web/ui-template.mjs:1-2150`
|
|
165
|
+
- Modify: `bin/hcc.mjs:2180-2250,2368-2470,5290-5300`
|
|
166
|
+
- Modify: `scripts/regression.mjs:1841-2115,3894-4300`
|
|
167
|
+
|
|
168
|
+
- [ ] **Step 1: Write failing redaction tests**
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import test from 'node:test';
|
|
172
|
+
import assert from 'node:assert/strict';
|
|
173
|
+
import { redactSecrets } from '../lib/shared/redact.mjs';
|
|
174
|
+
|
|
175
|
+
test('redacts CLI, URL, headers, cookies, and structured tokens', () => {
|
|
176
|
+
const secret = 's3cr3t-value';
|
|
177
|
+
const value = {
|
|
178
|
+
command: `hcc web --token ${secret}`,
|
|
179
|
+
url: `http://host/?token=${secret}&root=/tmp/x`,
|
|
180
|
+
headers: { authorization: `Bearer ${secret}`, cookie: `hcc_sid=${secret}` },
|
|
181
|
+
action_token: secret
|
|
182
|
+
};
|
|
183
|
+
const redacted = JSON.stringify(redactSecrets(value));
|
|
184
|
+
assert.equal(redacted.includes(secret), false);
|
|
185
|
+
assert.equal(redacted.includes('[REDACTED]'), true);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Add an HTML test that extracts the CSP nonce, verifies the script tag uses it, and asserts a second un-nonced inline script would violate the generated `script-src` policy.
|
|
190
|
+
|
|
191
|
+
- [ ] **Step 2: Run to verify RED**
|
|
192
|
+
|
|
193
|
+
Run: `node --test test/redact.test.mjs`.
|
|
194
|
+
|
|
195
|
+
Expected: missing-module failure.
|
|
196
|
+
|
|
197
|
+
- [ ] **Step 3: Implement redaction and nonce responses**
|
|
198
|
+
|
|
199
|
+
`redactSecrets(value)` recursively clones arrays/objects, replaces secret-key values, redacts bearer/cookie fragments, replaces `--token` arguments, and deletes URL `token` parameter values. It must handle cycles with a `WeakSet` and replace them with `[Circular]`.
|
|
200
|
+
|
|
201
|
+
Pass all startup/error logging through this helper before stringification. Never log raw request headers or URLs.
|
|
202
|
+
|
|
203
|
+
Generate a random nonce per HTML response and call `webIndexHtml({ nonce })` or `webLoginPage({ nonce })`. Add the nonce to every inline script. Emit CSP:
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
default-src 'self'; script-src 'self' 'nonce-<value>'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ws: wss:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
- [ ] **Step 4: Verify GREEN and leak controls**
|
|
210
|
+
|
|
211
|
+
Run unit tests. Start Web with unique test secrets, exercise login/WS/errors, stop it, and scan all generated logs/runtime output. Assert no supplied secret appears. Verify browser HTML contains a matching nonce and no un-nonced script.
|
|
212
|
+
|
|
213
|
+
Run: `npm run test:regression`.
|
|
214
|
+
|
|
215
|
+
- [ ] **Step 5: Commit**
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
git add lib/shared/redact.mjs test/redact.test.mjs lib/web/http.mjs lib/web/ui-template.mjs bin/hcc.mjs scripts/regression.mjs
|
|
219
|
+
git commit -m "fix: redact Web secrets and enforce CSP"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Task 4: Canonicalize arbitrary project roots without symlink escape
|
|
223
|
+
|
|
224
|
+
**Files:**
|
|
225
|
+
- Create: `lib/runtime/project-path.mjs`
|
|
226
|
+
- Create: `test/project-path.test.mjs`
|
|
227
|
+
- Modify: `bin/hcc.mjs:2479-2525`
|
|
228
|
+
- Modify: `scripts/regression.mjs:2866-3300,6300-6700`
|
|
229
|
+
|
|
230
|
+
- [ ] **Step 1: Write failing real-filesystem tests**
|
|
231
|
+
|
|
232
|
+
Create temporary roots for: normal project, missing `.hello-cc`, `.hello-cc` symlink to an outside directory, nested DB symlink escape, `/proc` pseudo-file target on Linux, and a normal custom DB below `.hello-cc`.
|
|
233
|
+
|
|
234
|
+
Use the intended API:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
const safe = resolveProjectDatabase({ root, db: path.join(root, '.hello-cc', 'mesh.db'), createStateDir: true });
|
|
238
|
+
assert.equal(safe.db.startsWith(safe.stateDir + path.sep), true);
|
|
239
|
+
assert.throws(() => resolveProjectDatabase({ root: escapedRoot, db: escapedDb, createStateDir: true }), /PROJECT_PATH_FORBIDDEN/);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
- [ ] **Step 2: Run to verify RED**
|
|
243
|
+
|
|
244
|
+
Run: `node --test test/project-path.test.mjs`.
|
|
245
|
+
|
|
246
|
+
Expected: missing-module failure.
|
|
247
|
+
|
|
248
|
+
- [ ] **Step 3: Implement canonical containment**
|
|
249
|
+
|
|
250
|
+
Resolve an existing root with `realpathSync`. Create `.hello-cc` with mode `0700` only when requested and absent. Reject it when `lstat` reports a symlink or when its real path is outside root. Resolve the DB parent, reject symlink components escaping the state directory, and require an existing DB target to be a regular file.
|
|
251
|
+
|
|
252
|
+
Replace `assertDbUnderRoot`/lexical-only checks with `resolveProjectDatabase`. Continue allowing every existing canonical root after administrator authentication.
|
|
253
|
+
|
|
254
|
+
- [ ] **Step 4: Verify GREEN and HTTP boundary**
|
|
255
|
+
|
|
256
|
+
Run unit tests, then issue real authenticated Web requests for normal arbitrary roots and each escape fixture. Require normal requests to work and every escape to return `PROJECT_PATH_FORBIDDEN` without creating/opening the outside DB.
|
|
257
|
+
|
|
258
|
+
Run: `npm run test:regression`.
|
|
259
|
+
|
|
260
|
+
- [ ] **Step 5: Commit**
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
git add lib/runtime/project-path.mjs test/project-path.test.mjs bin/hcc.mjs scripts/regression.mjs
|
|
264
|
+
git commit -m "fix: prevent project database symlink escape"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Task 5: Finish TLS and trusted-proxy mode coverage
|
|
268
|
+
|
|
269
|
+
**Files:**
|
|
270
|
+
- Track/modify: `lib/web/tls.mjs`
|
|
271
|
+
- Modify: `lib/web/runtime.mjs:80-190`
|
|
272
|
+
- Modify: `lib/runtime/state.mjs:91-123`
|
|
273
|
+
- Modify: `lib/web/http.mjs:76-112`
|
|
274
|
+
- Modify: `scripts/regression.mjs:6440-6600`
|
|
275
|
+
|
|
276
|
+
- [ ] **Step 1: Add failing mode-matrix tests**
|
|
277
|
+
|
|
278
|
+
Cover direct self-signed TLS, expired certificate rejection, SAN mismatch regeneration, key mismatch regeneration, loopback trusted proxy, forwarded headers from non-loopback, and accepted plaintext LAN. Assert `Secure` cookie only for direct TLS or trusted loopback proxy reporting HTTPS.
|
|
279
|
+
|
|
280
|
+
- [ ] **Step 2: Run to verify RED**
|
|
281
|
+
|
|
282
|
+
Run: `npm run test:regression`.
|
|
283
|
+
|
|
284
|
+
Expected: any uncovered certificate generation race or proxy-origin discrepancy fails with the specific mode fixture.
|
|
285
|
+
|
|
286
|
+
- [ ] **Step 3: Make only evidence-required production changes**
|
|
287
|
+
|
|
288
|
+
Keep generation directories, `.creating`/`.published`, atomic `current.json`, SAN/date/key validation, and CA-scoped runtime requests. Ensure proxy host/proto is consumed only when `--trust-proxy` is enabled and socket remote address is loopback. Do not make TLS mandatory or change the accepted default listener.
|
|
289
|
+
|
|
290
|
+
- [ ] **Step 4: Verify GREEN**
|
|
291
|
+
|
|
292
|
+
Run: `npm run test:unit`.
|
|
293
|
+
|
|
294
|
+
Run: `npm run test:regression`.
|
|
295
|
+
|
|
296
|
+
Expected: all three supported modes pass and every spoofed forwarded-header control fails closed.
|
|
297
|
+
|
|
298
|
+
- [ ] **Step 5: Commit**
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
git add lib/web/tls.mjs lib/web/runtime.mjs lib/runtime/state.mjs lib/web/http.mjs scripts/regression.mjs
|
|
302
|
+
git commit -m "test: close Web transport mode coverage"
|
|
303
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Runtime Buffer GC Two-Phase Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Make manual GC use one server-derived timing snapshot, one unified clock decision, a one-shot runtime file apply, and only then an exact database apply.
|
|
6
|
+
|
|
7
|
+
**Architecture:** The runtime prepares an immutable canonical file plan from a canonical retention period and stores it behind a short-lived root/DB-bound token. The CLI uses the runtime-derived cutoff to capture an exact database plan and merges both plans' would-delete boundaries into one clock observation. Runtime file batches each recheck clock grace under `BEGIN IMMEDIATE`; database rows are finalized only after the runtime reports that every prepared file candidate was processed.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js ESM, `node:sqlite`, synchronous filesystem APIs, built-in test runner, HTTP runtime API.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Read-only clock prediction
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `lib/core/coordination/clock-safety.mjs`
|
|
17
|
+
- Test: `test/clock-safety.test.mjs`
|
|
18
|
+
|
|
19
|
+
- [ ] Write a failing test that seeds a watermark, pending gap, and grace row, calls `previewClockSafety`, verifies the same grace decision expected from apply, and compares the complete `meta` table before/after.
|
|
20
|
+
- [ ] Run `node --test test/clock-safety.test.mjs` and verify the missing export fails.
|
|
21
|
+
- [ ] Extract the shared pending-gap decision calculation and implement `previewClockSafety(db, options)` without a transaction or write.
|
|
22
|
+
- [ ] Re-run the focused test and verify preview and observer decisions match while preview leaves `meta` byte-for-byte unchanged.
|
|
23
|
+
|
|
24
|
+
### Task 2: One-shot runtime plan tokens and bounded apply barriers
|
|
25
|
+
|
|
26
|
+
**Files:**
|
|
27
|
+
- Create: `lib/runtime/buffer-gc-protocol.mjs`
|
|
28
|
+
- Modify: `lib/runtime/buffer-gc.mjs`
|
|
29
|
+
- Create: `test/buffer-gc-protocol.test.mjs`
|
|
30
|
+
|
|
31
|
+
- [ ] Write failing tests for token entropy shape, root/DB binding, replay, expiry, mismatch cleanup, and dry-run plans leaving no token.
|
|
32
|
+
- [ ] Write a failing real SQLite/filesystem test with more than 64 frozen candidates; extend grace from a second connection after the first committed batch and assert exactly the first batch is deleted and the remainder deferred.
|
|
33
|
+
- [ ] Implement `createBufferGcPlanStore` with a 15-second TTL and destructive take semantics.
|
|
34
|
+
- [ ] Implement `applyClockSafeBufferPlan` using batches of 64; each batch opens `BEGIN IMMEDIATE`, runs `observeClockSafetyInTransaction`, checks current grace, and performs only that batch's bounded identity rechecks/unlinks before commit.
|
|
35
|
+
- [ ] Re-run the focused protocol tests.
|
|
36
|
+
|
|
37
|
+
### Task 3: Authenticated prepare/apply API
|
|
38
|
+
|
|
39
|
+
**Files:**
|
|
40
|
+
- Modify: `bin/hcc.mjs`
|
|
41
|
+
- Modify: `scripts/regression.mjs`
|
|
42
|
+
|
|
43
|
+
- [ ] Add failing authenticated endpoint regressions: legacy or forged `cutoffMs`/`observedAt` bodies return `BAD_REQUEST`; prepare accepts only canonical safe `retentionSec`; apply rejects replay, wrong root/DB, and expired tokens.
|
|
44
|
+
- [ ] Replace the single-call endpoint with `phase: prepare|apply`. Prepare derives `observedAt` and `cutoffMs` from server time, freezes the full actual-directory plan, and returns counts/cutoffs plus a token only for apply. Apply destructively takes the token and runs the bounded barrier.
|
|
45
|
+
- [ ] Add periodic opportunistic expiry cleanup and verify dry-run preparation stores no token.
|
|
46
|
+
- [ ] Run the endpoint regression through the multi-project runtime workflow.
|
|
47
|
+
|
|
48
|
+
### Task 4: Manual CLI unified decision and apply ordering
|
|
49
|
+
|
|
50
|
+
**Files:**
|
|
51
|
+
- Modify: `bin/hcc.mjs`
|
|
52
|
+
- Modify: `lib/core/coordination/gc-plan.mjs`
|
|
53
|
+
- Modify: `scripts/regression.mjs`
|
|
54
|
+
- Test: `test/gc-plan.test.mjs`
|
|
55
|
+
|
|
56
|
+
- [ ] Add a failing regression with an exact DB history/lock/peer plan plus an actual-directory buffer candidate crossing one pending gap; assert dry-run predicts all would-delete categories deferred without changing `meta`, and apply deletes neither resource.
|
|
57
|
+
- [ ] Add a failing regression where grace is extended after prepare and before apply; assert runtime deletes zero and database finalization never starts.
|
|
58
|
+
- [ ] Add async snapshot cleanup support so the history temp table is always dropped across runtime awaits while preserving primary/cleanup errors.
|
|
59
|
+
- [ ] Split manual GC into prepare, unified preview/observe, runtime/local file apply, and exact DB finalization. Treat runtime unavailability, token failure, incomplete batched apply, subject drift, or clock failure as a barrier that prevents all database deletion.
|
|
60
|
+
- [ ] Ensure result counters report only committed deletes, retain verified-live buffer counts as protected, and report all unprocessed would-delete rows/files as deferred.
|
|
61
|
+
- [ ] Run `node --test test/gc-plan.test.mjs` and the focused manual/runtime regression.
|
|
62
|
+
|
|
63
|
+
### Task 5: Full verification and amended commit
|
|
64
|
+
|
|
65
|
+
**Files:**
|
|
66
|
+
- Verify all Task 4 files only.
|
|
67
|
+
|
|
68
|
+
- [ ] Run `npm run test:unit` and require zero failures.
|
|
69
|
+
- [ ] Run `npm run test:regression` and require `FULL_REGRESSION_OK`.
|
|
70
|
+
- [ ] Rebuild the Node 24 image and run its full regression.
|
|
71
|
+
- [ ] Run `git diff --cached --check`, selectively stage Task 4 files, and amend `f075976` without staging unrelated worktree changes.
|