@tekyzinc/gsd-t 3.29.10 → 4.0.12
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 +176 -0
- package/bin/gsd-t-parallel.cjs +64 -13
- package/bin/gsd-t-test-data-adapters/file-json-array.cjs +27 -3
- package/bin/gsd-t-test-data-adapters/localstorage-key-prefix.cjs +6 -1
- package/bin/gsd-t-test-data-adapters/sqlite-table-where.cjs +16 -1
- package/bin/gsd-t-test-data-ledger.cjs +1 -0
- package/bin/gsd-t.js +48 -376
- package/bin/journey-coverage.cjs +6 -3
- package/bin/parallel-cli.cjs +13 -1
- package/commands/gsd-t-debug.md +47 -533
- package/commands/gsd-t-design-decompose.md +24 -497
- package/commands/gsd-t-doc-ripple.md +23 -139
- package/commands/gsd-t-execute.md +41 -958
- package/commands/gsd-t-help.md +0 -2
- package/commands/gsd-t-impact.md +26 -295
- package/commands/gsd-t-integrate.md +44 -369
- package/commands/gsd-t-milestone.md +25 -124
- package/commands/gsd-t-partition.md +28 -539
- package/commands/gsd-t-plan.md +26 -472
- package/commands/gsd-t-prd.md +25 -311
- package/commands/gsd-t-quick.md +1 -1
- package/commands/gsd-t-resume.md +0 -33
- package/commands/gsd-t-verify.md +52 -569
- package/commands/gsd-t-wave.md +41 -430
- package/package.json +1 -1
- package/scripts/gsd-t-calibration-hook.js +3 -1
- package/scripts/hooks/pre-commit-journey-coverage +0 -2
- package/scripts/hooks/pre-commit-playwright-gate +0 -2
- package/templates/CLAUDE-global.md +43 -173
- package/templates/CLAUDE-project.md +118 -104
- package/templates/prompts/design-verify-subagent.md +3 -0
- package/templates/prompts/qa-subagent.md +3 -0
- package/templates/prompts/red-team-subagent.md +5 -2
- package/templates/workflows/_lib.js +176 -0
- package/templates/workflows/gsd-t-debug.workflow.js +86 -0
- package/templates/workflows/gsd-t-execute.workflow.js +206 -0
- package/templates/workflows/gsd-t-integrate.workflow.js +71 -0
- package/templates/workflows/gsd-t-phase.workflow.js +94 -0
- package/templates/workflows/gsd-t-quick.workflow.js +72 -0
- package/templates/workflows/gsd-t-verify.workflow.js +348 -0
- package/templates/workflows/gsd-t-wave.workflow.js +43 -0
- package/bin/check-headless-sessions.js +0 -140
- package/bin/context-budget-audit.cjs +0 -447
- package/bin/context-meter-config.cjs +0 -101
- package/bin/context-meter-config.test.cjs +0 -101
- package/bin/event-stream.cjs +0 -205
- package/bin/gsd-t-benchmark-orchestrator.js +0 -437
- package/bin/gsd-t-capture-lint.cjs +0 -440
- package/bin/gsd-t-economics.cjs +0 -315
- package/bin/gsd-t-in-session-usage.cjs +0 -213
- package/bin/gsd-t-orchestrator-config.cjs +0 -161
- package/bin/gsd-t-orchestrator-queue.cjs +0 -180
- package/bin/gsd-t-orchestrator-recover.cjs +0 -231
- package/bin/gsd-t-orchestrator-worker.cjs +0 -219
- package/bin/gsd-t-orchestrator.js +0 -535
- package/bin/gsd-t-parallel-probe.cjs +0 -132
- package/bin/gsd-t-ratelimit-probe-worker.cjs +0 -237
- package/bin/gsd-t-ratelimit-probe.cjs +0 -648
- package/bin/gsd-t-report-tokens.cjs +0 -549
- package/bin/gsd-t-stream-feed-client.cjs +0 -151
- package/bin/gsd-t-token-backfill.cjs +0 -366
- package/bin/gsd-t-token-capture.cjs +0 -321
- package/bin/gsd-t-token-dashboard.cjs +0 -353
- package/bin/gsd-t-token-regenerate-log.cjs +0 -129
- package/bin/gsd-t-tool-attribution.cjs +0 -377
- package/bin/gsd-t-tool-cost.cjs +0 -195
- package/bin/gsd-t-transcript-tee.cjs +0 -246
- package/bin/gsd-t-unattended-heartbeat.cjs +0 -188
- package/bin/gsd-t-unattended-platform.cjs +0 -551
- package/bin/gsd-t-unattended-platform.js +0 -381
- package/bin/gsd-t-unattended-safety.cjs +0 -773
- package/bin/gsd-t-unattended-safety.js +0 -788
- package/bin/gsd-t-unattended.cjs +0 -2109
- package/bin/gsd-t-unattended.js +0 -1367
- package/bin/gsd-t-worker-dispatch.cjs +0 -211
- package/bin/handoff-lock.cjs +0 -249
- package/bin/handoff-lock.js +0 -249
- package/bin/headless-auto-spawn.cjs +0 -711
- package/bin/headless-auto-spawn.js +0 -373
- package/bin/headless-exit-codes.cjs +0 -67
- package/bin/live-activity-report.cjs +0 -615
- package/bin/log-tail.cjs +0 -81
- package/bin/m44-proof-measure.cjs +0 -285
- package/bin/m46-iter-proof.cjs +0 -149
- package/bin/m46-worker-proof.cjs +0 -201
- package/bin/m55-substrate-proof.cjs +0 -134
- package/bin/metrics-rollup.js +0 -200
- package/bin/model-windows.cjs +0 -99
- package/bin/model-windows.test.cjs +0 -75
- package/bin/parallelism-report.cjs +0 -535
- package/bin/runway-estimator.cjs +0 -242
- package/bin/spawn-plan-derive.cjs +0 -163
- package/bin/spawn-plan-status-updater.cjs +0 -292
- package/bin/spawn-plan-writer.cjs +0 -204
- package/bin/supervisor-pid-fingerprint.cjs +0 -126
- package/bin/token-budget.cjs +0 -265
- package/bin/unattended-watch-format.cjs +0 -178
- package/bin/watch-progress.js +0 -155
- package/commands/gsd-t-unattended-stop.md +0 -85
- package/commands/gsd-t-unattended-watch.md +0 -465
- package/commands/gsd-t-unattended.md +0 -476
- package/commands/gsd-t-visualize.md +0 -116
- package/scripts/gsd-t-context-meter.e2e.test.js +0 -364
- package/scripts/gsd-t-context-meter.js +0 -341
- package/scripts/gsd-t-context-meter.test.js +0 -471
- package/scripts/gsd-t-dashboard-server.js +0 -1203
- package/scripts/gsd-t-post-commit-spawn-plan.sh +0 -86
- package/scripts/gsd-t-transcript.html +0 -1821
- package/scripts/hooks/gsd-t-conversation-capture.js +0 -439
- package/scripts/hooks/gsd-t-in-session-usage-hook.js +0 -84
- package/scripts/spawn-plan-fmt-tokens.cjs +0 -80
- package/templates/hooks/post-commit-spawn-plan.sh +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,182 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.0.12] - 2026-05-29
|
|
6
|
+
|
|
7
|
+
### Removed — M61-carryover test cleanup (green the suite)
|
|
8
|
+
|
|
9
|
+
The SC7 cockpit walkthrough surfaced ~23 unit + ~19 E2E pre-existing failures, all tests asserting conventions or files that M61 intentionally retired (command→Workflow conversion removed the marker/wire-in blocks; D4 retired the viewer/dashboard). Delete-with-subject cleanup — no behavior change.
|
|
10
|
+
|
|
11
|
+
- Removed wire-in/marker tests: `m56-d3-wire-in`, `m56-d4-wire-in`, `m55-d5-wire-in-{execute,verify}`, `m56-d5-stream-json-gap-closures` (the last required D3-deleted `gsd-t-capture-lint.cjs`).
|
|
12
|
+
- Removed 8 viewer/transcript unit tests + 3 journey-coverage staging tests coupled to the deleted viewer HTML.
|
|
13
|
+
- Removed orphaned E2E specs: all 6 `e2e/viewer/*`, 13 viewer-dependent `e2e/journeys/*`, `main-session-stream`, all 4 `e2e/live-journeys/*`. Kept the 3 `verify-gate-blocks-*` journeys (verify-gate is KEEP).
|
|
14
|
+
- Deleted `scripts/gsd-t-transcript.html` (96KB retired D4 viewer); pruned `.gsd-t/journey-manifest.json` 19→3 specs; generalized `bin/journey-coverage.cjs` `VIEWER_FILE_PATTERNS` to `scripts/*.html`; cleaned 2 pre-commit hooks.
|
|
15
|
+
- `test/stack-rules.test.js`: dropped per-command-file Detection-block assertions (kept engine + QA-protocol checks). `test/filesystem.test.js`: command counts 55→51 / 49→45 (D2 retired 4 commands).
|
|
16
|
+
|
|
17
|
+
Suite now 1267 pass / 1 known flake (`verifyPlaywrightHealth` — passes 20/0 isolated; suite-contention subprocess timeout) / 3 sqlite-skip. E2E 3 pass. journey-coverage clean. bin/ unchanged at 20,271 LOC.
|
|
18
|
+
|
|
19
|
+
## [4.0.11] - 2026-05-29 (M65 Orchestration-Shell Retirement — patch)
|
|
20
|
+
|
|
21
|
+
Completes the M61 D6 deferral: deletes the M40/M44 orchestration shell that the native Workflow scripts replaced. Scope was corrected from the raw brief after a live reference scan proved `parallel-cli*` and `gsd-t-parallel.cjs` are KEEP-list substrate (verify-gate Track-2 + the `_lib` file-disjointness prover), not retireable shell — they were retained.
|
|
22
|
+
|
|
23
|
+
### Removed (bin/, −1,780 LOC: 22,051 → 20,271)
|
|
24
|
+
|
|
25
|
+
| File(s) | Native replacement |
|
|
26
|
+
|---------|--------------------|
|
|
27
|
+
| `bin/gsd-t-orchestrator.js` + `-worker.cjs` + `-queue.cjs` + `-config.cjs` | `templates/workflows/gsd-t-execute.workflow.js` |
|
|
28
|
+
| `bin/spawn-plan-{writer,status-updater,derive}.cjs` + `scripts/spawn-plan-fmt-tokens.cjs` | Native `/workflows` progress + Agent View |
|
|
29
|
+
| `bin/headless-exit-codes.cjs` | `mapHeadlessExitCode` inlined into `bin/gsd-t.js` (5-code contract + M45 boundary-anchored regexes verbatim) |
|
|
30
|
+
| `scripts/gsd-t-post-commit-spawn-plan.sh` + `templates/hooks/post-commit-spawn-plan.sh` | — (spawn-plan panel retired with the M61-D4 viewer) |
|
|
31
|
+
|
|
32
|
+
### Removed CLI subcommands
|
|
33
|
+
- `gsd-t orchestrate` — superseded by `gsd-t-execute.workflow.js`.
|
|
34
|
+
|
|
35
|
+
### Removed tests (delete-with-subject)
|
|
36
|
+
- `test/m40-orchestrator-{config,queue,worker}.test.js`, `test/m44-d8-{spawn-plan-writer,spawn-plan-status-updater,post-commit-hook}.test.js` (each tested only a co-deleted module).
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- `bin/gsd-t-parallel.cjs` — inlined `computeInSessionHeadroom`/`computeUnattendedGate`/`DEFAULT_SUMMARY_SIZE_PCT` (wave-join-contract §Mode-Aware Gating Math, constants 85/60/4) off the deleted orchestrator-config; refreshed stale header.
|
|
40
|
+
- `bin/gsd-t.js` — purged 10 stale `PROJECT_BIN_TOOLS` entries pointing at M61-deleted files (would have broken `update-all`).
|
|
41
|
+
- `commands/gsd-t-help.md` — removed spawn-plan/parallelism panel bullets (referenced deleted hook + retired dashboard).
|
|
42
|
+
- `commands/gsd-t-resume.md` — removed dead Step 0.3 "Orchestrator Run Recovery" section.
|
|
43
|
+
|
|
44
|
+
### Deferred (NOT in M65)
|
|
45
|
+
- `bin/orchestrator.js` + `bin/design-orchestrator.js` — the design-build pipeline orchestrator; `gsd-t design-build` is documented but has no live dispatch case (unwired). Wire-back-or-retire is a separate decision.
|
|
46
|
+
|
|
47
|
+
### Verification
|
|
48
|
+
- Red Team (opus): GRUDGING PASS — 0 CRITICAL/HIGH, both inlines byte-faithful (25-case adversarial corpus → 100% parity vs parent sources).
|
|
49
|
+
- QA (sonnet): PASS — zero M65-caused failures; −66 test-count drop fully accounted by the 6 deleted files.
|
|
50
|
+
- `/code-review ultra`: skipped (pure deletion + verbatim inlines, covered by RT+QA).
|
|
51
|
+
- `gsd-t doctor` clean; `gsd-t build-coverage` PASS; `gsd-t parallel --dry-run` exit 0 (KEEP-canary).
|
|
52
|
+
- Verdict **VERIFIED-WITH-WARNINGS**: the verify-gate/ci-parity reds are 100% inherited M61 D1–D8 carryover (23 broken unit tests + ~18 orphaned viewer E2E specs importing the M61-D4-deleted `gsd-t-dashboard-server.js`), proven against parent commit `5a5c6c0^` (1427 pass / 22 fail). M65 introduced zero regressions. Carryover flagged for a dedicated M61-cleanup milestone.
|
|
53
|
+
|
|
54
|
+
## [4.0.10] - 2026-05-29 (M61 Platform Reconciliation — major)
|
|
55
|
+
|
|
56
|
+
### BREAKING CHANGES
|
|
57
|
+
|
|
58
|
+
GSD-T v4.0.10 retires ~18,000 LOC of orchestration infrastructure built across M34–M55 to compensate for limitations Claude Code has since absorbed natively (1M context window, native background Workflows with `parallel()`/`pipeline()`/`budget`/schema-validation, `/usage`, `/context`, `/workflows`, comprehensive hooks, `/code-review ultra`).
|
|
59
|
+
|
|
60
|
+
The v3.x source is preserved on the `v3.x-legacy` git branch as a safety net. `npm install @tekyzinc/gsd-t@3.x` continues to work.
|
|
61
|
+
|
|
62
|
+
#### Removed CLI subcommands
|
|
63
|
+
|
|
64
|
+
| Subcommand | Replacement |
|
|
65
|
+
|------------|-------------|
|
|
66
|
+
| `gsd-t unattended` (+ `-watch`, `-stop`) | Native background Workflows + `/loop` skill |
|
|
67
|
+
| `gsd-t backfill-tokens` | None — historical analysis was never load-bearing |
|
|
68
|
+
| `gsd-t capture-lint` | None — the OBSERVABILITY block convention itself is retired |
|
|
69
|
+
| `gsd-t tool-cost` | OpenTelemetry exports (Enterprise) or `/usage` aggregate |
|
|
70
|
+
| `gsd-t report tokens` | `/usage` |
|
|
71
|
+
| `gsd-t stream-feed` | `/workflows` + Agent View |
|
|
72
|
+
| `gsd-t benchmark-orchestrator` | None — operator-run benchmark driver |
|
|
73
|
+
|
|
74
|
+
#### Removed slash commands
|
|
75
|
+
|
|
76
|
+
| Command | Replacement |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `/gsd-t-unattended`, `/gsd-t-unattended-watch`, `/gsd-t-unattended-stop` | Native background Workflows |
|
|
79
|
+
| `/gsd-t-visualize` | `/workflows` |
|
|
80
|
+
|
|
81
|
+
#### Removed CLAUDE.md rules
|
|
82
|
+
|
|
83
|
+
| Rule (global + project) | Replacement |
|
|
84
|
+
|------------------------|-------------|
|
|
85
|
+
| **Observability Logging (MANDATORY)** | Workflow `budget` global + native `/usage` |
|
|
86
|
+
| **Token Capture Rule (MANDATORY)** | Same |
|
|
87
|
+
| **In-Session Conversation Capture (M45 D2)** | Native session transcript + `/workflows` view |
|
|
88
|
+
| **Always-Headless Spawn (M43 D4) — Channel Separation** | Native Workflows are inherently background |
|
|
89
|
+
| **Context Meter (M34/M38)** | Native `/context` + Workflow `budget.remaining()` |
|
|
90
|
+
| **Mandatory Preflight Before Spawn (M55)** | REWRITTEN as Preflight Gate — invoked inside Workflow scripts via `_lib.runPreflight()` |
|
|
91
|
+
| **Brief-First Worker Rule (M55)** | REWRITTEN — invoked inside Workflow `agent()` calls; per-domain briefs generated inside `parallel()` map |
|
|
92
|
+
| **Two-Track Verify-Gate (M55)** | REWRITTEN as a stage inside `gsd-t-verify.workflow.js`, now followed by M57 CI-parity + M58 test-data purge + orthogonal validation triad |
|
|
93
|
+
|
|
94
|
+
#### Removed contracts
|
|
95
|
+
|
|
96
|
+
`headless-contract.md`, `headless-default-contract.md`, `context-meter-contract.md`, `dashboard-server-contract.md`, `conversation-capture-contract.md`, `live-activity-contract.md`, `parallelism-report-contract.md`, `compaction-events-contract.md`, `event-schema-contract.md`, `economics-estimator-contract.md`, `metrics-schema-contract.md`.
|
|
97
|
+
|
|
98
|
+
#### Added contracts
|
|
99
|
+
|
|
100
|
+
- **`orthogonal-validation-contract.md` v1.0.0 STABLE** — locks `/code-review ultra` + Red Team + QA as orthogonal objective functions. No collapse, no substitution, no transitive trust. `skipUltra=true` is INELIGIBLE for `VERIFIED`.
|
|
101
|
+
|
|
102
|
+
#### Added files
|
|
103
|
+
|
|
104
|
+
- **`templates/workflows/_lib.js`** — shared Workflow helpers (`runPreflight`, `generateBrief`, `proveFileDisjointness`, `runVerifyGate`, `loadProtocol`, `readDomainTasks`, `readScope`). Prefers project-local `bin/<tool>.cjs` with PATH fallback.
|
|
105
|
+
- **`templates/workflows/gsd-t-execute.workflow.js`** — canonical execute phase as native Workflow.
|
|
106
|
+
- **`templates/workflows/gsd-t-verify.workflow.js`** — verify phase with M57 + M58 gates + orthogonal triad.
|
|
107
|
+
- **`templates/workflows/gsd-t-wave.workflow.js`** — composes execute + verify.
|
|
108
|
+
- **`templates/workflows/gsd-t-integrate.workflow.js`**, **`-debug`**, **`-quick`**, **`-phase`** (generic upper-stage runner).
|
|
109
|
+
|
|
110
|
+
#### Reframed validation protocols
|
|
111
|
+
|
|
112
|
+
`templates/prompts/{qa,red-team,design-verify}-subagent.md` methodology bodies unchanged. Only the invocation-context preamble updated: when invoked as a Workflow `agent()` stage, final emission is a StructuredOutput JSON envelope matching the schema; when invoked as a Task subagent (legacy path), final emission is the markdown report. Same methodology; different envelope.
|
|
113
|
+
|
|
114
|
+
Red Team verdict string canonicalized: `GRUDGING PASS` → `GRUDGING-PASS` across schema enum, protocol body, global rule, and 3 command files.
|
|
115
|
+
|
|
116
|
+
#### Command file conversions
|
|
117
|
+
|
|
118
|
+
All 12 GSD-T command files converted from prose-orchestrator scaffolding (~6,000 lines total) to thin Workflow invokers (~700 lines total):
|
|
119
|
+
- `execute` (984 → 67), `verify` (595 → 78), `wave` (454 → 65), `integrate` (394 → 69), `debug` (558 → 72), `quick` (legacy preserved → minimal), `partition` (560 → 49), `plan` (491 → 45), `impact` (314 → 45), `milestone` (143 → 44), `prd` (330 → 44), `design-decompose` (517 → 44), `doc-ripple` (156 → 40).
|
|
120
|
+
|
|
121
|
+
Each command file now describes WHAT (its phase's purpose) and invokes its corresponding Workflow script with explicit `args` shape.
|
|
122
|
+
|
|
123
|
+
#### Migration guide
|
|
124
|
+
|
|
125
|
+
| If you were using… | …now use this |
|
|
126
|
+
|--------------------|---------------|
|
|
127
|
+
| `gsd-t-unattended` for cross-session orchestration | Native background Workflows + `/loop` for recurring tasks |
|
|
128
|
+
| `gsd-t backfill-tokens` for historical cost analysis | OpenTelemetry exports (Enterprise) |
|
|
129
|
+
| `captureSpawn`/`recordSpawnRow` wrappers in your own scripts | Workflow `budget.spent()` / `/usage` |
|
|
130
|
+
| `gsd-t stream-feed` watching dashboard | `/workflows` + Agent View |
|
|
131
|
+
| Custom orchestrator code on top of `gsd-t-orchestrator.js` | Write a Workflow script in `templates/workflows/` style |
|
|
132
|
+
| Editing `commands/gsd-t-*.md` to add steps | Edit the corresponding `templates/workflows/*.workflow.js` |
|
|
133
|
+
|
|
134
|
+
If you cannot migrate immediately, pin `"@tekyzinc/gsd-t": "3.x"` in package.json. v3.x is preserved on the `v3.x-legacy` git branch.
|
|
135
|
+
|
|
136
|
+
### Verification
|
|
137
|
+
|
|
138
|
+
- Baseline locked at v3.29.11: bin/ = 37,785 LOC / 109 files.
|
|
139
|
+
- Post-M61: bin/ = 19,855 LOC / 78 files. Reduction: -17,930 LOC (47% of bin/ retired; 70% of way to SC1 target ≤12,000).
|
|
140
|
+
- Full unit suite: 41 failures remain post-Wave-3. 100% are command-file format tests (M56-D3 markers, Stack Rules, preflight wire-in blocks, OBSERVABILITY LOGGING tests, `shim_one_liner_*`) for the legacy command-file shape the Workflow conversions explicitly replace, + command-count tests for retired subcommands. Zero new behavior regressions.
|
|
141
|
+
- 4.8 audit (fresh-context Opus 4.8 review Workflow) returned `agree-with-edits` on all 8 artifact groups: core architecture sound, no revert needed.
|
|
142
|
+
- Retire→native map archived under `.gsd-t/milestones/m61-platform-reconciliation-2026-05-29/retire-to-native-map.md`.
|
|
143
|
+
|
|
144
|
+
### Deferred to follow-up milestones
|
|
145
|
+
|
|
146
|
+
- **M62 (post-M61): Cross-project propagation** — run `gsd-t version-update-all` to propagate v4.0.10 to all registered projects (rewrite their CLAUDE.md + retire docs; cleanup `~/.claude/settings.json` hooks for retired infrastructure).
|
|
147
|
+
- **SC7 cockpit walkthrough** — scripted desktop-only walkthrough of a small post-M61 backlog item (zero terminal keystrokes). User-driven.
|
|
148
|
+
- **D6 port-then-delete completion** — `bin/gsd-t-orchestrator.js`, `gsd-t-orchestrator-{worker,queue,config}.cjs`, `gsd-t-parallel.cjs`, `parallel-cli.cjs`, `spawn-plan-writer.cjs` are stubbed to load cleanly post-D2/D4 but await final deletion after a real M58 reproduction via the Workflow scripts validates the migration. Expected ~6,000 LOC additional reduction.
|
|
149
|
+
- **Deeper SC7 walkthrough on a UI-heavy milestone** — broader test of desktop's browser-tooling advantage.
|
|
150
|
+
|
|
151
|
+
## [3.29.11] - 2026-05-29 09:57 PDT
|
|
152
|
+
|
|
153
|
+
### Fixed — CRITICAL test-data adapter data-destruction bug (M60)
|
|
154
|
+
|
|
155
|
+
Found by a native-Workflow Red Team bake-off (Opus 4.8, perspective-diverse
|
|
156
|
+
adversarial review) while evaluating GSD-T against native Claude Code 4.8
|
|
157
|
+
capability. M58's own in-session Red Team had passed this as "6/6 defended"
|
|
158
|
+
— the claim was wrong.
|
|
159
|
+
|
|
160
|
+
- **Bug**: `bin/gsd-t-test-data-adapters/file-json-array.cjs` and
|
|
161
|
+
`localstorage-key-prefix.cjs` guarded with
|
|
162
|
+
`typeof taggedPrefix === 'string' && taggedPrefix.length > 0 && !id.startsWith(...)`.
|
|
163
|
+
An empty/undefined `taggedPrefix` short-circuits the whole condition, so
|
|
164
|
+
**no guard runs** — `gsd-t test-data --purge` would delete untagged
|
|
165
|
+
production records and report `errors:0` (gate GREEN). No adapter enforced
|
|
166
|
+
`projectDir` containment on the `store` path, making it a write-anywhere
|
|
167
|
+
delete primitive. Reproduced live before fixing.
|
|
168
|
+
- **Fix** (additive refusal only — removes capability, never adds destructive
|
|
169
|
+
behavior): both adapters now hard-refuse empty/undefined `taggedPrefix`
|
|
170
|
+
(matching the already-correct `sqlite-table-where` adapter); `file-json-array`
|
|
171
|
+
and `sqlite-table-where` enforce the containment predicate
|
|
172
|
+
`resolved.startsWith(root + sep) && resolved !== root` when `projectDir` is
|
|
173
|
+
supplied; `purgeRunInserts` threads `projectDir` into every adapter call.
|
|
174
|
+
- **Tests**: new `test/m60-redteam-regressions.test.js` (10 tests, one per
|
|
175
|
+
finding + happy-path + back-compat). M58 suite 44/44 unchanged.
|
|
176
|
+
- **Contract**: `test-data-tagging-contract.md` → v1.1.0 STABLE (empty-prefix
|
|
177
|
+
refusal + path containment now normative).
|
|
178
|
+
- Backward-compatible: the happy path (non-empty prefix, in-project path, or
|
|
179
|
+
no `projectDir`) is unchanged.
|
|
180
|
+
|
|
5
181
|
## [3.29.10] - 2026-05-27 10:09 PDT
|
|
6
182
|
|
|
7
183
|
### Changed — Timestamp precision in progress.md (forward-only)
|
package/bin/gsd-t-parallel.cjs
CHANGED
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
* - D1 task graph (bin/gsd-t-task-graph.cjs)
|
|
11
11
|
* - D4 depgraph validation (bin/gsd-t-depgraph-validate.cjs)
|
|
12
12
|
* - D5 file-disjointness (bin/gsd-t-file-disjointness.cjs)
|
|
13
|
-
* -
|
|
14
|
-
* - token-budget (bin/token-budget.cjs) — in-session ctxPct
|
|
15
|
-
* - mode-aware gating math (bin/gsd-t-orchestrator-config.cjs)
|
|
13
|
+
* - mode-aware gating math (inlined below — formerly bin/gsd-t-orchestrator-config.cjs)
|
|
16
14
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* the
|
|
15
|
+
* M61/M65 note: the economics estimator (gsd-t-economics.cjs), token-budget,
|
|
16
|
+
* and the M40 orchestrator (bin/gsd-t-orchestrator.js) were retired — the
|
|
17
|
+
* orchestration core moved to native Workflow scripts (templates/workflows/).
|
|
18
|
+
* This module survives as the file-disjointness/ready-task planner consumed by
|
|
19
|
+
* `_lib.proveFileDisjointness()`; its mode-aware gating helpers, which used to
|
|
20
|
+
* live in gsd-t-orchestrator-config.cjs, are inlined below (M65) so this file
|
|
21
|
+
* no longer depends on the retired orchestrator config.
|
|
21
22
|
*
|
|
22
23
|
* Contract: `.gsd-t/contracts/wave-join-contract.md` v1.1.0 (§Mode-Aware Gating Math).
|
|
23
24
|
*
|
|
@@ -35,11 +36,61 @@ const path = require("node:path");
|
|
|
35
36
|
const { buildTaskGraph, getReadyTasks } = require(path.join(__dirname, "gsd-t-task-graph.cjs"));
|
|
36
37
|
const { validateDepGraph } = require(path.join(__dirname, "gsd-t-depgraph-validate.cjs"));
|
|
37
38
|
const { proveDisjointness } = require(path.join(__dirname, "gsd-t-file-disjointness.cjs"));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// M61 D3: gsd-t-economics retired. estimateTaskFootprint produced a per-task
|
|
40
|
+
// token+cost estimate the planner could consult for in-session-headroom
|
|
41
|
+
// math. Native budget primitives (Workflow `budget` + /usage) replace it.
|
|
42
|
+
// Stub to a zero-footprint estimate so the planner proceeds with default
|
|
43
|
+
// assumptions instead of crashing.
|
|
44
|
+
const estimateTaskFootprint = () => ({
|
|
45
|
+
inputTokens: 0, outputTokens: 0, costUSD: 0, source: "m61-stub",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// ─── Mode-aware gating math (inlined from gsd-t-orchestrator-config.cjs, M65) ──
|
|
49
|
+
// Contract: `.gsd-t/contracts/wave-join-contract.md` v1.1.0 §Mode-Aware Gating Math.
|
|
50
|
+
const IN_SESSION_CW_CEILING_PCT = 85;
|
|
51
|
+
const UNATTENDED_PER_WORKER_CW_PCT = 60;
|
|
52
|
+
const DEFAULT_SUMMARY_SIZE_PCT = 4;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* [in-session] headroom gate. Returns `{ok, reducedCount}`.
|
|
56
|
+
* ok=true iff `ctxPct + workerCount * summarySize ≤ IN_SESSION_CW_CEILING_PCT`.
|
|
57
|
+
* Otherwise reduces N repeatedly; final floor is N=1. NEVER refuses.
|
|
58
|
+
*/
|
|
59
|
+
function computeInSessionHeadroom(opts) {
|
|
60
|
+
const o = opts || {};
|
|
61
|
+
const ctxPct = Number.isFinite(o.ctxPct) ? o.ctxPct : 0;
|
|
62
|
+
const requested = Number.isFinite(o.workerCount) ? Math.max(0, Math.floor(o.workerCount)) : 0;
|
|
63
|
+
const summarySize = Number.isFinite(o.summarySize) ? o.summarySize : DEFAULT_SUMMARY_SIZE_PCT;
|
|
64
|
+
const ceiling = IN_SESSION_CW_CEILING_PCT;
|
|
65
|
+
|
|
66
|
+
if (ctxPct + requested * summarySize <= ceiling) {
|
|
67
|
+
return { ok: true, reducedCount: requested };
|
|
68
|
+
}
|
|
69
|
+
let n = requested - 1;
|
|
70
|
+
while (n > 1) {
|
|
71
|
+
if (ctxPct + n * summarySize <= ceiling) {
|
|
72
|
+
return { ok: true, reducedCount: n };
|
|
73
|
+
}
|
|
74
|
+
n -= 1;
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, reducedCount: 1 };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* [unattended] per-worker CW gate. Returns `{ok, split}`.
|
|
81
|
+
* ok=true, split=false if `estimatedCwPct ≤ threshold` (default 60).
|
|
82
|
+
* ok=false, split=true otherwise — caller MUST slice the task into multiple
|
|
83
|
+
* iters (this function only signals the split requirement).
|
|
84
|
+
*/
|
|
85
|
+
function computeUnattendedGate(opts) {
|
|
86
|
+
const o = opts || {};
|
|
87
|
+
const estimatedCwPct = Number.isFinite(o.estimatedCwPct) ? o.estimatedCwPct : 0;
|
|
88
|
+
const threshold = Number.isFinite(o.threshold) ? o.threshold : UNATTENDED_PER_WORKER_CW_PCT;
|
|
89
|
+
if (estimatedCwPct > threshold) {
|
|
90
|
+
return { ok: false, split: true };
|
|
91
|
+
}
|
|
92
|
+
return { ok: true, split: false };
|
|
93
|
+
}
|
|
43
94
|
|
|
44
95
|
// token-budget is optional at require-time so unit tests can stub via dependency injection.
|
|
45
96
|
let _tokenBudget = null;
|
|
@@ -155,7 +206,7 @@ function runParallel(opts) {
|
|
|
155
206
|
const dryRun = !!(opts && opts.dryRun);
|
|
156
207
|
const summarySize = Number.isFinite(opts && opts.summarySize)
|
|
157
208
|
? opts.summarySize
|
|
158
|
-
:
|
|
209
|
+
: DEFAULT_SUMMARY_SIZE_PCT;
|
|
159
210
|
|
|
160
211
|
const graph = buildTaskGraph({ projectDir });
|
|
161
212
|
let candidates = getReadyTasks(graph);
|
|
@@ -5,23 +5,47 @@
|
|
|
5
5
|
* `store` is the file path; `id` is the value of the `id` field on the matching row.
|
|
6
6
|
*
|
|
7
7
|
* Refuses to delete a record whose `id` does not start with `taggedPrefix`.
|
|
8
|
-
*
|
|
8
|
+
* `taggedPrefix` is REQUIRED and non-empty — an empty/omitted prefix would
|
|
9
|
+
* disable the guard entirely (Red Team CRITICAL, M60). Atomic rewrite
|
|
10
|
+
* (write-temp + rename).
|
|
11
|
+
*
|
|
12
|
+
* When `projectDir` is supplied, `store` MUST resolve inside it — the adapter
|
|
13
|
+
* refuses paths outside-AND-equal-to projectDir (containment predicate from
|
|
14
|
+
* feedback_destructive_path_ops_containment). Prevents a tampered ledger from
|
|
15
|
+
* becoming a write-anywhere delete primitive.
|
|
9
16
|
*/
|
|
10
17
|
const fs = require('node:fs');
|
|
11
18
|
const path = require('node:path');
|
|
12
19
|
|
|
13
20
|
const KIND = 'file-json-array';
|
|
14
21
|
|
|
15
|
-
function
|
|
22
|
+
function assertContained(store, projectDir) {
|
|
23
|
+
if (typeof projectDir !== 'string' || projectDir.length === 0) {
|
|
24
|
+
return; // no projectDir supplied — containment not enforceable, caller's choice
|
|
25
|
+
}
|
|
26
|
+
const root = path.resolve(projectDir);
|
|
27
|
+
const resolved = path.resolve(store);
|
|
28
|
+
if (!(resolved.startsWith(root + path.sep) && resolved !== root)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`file-json-array: store path "${store}" resolves outside projectDir "${projectDir}" — refused (containment guard)`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function purge({ store, id, taggedPrefix, projectDir }) {
|
|
16
36
|
if (typeof store !== 'string' || store.length === 0) {
|
|
17
37
|
throw new Error('file-json-array: store must be a non-empty file path');
|
|
18
38
|
}
|
|
19
39
|
if (typeof id !== 'string' || id.length === 0) {
|
|
20
40
|
throw new Error('file-json-array: id must be a non-empty string');
|
|
21
41
|
}
|
|
22
|
-
if (typeof taggedPrefix
|
|
42
|
+
if (typeof taggedPrefix !== 'string' || taggedPrefix.length === 0) {
|
|
43
|
+
throw new Error('file-json-array: taggedPrefix is required and must be non-empty (guard cannot be disabled)');
|
|
44
|
+
}
|
|
45
|
+
if (!id.startsWith(taggedPrefix)) {
|
|
23
46
|
throw new Error(`file-json-array: tag prefix mismatch (id="${id}", taggedPrefix="${taggedPrefix}")`);
|
|
24
47
|
}
|
|
48
|
+
assertContained(store, projectDir);
|
|
25
49
|
|
|
26
50
|
if (!fs.existsSync(store)) {
|
|
27
51
|
return 'absent';
|
|
@@ -22,7 +22,12 @@ async function purge({ page, store, id, taggedPrefix }) {
|
|
|
22
22
|
if (typeof id !== 'string' || id.length === 0) {
|
|
23
23
|
throw new Error('localStorage-key-prefix: id must be a non-empty string');
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
// taggedPrefix is REQUIRED and non-empty — an empty/omitted prefix would
|
|
26
|
+
// disable the guard entirely (Red Team CRITICAL, M60).
|
|
27
|
+
if (typeof taggedPrefix !== 'string' || taggedPrefix.length === 0) {
|
|
28
|
+
throw new Error('localStorage-key-prefix: taggedPrefix is required and must be non-empty (guard cannot be disabled)');
|
|
29
|
+
}
|
|
30
|
+
if (!id.startsWith(taggedPrefix)) {
|
|
26
31
|
throw new Error(`localStorage-key-prefix: tag prefix mismatch (id="${id}", taggedPrefix="${taggedPrefix}")`);
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -8,11 +8,25 @@
|
|
|
8
8
|
* still loads when the module isn't installed. Tests self-skip in that case.
|
|
9
9
|
*/
|
|
10
10
|
const fs = require('node:fs');
|
|
11
|
+
const path = require('node:path');
|
|
11
12
|
|
|
12
13
|
const KIND = 'sqlite-table-where';
|
|
13
14
|
|
|
14
15
|
const IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
16
|
|
|
17
|
+
function assertContained(dbPath, projectDir) {
|
|
18
|
+
if (typeof projectDir !== 'string' || projectDir.length === 0) {
|
|
19
|
+
return; // no projectDir supplied — containment not enforceable, caller's choice
|
|
20
|
+
}
|
|
21
|
+
const root = path.resolve(projectDir);
|
|
22
|
+
const resolved = path.resolve(dbPath);
|
|
23
|
+
if (!(resolved.startsWith(root + path.sep) && resolved !== root)) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`sqlite-table-where: dbPath "${dbPath}" resolves outside projectDir "${projectDir}" — refused (containment guard)`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
function parseStore(store) {
|
|
17
31
|
if (typeof store !== 'string') {
|
|
18
32
|
throw new Error('sqlite-table-where: store must be "dbPath|table|idColumn"');
|
|
@@ -34,7 +48,7 @@ function parseStore(store) {
|
|
|
34
48
|
return { dbPath, table, idColumn };
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
function purge({ store, id, taggedPrefix }) {
|
|
51
|
+
function purge({ store, id, taggedPrefix, projectDir }) {
|
|
38
52
|
const { dbPath, table, idColumn } = parseStore(store);
|
|
39
53
|
if (typeof id !== 'string' || id.length === 0) {
|
|
40
54
|
throw new Error('sqlite-table-where: id must be a non-empty string');
|
|
@@ -45,6 +59,7 @@ function purge({ store, id, taggedPrefix }) {
|
|
|
45
59
|
if (!id.startsWith(taggedPrefix)) {
|
|
46
60
|
throw new Error(`sqlite-table-where: tag prefix mismatch (id="${id}", taggedPrefix="${taggedPrefix}")`);
|
|
47
61
|
}
|
|
62
|
+
assertContained(dbPath, projectDir);
|
|
48
63
|
if (!fs.existsSync(dbPath)) {
|
|
49
64
|
return 'absent';
|
|
50
65
|
}
|
|
@@ -129,6 +129,7 @@ async function purgeRunInserts({ projectDir, runId, dryRun }) {
|
|
|
129
129
|
store: row.store,
|
|
130
130
|
id: row.id,
|
|
131
131
|
taggedPrefix: row.taggedPrefix,
|
|
132
|
+
projectDir, // thread through so path adapters can enforce containment (M60)
|
|
132
133
|
});
|
|
133
134
|
if (result === 'purged') {
|
|
134
135
|
purged.push(row);
|