cleargate 0.14.0 → 0.15.1
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 +21 -0
- package/dist/MANIFEST.json +72 -16
- package/dist/admin-api/index.cjs +0 -1
- package/dist/admin-api/index.js +1 -2
- package/dist/auth/factory.cjs +0 -1
- package/dist/auth/factory.js +2 -3
- package/dist/auth/require-token.cjs +0 -1
- package/dist/auth/require-token.js +1 -2
- package/dist/auth/token-store.cjs +0 -1
- package/dist/auth/token-store.js +1 -2
- package/dist/{bootstrap-root-QKSA5V75.js → bootstrap-root-2H5HVTCC.js} +1 -2
- package/dist/{chunk-PDE37WFQ.js → chunk-A7MSQUU7.js} +2 -3
- package/dist/{chunk-BTSZOEWC.js → chunk-P6KEDAK2.js} +0 -1
- package/dist/{chunk-E3X7IE5E.js → chunk-PY6FHGV5.js} +1 -2
- package/dist/{chunk-5DI2Z3C2.js → chunk-Y53ZZYYU.js} +1 -2
- package/dist/cli.cjs +1564 -1414
- package/dist/cli.js +1514 -1364
- package/dist/lib/ledger.cjs +0 -1
- package/dist/lib/ledger.js +1 -2
- package/dist/lib/lifecycle-reconcile.cjs +0 -1
- package/dist/lib/lifecycle-reconcile.js +2 -3
- package/dist/{whoami-EANGN46Z.js → whoami-JKQQPABQ.js} +3 -4
- package/package.json +4 -3
- package/templates/cleargate-planning/.claude/agents/architect-synth.md +2 -0
- package/templates/cleargate-planning/.claude/agents/architect.md +4 -2
- package/templates/cleargate-planning/.claude/agents/developer.md +4 -11
- package/templates/cleargate-planning/.claude/agents/qa.md +14 -6
- package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +2 -2
- package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +19 -1
- package/templates/cleargate-planning/.cleargate/config.example.yml +16 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.deferred-verify.red.node.test.ts +245 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +227 -0
- package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +5 -4
- package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +75 -2
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +48 -0
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +57 -1
- package/templates/cleargate-planning/.cleargate/scripts/provision_worktree_config.sh +155 -0
- package/templates/cleargate-planning/.cleargate/scripts/qa_red_lint.mjs +380 -0
- package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +34 -1
- package/templates/cleargate-planning/.cleargate/scripts/test/cr077_eviction.red.sh +113 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr078_init.test.sh +309 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr079_provision.red.sh +262 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr080_wrapper.test.sh +177 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr081_qa_red_lint.red.sh +348 -0
- package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/.session-totals.json +1 -0
- package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/token-ledger.jsonl +222 -0
- package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +17 -0
- package/templates/cleargate-planning/.cleargate/templates/story.md +1 -0
- package/templates/cleargate-planning/MANIFEST.json +72 -16
- package/dist/admin-api/index.cjs.map +0 -1
- package/dist/admin-api/index.js.map +0 -1
- package/dist/auth/factory.cjs.map +0 -1
- package/dist/auth/factory.js.map +0 -1
- package/dist/auth/require-token.cjs.map +0 -1
- package/dist/auth/require-token.js.map +0 -1
- package/dist/auth/token-store.cjs.map +0 -1
- package/dist/auth/token-store.js.map +0 -1
- package/dist/bootstrap-root-QKSA5V75.js.map +0 -1
- package/dist/chunk-5DI2Z3C2.js.map +0 -1
- package/dist/chunk-BTSZOEWC.js.map +0 -1
- package/dist/chunk-E3X7IE5E.js.map +0 -1
- package/dist/chunk-PDE37WFQ.js.map +0 -1
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/lib/ledger.cjs.map +0 -1
- package/dist/lib/ledger.js.map +0 -1
- package/dist/lib/lifecycle-reconcile.cjs.map +0 -1
- package/dist/lib/lifecycle-reconcile.js.map +0 -1
- package/dist/templates/cleargate-planning/.claude/agents/architect-reader.md +0 -61
- package/dist/templates/cleargate-planning/.claude/agents/architect-synth.md +0 -124
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +0 -230
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +0 -108
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +0 -194
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +0 -261
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-query.md +0 -143
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +0 -185
- package/dist/templates/cleargate-planning/.claude/agents/devops.md +0 -257
- package/dist/templates/cleargate-planning/.claude/agents/qa.md +0 -171
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +0 -274
- package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +0 -209
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +0 -33
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +0 -58
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +0 -19
- package/dist/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +0 -162
- package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-autonomy.sh +0 -58
- package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +0 -148
- package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +0 -75
- package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +0 -43
- package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +0 -590
- package/dist/templates/cleargate-planning/.claude/settings.json +0 -68
- package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +0 -102
- package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +0 -742
- package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +0 -7
- package/dist/templates/cleargate-planning/.cleargate/config.example.yml +0 -67
- package/dist/templates/cleargate-planning/.cleargate/config.yml +0 -18
- package/dist/templates/cleargate-planning/.cleargate/delivery/archive/.gitkeep +0 -0
- package/dist/templates/cleargate-planning/.cleargate/delivery/pending-sync/.gitkeep +0 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +0 -551
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +0 -878
- package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +0 -160
- package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +0 -213
- package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +0 -71
- package/dist/templates/cleargate-planning/.cleargate/scripts/_migrate-schema-v3.mjs +0 -120
- package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +0 -265
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +0 -1012
- package/dist/templates/cleargate-planning/.cleargate/scripts/collision_surface.sh +0 -114
- package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +0 -62
- package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +0 -219
- package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +0 -320
- package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +0 -15
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +0 -38
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +0 -240
- package/dist/templates/cleargate-planning/.cleargate/scripts/launch_wave.mjs +0 -341
- package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +0 -54
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +0 -206
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +0 -371
- package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +0 -280
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +0 -378
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +0 -888
- package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +0 -209
- package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +0 -71
- package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +0 -127
- package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +0 -717
- package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +0 -27
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +0 -261
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +0 -210
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +0 -190
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +0 -482
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +0 -327
- package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +0 -261
- package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +0 -246
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +0 -111
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +0 -184
- package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +0 -172
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +0 -126
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +0 -130
- package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +0 -137
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +0 -166
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +0 -111
- package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +0 -122
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +0 -50
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +0 -224
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +0 -213
- package/dist/templates/cleargate-planning/CLAUDE.md +0 -66
- package/dist/templates/cleargate-planning/MANIFEST.json +0 -503
- package/dist/templates/synthesis/active-sprint.md +0 -30
- package/dist/templates/synthesis/open-gates.md +0 -38
- package/dist/templates/synthesis/product-state.md +0 -31
- package/dist/templates/synthesis/roadmap.md +0 -63
- package/dist/whoami-EANGN46Z.js.map +0 -1
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Mid-Sprint Triage Rubric
|
|
2
|
-
|
|
3
|
-
**CR-047 · SPRINT-23 · Authoritative Reference**
|
|
4
|
-
|
|
5
|
-
This document is the authoritative rubric for classifying mid-sprint user input. It complements the operational routing table in SKILL.md §C.10 (new rubric section). Read this doc to understand *why* a class exists; read SKILL.md §C.11 (post-CR-047 renumber) to see *how* routing works in practice.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
When a user injects input during an active sprint (between story kickoff and story merge), the orchestrator must classify it before routing. Unclassified input leads to either silent scope creep or unnecessary story restarts. The four classes below are mutually exclusive and exhaustive.
|
|
12
|
-
|
|
13
|
-
**Classifier aid:** `cleargate-cli/src/lib/triage-classifier.ts` exports a `classify()` pure function that performs keyword-heuristic pre-classification. Output is advisory — the orchestrator confirms before acting. `confidence: 'low'` always requires human verification.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Class 1: Bug
|
|
18
|
-
|
|
19
|
-
**Definition:** The user reports that existing implemented behaviour is incorrect, broken, or regressed against the current story's acceptance spec. A bug does NOT introduce new requirements — it identifies a gap between the spec and the actual behaviour.
|
|
20
|
-
|
|
21
|
-
**Keywords (heuristic):** broken, crashes, doesn't work, does not work, regression, nothing works, not working, failed, failure, error, exception, bug, defect, broke.
|
|
22
|
-
|
|
23
|
-
**Boundary cases:**
|
|
24
|
-
- "The button is broken" → Bug (existing behaviour broken vs spec).
|
|
25
|
-
- "The button should also glow" → NOT Bug; this is Scope Change (new requirement).
|
|
26
|
-
- "After the deploy nothing works" → Bug (regression language).
|
|
27
|
-
|
|
28
|
-
**Worked examples:**
|
|
29
|
-
|
|
30
|
-
1. "The login button is broken — it throws a 500 error instead of returning 401."
|
|
31
|
-
→ Class: Bug · Route: re-open story, Dev fixes, QA re-verifies.
|
|
32
|
-
|
|
33
|
-
2. "After the deploy the email sending stopped working."
|
|
34
|
-
→ Class: Bug · Route: same as above.
|
|
35
|
-
|
|
36
|
-
**Routing rules:**
|
|
37
|
-
|
|
38
|
-
- Increment `qa_bounces` via `update_state.mjs <story-id> --qa-bounce`.
|
|
39
|
-
- Re-open the story; Developer fixes; QA re-verifies (full loop).
|
|
40
|
-
- Log in sprint §4 Execution Log: date + story ID + one-line description.
|
|
41
|
-
- Human approval: NOT required (orchestrator routes autonomously).
|
|
42
|
-
|
|
43
|
-
**Bounce-counter impact:** `qa_bounces++`. If `qa_bounces ≥ 3` → `Escalated`, halt.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Class 2: Spec Clarification
|
|
48
|
-
|
|
49
|
-
**Definition:** The user asks a question or requests clarification about an existing requirement without adding new scope. The story's acceptance Gherkin remains unchanged after the clarification — at most, ambiguous language in §1.2 gets updated in place.
|
|
50
|
-
|
|
51
|
-
**Keywords (heuristic):** what does, what is, clarify, clarification, is the same as, the same as, same as, mean in, mean by, what do you mean, does it include, is this, should it.
|
|
52
|
-
|
|
53
|
-
**Boundary cases:**
|
|
54
|
-
- "What does 'eligible' mean in §3?" → Clarification (no new scope).
|
|
55
|
-
- "Is 'merged' the same as 'closed'?" → Clarification (terminology disambiguation).
|
|
56
|
-
- "What does 'eligible' mean — and should we also check for X?" → SPLIT: Clarification + Scope Change. Handle separately.
|
|
57
|
-
|
|
58
|
-
**Worked examples:**
|
|
59
|
-
|
|
60
|
-
1. "What does 'eligible' mean in the eligibility check requirement?"
|
|
61
|
-
→ Class: Clarification · Update §1.2 with the answer; no story restart.
|
|
62
|
-
|
|
63
|
-
2. "Is 'merged' the same as 'closed' for the purposes of the state machine?"
|
|
64
|
-
→ Class: Clarification · Add a definition note; no counter impact.
|
|
65
|
-
|
|
66
|
-
**Routing rules:**
|
|
67
|
-
|
|
68
|
-
- No counter increment.
|
|
69
|
-
- Update §1.2 (Acceptance Criteria) in place with the clarified definition.
|
|
70
|
-
- Re-run only the impacted test(s) (not full loop).
|
|
71
|
-
- Log in sprint §4 Execution Log: date + story ID + one-line clarification.
|
|
72
|
-
- Human approval: NOT required for terminology clarifications. Required if the clarification reveals a spec gap (surface to human before updating §1.2).
|
|
73
|
-
|
|
74
|
-
**Bounce-counter impact:** None.
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Class 3: Scope Change
|
|
79
|
-
|
|
80
|
-
**Definition:** The user introduces a net-new requirement that was not in the original story spec. Even if "obvious" or "related", if the Gherkin would need a new scenario, it is Scope Change.
|
|
81
|
-
|
|
82
|
-
**Keywords (heuristic):** also need, we also need, plus add, additionally, new requirement, add a, add an, plus, as well, in addition, new feature, extend with.
|
|
83
|
-
|
|
84
|
-
**Boundary cases:**
|
|
85
|
-
- "We also need a CSV export" → Scope Change (new feature).
|
|
86
|
-
- "Plus add audit logging" → Scope Change (additional requirement).
|
|
87
|
-
- "The export is broken" → NOT Scope Change; this is Bug.
|
|
88
|
-
|
|
89
|
-
**Worked examples:**
|
|
90
|
-
|
|
91
|
-
1. "We also need a CSV export alongside the existing JSON export."
|
|
92
|
-
→ Class: Scope Change · Quarantine into new story in `pending-sync/`. Current story unchanged.
|
|
93
|
-
|
|
94
|
-
2. "Plus add audit logging for all admin actions."
|
|
95
|
-
→ Class: Scope Change · Same quarantine routing.
|
|
96
|
-
|
|
97
|
-
**Routing rules:**
|
|
98
|
-
|
|
99
|
-
- **Quarantine by default.** Create a new Story file in `.cleargate/delivery/pending-sync/` for the next sprint.
|
|
100
|
-
- Current story proceeds UNCHANGED.
|
|
101
|
-
- **Goal-critical override:** if the new requirement is critical to the active sprint goal, escalate to human: *"This scope-change is goal-critical: the sprint goal is `<verbatim>` and without this change, the goal will not be met. Add to current sprint? (Adding mid-sprint requires explicit ack.)"*
|
|
102
|
-
- Log in sprint §4 Execution Log: date + new story ID + one-line description.
|
|
103
|
-
- Human approval: REQUIRED for mid-sprint addition; NOT required for quarantine.
|
|
104
|
-
|
|
105
|
-
**Bounce-counter impact:** None (quarantine path). If mid-sprint addition approved: treat as a new story dispatch (all counters reset for the new story).
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Class 4: Approach Change
|
|
110
|
-
|
|
111
|
-
**Definition:** The user proposes a different implementation technique or technology for the same spec. The acceptance Gherkin remains identical — only the *how* changes, not the *what*.
|
|
112
|
-
|
|
113
|
-
**Keywords (heuristic):** instead of, switch to, different way, different approach, replace with, rather than, alternative, migrate to.
|
|
114
|
-
|
|
115
|
-
**Boundary cases:**
|
|
116
|
-
- "Instead of polling, switch to websockets" → Approach Change (same behaviour spec, different mechanism).
|
|
117
|
-
- "Switch to Postgres instead of Redis for invite storage" → Approach Change (storage backend, same API).
|
|
118
|
-
- "Instead of storing invites, delete them" → NOT Approach Change; this is Scope Change (spec changes).
|
|
119
|
-
|
|
120
|
-
**Worked examples:**
|
|
121
|
-
|
|
122
|
-
1. "Instead of polling the API every 5 seconds, switch to websockets for real-time updates."
|
|
123
|
-
→ Class: Approach Change · No counter; reset Developer context; re-spawn with updated approach note.
|
|
124
|
-
|
|
125
|
-
2. "Use a different algorithm — BFS instead of DFS for the graph traversal."
|
|
126
|
-
→ Class: Approach Change · Same routing.
|
|
127
|
-
|
|
128
|
-
**Routing rules:**
|
|
129
|
-
|
|
130
|
-
- No counter increment.
|
|
131
|
-
- Reset Developer context (re-spawn Developer with the updated approach in the dispatch prompt).
|
|
132
|
-
- Story Gherkin and acceptance criteria remain UNCHANGED.
|
|
133
|
-
- Log in sprint §4 Execution Log: date + story ID + one-line approach delta.
|
|
134
|
-
- Human approval: NOT required if the approach is technically equivalent. Required if the approach change affects cost, timeline, or cross-story surfaces.
|
|
135
|
-
|
|
136
|
-
**Bounce-counter impact:** None.
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Routing Summary Table
|
|
141
|
-
|
|
142
|
-
| Class | Trigger | Counter | Human Approval | Routing Action |
|
|
143
|
-
|---|---|---|---|---|
|
|
144
|
-
| Bug | Defect vs spec | `qa_bounces++` | No | Re-open story; Dev fix; QA re-verify |
|
|
145
|
-
| Spec Clarification | Ambiguity question | None | No (yes if spec gap) | Update §1.2 in place; re-run impacted test |
|
|
146
|
-
| Scope Change | Net-new requirement | None | YES for mid-sprint add | Quarantine to next sprint (default); escalate if goal-critical |
|
|
147
|
-
| Approach Change | Different impl, same spec | None | No (yes if cross-story impact) | Reset Dev context; re-spawn with updated approach |
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Cross-References
|
|
152
|
-
|
|
153
|
-
- **SKILL.md §C.10** — NEW Mid-Sprint Triage section (operational routing table, added by CR-047).
|
|
154
|
-
- **SKILL.md §C.11** — Mid-cycle User Input table (pre-CR-047 §C.10; renumbered to §C.11 by this CR).
|
|
155
|
-
- **`cleargate-cli/src/lib/triage-classifier.ts`** — keyword-heuristic classifier (advisory, not authoritative).
|
|
156
|
-
- **SKILL.md §C.3.5** — TPV Gate (Architect-only wiring check between QA-Red and Developer).
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
_This document is append-only. Add new examples or boundary cases at the bottom of the relevant class block. Do not restructure class ordering — it matches the classifier priority (bug → approach → scope → clarification)._
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
# Readiness Gates
|
|
2
|
-
|
|
3
|
-
This file is the single source of truth for ClearGate's machine-checkable readiness gates. Each gate entry declares the `{work_item_type, transition}` pair it governs, along with a list of criteria expressed in the closed-set predicate vocabulary defined below. The predicate evaluator in `cleargate-cli/src/lib/readiness-predicates.ts` (STORY-008-02) reads these YAML blocks and evaluates them against a target document's frontmatter and body. Gate check results are cached in the document's own frontmatter under `cached_gate_result:` by `cleargate gate check <file>`.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Predicate Vocabulary
|
|
8
|
-
|
|
9
|
-
There are exactly **7 predicate shapes**. No other shapes are recognized; a check string that does not match one of these forms throws a parse error at evaluation time.
|
|
10
|
-
|
|
11
|
-
**1. `frontmatter(<ref>).<field> <op> <value>`**
|
|
12
|
-
Reads a frontmatter field from a document. `<ref>` is either `.` (the document being evaluated) or a frontmatter key whose value is a relative path to another document (e.g. `context_source`). `<op>` is one of `==`, `!=`, `>=`, `<=`. `<value>` is a literal string, number, or boolean. Example: `frontmatter(context_source).approved == true` reads the file named by the evaluated document's `context_source` key and asserts its `approved` field equals `true`.
|
|
13
|
-
|
|
14
|
-
**2. `body contains "<string>"` / `body does not contain "<string>"`**
|
|
15
|
-
Performs a case-sensitive substring search on the document body (everything after the frontmatter block). The negated form `body does not contain` passes when the string is absent. Example: `body does not contain 'TBD'` fails if the literal string `TBD` appears anywhere in the body.
|
|
16
|
-
|
|
17
|
-
**3. `section(<N>) has <count> <item-type>`**
|
|
18
|
-
Splits the document body on `## ` heading boundaries (1-indexed) and counts items of a given type within section N. `<count>` is an expression like `≥1`, `≥3`, or `0` (exact zero). `<item-type>` is one of:
|
|
19
|
-
- `checked-checkbox` — lines matching `- [x]`
|
|
20
|
-
- `unchecked-checkbox` — lines matching `- [ ]`
|
|
21
|
-
- `listed-item` — lines matching `- ` regardless of checkbox state (bullet-precise; use when checkbox/task-list semantics are required, e.g. DoD)
|
|
22
|
-
- `declared-item` — any line that declares a structured item: bullet lines (`- ...`), table data rows (`| ... |` lines following a `|---|`-style separator within the section), or definition-list terms (lines matching `**Item:**`, `Item:`, `*Item*:` etc.). Use `declared-item` when the gate cares only that the author declared at least N entries in section N, regardless of presentation format (table vs bullet vs def-list).
|
|
23
|
-
|
|
24
|
-
Example: `section(2) has ≥1 checked-checkbox` asserts that the second `##` section contains at least one checked markdown checkbox. Example: `section(3) has ≥1 declared-item` passes when §3 contains at least one bullet, table data row, or definition-list term.
|
|
25
|
-
|
|
26
|
-
**4. `file-exists(<path>)`**
|
|
27
|
-
Asserts that a file exists on disk at the given path, resolved relative to the project root. Example: `file-exists(.cleargate/knowledge/cleargate-protocol.md)` passes when that file is present in the working tree.
|
|
28
|
-
|
|
29
|
-
**5. `link-target-exists(<[[WORK-ITEM-ID]]>)`**
|
|
30
|
-
Reads `.cleargate/wiki/index.md` and asserts that the wiki index contains a reference to the given ID. Passes when the wiki has an entry for the linked item, meaning it has been ingested at least once. Example: `link-target-exists([[EPIC-008]])` passes when EPIC-008 appears in the compiled wiki index.
|
|
31
|
-
|
|
32
|
-
**6. `status-of(<[[ID]]>) == <value>`**
|
|
33
|
-
Resolves the given ID via the wiki index, reads that page's compiled frontmatter `status:` field, and compares it to `<value>`. Status values in the live corpus are textual strings (`Draft`, `Ready`, `Active`, `Done`) — not emoji. Example: `status-of([[EPIC-008]]) == Active` passes when EPIC-008's wiki page has `status: Active`. Note: this predicate returns `unknown` (evaluates to fail) when the wiki index is stale and the item is not yet compiled. Run `cleargate wiki build` before relying on `status-of` predicates.
|
|
34
|
-
|
|
35
|
-
**7. `existing-surfaces-verified`**
|
|
36
|
-
Closed-set predicate (no parameters). Locates the `## Existing Surfaces` section in the document body, extracts path-shaped substrings via regex, asserts each cited path exists on disk relative to the project root. Passes when section is absent (defers to `reuse-audit-recorded`) OR all cited paths exist OR section contains a "no overlap found" / "no existing surface" / "no prior implementation" / "audit returned empty" sentinel. Sandbox-rejected paths (escaping project root) are treated as missing. Example: `existing-surfaces-verified` against an Epic body whose `## Existing Surfaces` cites `cleargate-cli/src/lib/work-item-type.ts:detectWorkItemTypeFromFm` passes when that path exists.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Severity Model
|
|
41
|
-
|
|
42
|
-
Gates are classified as either **advisory** or **enforcing**.
|
|
43
|
-
|
|
44
|
-
**Advisory** gates (Proposal only) emit warnings and exit 0 regardless of pass/fail. They are informational checkpoints — they record `cached_gate_result.pass: false` in the document's frontmatter so an agent can read the state, but they never block a downstream action. Crucially, a Proposal's `approved: true` field is a pure human judgment: a Vibe Coder manually sets it after reviewing the document. The gate cannot and must not intercept that. Failing an advisory gate means "the document could be stronger" — it does not mean "the human may not approve."
|
|
45
|
-
|
|
46
|
-
**Enforcing** gates (Epic, Story, CR, Bug) exit non-zero on any failing criterion. `cleargate wiki lint` refuses to mark an Epic/Story/CR/Bug as 🟢-candidate when `cached_gate_result.pass == false` or when `last_gate_check < updated_at` (stale result). This ensures every enforcing gate check is fresh at the time of promotion.
|
|
47
|
-
|
|
48
|
-
The asymmetry exists because Proposal documents are human-authored strategy artifacts where partial drafts are normal and iterative. Epics, Stories, CRs, and Bugs represent engineering commitments where incomplete specification directly causes execution failures.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Gate Definitions
|
|
53
|
-
|
|
54
|
-
```yaml
|
|
55
|
-
- work_item_type: proposal
|
|
56
|
-
transition: ready-for-decomposition
|
|
57
|
-
severity: advisory
|
|
58
|
-
criteria:
|
|
59
|
-
- id: architecture-populated
|
|
60
|
-
check: "section(2) has ≥1 listed-item"
|
|
61
|
-
- id: touched-files-populated
|
|
62
|
-
check: "section(3) has ≥1 listed-item"
|
|
63
|
-
- id: no-tbds
|
|
64
|
-
check: "body does not contain marker 'TBD'"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
```yaml
|
|
68
|
-
- work_item_type: epic
|
|
69
|
-
transition: ready-for-decomposition
|
|
70
|
-
severity: enforcing
|
|
71
|
-
criteria:
|
|
72
|
-
- id: parent-approved-proposal
|
|
73
|
-
check: "frontmatter(context_source).approved == true"
|
|
74
|
-
or_group: parent-approved
|
|
75
|
-
- id: parent-approved-initiative
|
|
76
|
-
check: "frontmatter(context_source).status == 'Triaged'"
|
|
77
|
-
or_group: parent-approved
|
|
78
|
-
- id: no-tbds
|
|
79
|
-
check: "body does not contain marker 'TBD'"
|
|
80
|
-
- id: scope-in-populated
|
|
81
|
-
check: "section(3) has ≥1 declared-item"
|
|
82
|
-
- id: affected-files-declared
|
|
83
|
-
check: "section(5) has ≥1 declared-item"
|
|
84
|
-
- id: interrogation-resolved
|
|
85
|
-
check: "body does not contain 'Unresolved'"
|
|
86
|
-
- id: discovery-checked
|
|
87
|
-
check: "frontmatter(.).context_source != null"
|
|
88
|
-
- id: reuse-audit-recorded
|
|
89
|
-
check: "body contains '## Existing Surfaces'"
|
|
90
|
-
- id: existing-surfaces-verified
|
|
91
|
-
check: "existing-surfaces-verified"
|
|
92
|
-
- id: simplest-form-justified
|
|
93
|
-
check: "body contains '## Why not simpler?'"
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
```yaml
|
|
97
|
-
- work_item_type: epic
|
|
98
|
-
transition: ready-for-coding
|
|
99
|
-
severity: enforcing
|
|
100
|
-
criteria:
|
|
101
|
-
- id: stories-referenced
|
|
102
|
-
check: "body contains 'STORY-'"
|
|
103
|
-
- id: gherkin-happy-path
|
|
104
|
-
check: "body contains 'Scenario:'"
|
|
105
|
-
- id: gherkin-error-path
|
|
106
|
-
check: "body contains 'Error'"
|
|
107
|
-
- id: no-tbds
|
|
108
|
-
check: "body does not contain marker 'TBD'"
|
|
109
|
-
- id: interrogation-resolved
|
|
110
|
-
check: "body does not contain 'Unresolved'"
|
|
111
|
-
- id: discovery-checked
|
|
112
|
-
check: "frontmatter(.).context_source != null"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
```yaml
|
|
116
|
-
- work_item_type: story
|
|
117
|
-
transition: ready-for-execution
|
|
118
|
-
severity: enforcing
|
|
119
|
-
criteria:
|
|
120
|
-
- id: parent-epic-ref-set
|
|
121
|
-
check: "frontmatter(.).parent_epic_ref != null"
|
|
122
|
-
- id: no-tbds
|
|
123
|
-
check: "body does not contain marker 'TBD'"
|
|
124
|
-
- id: implementation-files-declared
|
|
125
|
-
check: "section(3) has ≥1 declared-item"
|
|
126
|
-
- id: dod-declared
|
|
127
|
-
check: "section(4) has ≥1 listed-item"
|
|
128
|
-
- id: gherkin-present
|
|
129
|
-
check: "body contains 'Scenario:'"
|
|
130
|
-
- id: discovery-checked
|
|
131
|
-
check: "frontmatter(.).context_source != null"
|
|
132
|
-
- id: reuse-audit-recorded
|
|
133
|
-
check: "body contains '## Existing Surfaces'"
|
|
134
|
-
- id: existing-surfaces-verified
|
|
135
|
-
check: "existing-surfaces-verified"
|
|
136
|
-
- id: simplest-form-justified
|
|
137
|
-
check: "body contains '## Why not simpler?'"
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
```yaml
|
|
141
|
-
- work_item_type: cr
|
|
142
|
-
transition: ready-to-apply
|
|
143
|
-
severity: enforcing
|
|
144
|
-
criteria:
|
|
145
|
-
- id: blast-radius-populated
|
|
146
|
-
check: "section(2) has ≥1 declared-item"
|
|
147
|
-
- id: no-tbds
|
|
148
|
-
check: "body does not contain marker 'TBD'"
|
|
149
|
-
- id: sandbox-paths-declared
|
|
150
|
-
check: "section(3) has ≥1 declared-item"
|
|
151
|
-
- id: discovery-checked
|
|
152
|
-
check: "frontmatter(.).context_source != null"
|
|
153
|
-
- id: reuse-audit-recorded
|
|
154
|
-
check: "body contains '## Existing Surfaces'"
|
|
155
|
-
- id: existing-surfaces-verified
|
|
156
|
-
check: "existing-surfaces-verified"
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
```yaml
|
|
160
|
-
- work_item_type: bug
|
|
161
|
-
transition: ready-for-fix
|
|
162
|
-
severity: enforcing
|
|
163
|
-
criteria:
|
|
164
|
-
- id: repro-steps-deterministic
|
|
165
|
-
check: "section(2) has ≥3 declared-item"
|
|
166
|
-
- id: severity-set
|
|
167
|
-
check: "frontmatter(.).severity != null"
|
|
168
|
-
- id: no-tbds
|
|
169
|
-
check: "body does not contain marker 'TBD'"
|
|
170
|
-
- id: discovery-checked
|
|
171
|
-
check: "frontmatter(.).context_source != null"
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
```yaml
|
|
175
|
-
- work_item_type: sprint
|
|
176
|
-
transition: ready-for-execution
|
|
177
|
-
severity: enforcing
|
|
178
|
-
criteria:
|
|
179
|
-
- id: risk-table-populated
|
|
180
|
-
check: "body contains '| Mitigation'"
|
|
181
|
-
- id: discovery-checked
|
|
182
|
-
check: "frontmatter(.).context_source != null"
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
```yaml
|
|
186
|
-
- work_item_type: initiative
|
|
187
|
-
transition: ready-for-decomposition
|
|
188
|
-
severity: advisory
|
|
189
|
-
criteria:
|
|
190
|
-
- id: no-tbds
|
|
191
|
-
check: "body does not contain marker 'TBD'"
|
|
192
|
-
- id: user-flow-populated
|
|
193
|
-
check: "section(1) has ≥1 listed-item"
|
|
194
|
-
- id: success-criteria-populated
|
|
195
|
-
check: "section(5) has ≥1 listed-item"
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
```yaml
|
|
199
|
-
- work_item_type: hotfix
|
|
200
|
-
transition: ready-for-merge
|
|
201
|
-
severity: enforcing
|
|
202
|
-
criteria:
|
|
203
|
-
- id: anomaly-populated
|
|
204
|
-
check: "section(2) has ≥1 listed-item"
|
|
205
|
-
- id: files-touched-declared
|
|
206
|
-
check: "section(3) has ≥1 declared-item"
|
|
207
|
-
- id: verification-steps-nonempty
|
|
208
|
-
check: "section(4) has ≥1 unchecked-checkbox"
|
|
209
|
-
- id: severity-set
|
|
210
|
-
check: "frontmatter(.).severity != null"
|
|
211
|
-
- id: no-tbds
|
|
212
|
-
check: "body does not contain marker 'TBD'"
|
|
213
|
-
```
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Sprint Closeout Doc & Metadata Refresh Checklist
|
|
2
|
-
|
|
3
|
-
> Read at sprint close (Gate 4 ack). Each item names a surface that may need
|
|
4
|
-
> updating based on what shipped this sprint. Trigger conditions tell you when
|
|
5
|
-
> review is required vs when the surface can be skipped.
|
|
6
|
-
>
|
|
7
|
-
> Use `node .cleargate/scripts/prep_doc_refresh.mjs <sprint-id>` to generate
|
|
8
|
-
> a per-sprint tailored checklist that pre-checks items based on actual
|
|
9
|
-
> changed files in the sprint window.
|
|
10
|
-
|
|
11
|
-
### 1. Project READMEs
|
|
12
|
-
| Surface | Trigger condition |
|
|
13
|
-
|---|---|
|
|
14
|
-
| `README.md` | Any feature shipped that changes user-visible product behavior |
|
|
15
|
-
| `cleargate-cli/README.md` | Any change to `cleargate-cli/src/commands/*.ts` |
|
|
16
|
-
| `cleargate-planning/README.md` | Any change under `cleargate-planning/` |
|
|
17
|
-
| `mcp/README.md` | Any change under `mcp/src/` (note: nested repo; check separately) |
|
|
18
|
-
| `admin/README.md` | Any change under `admin/` (currently stub) |
|
|
19
|
-
|
|
20
|
-
### 2. CHANGELOG files (Common-Changelog format per STORY-016-03)
|
|
21
|
-
| Surface | Trigger condition |
|
|
22
|
-
|---|---|
|
|
23
|
-
| `cleargate-cli/CHANGELOG.md` | Any user-visible change in `cleargate-cli/` (CLI surface, error messages, package contents) |
|
|
24
|
-
| `mcp/CHANGELOG.md` | Any user-visible change in `mcp/` (if file exists) |
|
|
25
|
-
|
|
26
|
-
### 3. Manifest / package metadata
|
|
27
|
-
| Surface | Trigger condition |
|
|
28
|
-
|---|---|
|
|
29
|
-
| `cleargate-planning/MANIFEST.json` | Any change to `.claude/agents/*.md`, `.cleargate/templates/*`, `.cleargate/knowledge/*`, or `.cleargate/scripts/*`. Run `cleargate doctor` to verify scaffold registry. |
|
|
30
|
-
| `cleargate-cli/package.json` | Version bump only if releasing this sprint (release lane is separate from sprint close) |
|
|
31
|
-
| `mcp/package.json` | Version bump only if releasing this sprint |
|
|
32
|
-
|
|
33
|
-
### 4. CLAUDE.md "Active state" subsection
|
|
34
|
-
| Surface | Trigger condition |
|
|
35
|
-
|---|---|
|
|
36
|
-
| `CLAUDE.md` lines containing "Active state (as of YYYY-MM-DD)" | Any EPIC / CR / Bug / Hotfix archived this sprint, OR any stack version bumped |
|
|
37
|
-
| `cleargate-planning/CLAUDE.md` mirror | Same edit as live (CLEARGATE-tag-block region only — outside-block diverges intentionally) |
|
|
38
|
-
|
|
39
|
-
### 5. Wiki surfaces (auto-rebuilt by PostToolUse hooks; verify after close)
|
|
40
|
-
| Surface | Verify by |
|
|
41
|
-
|---|---|
|
|
42
|
-
| `.cleargate/wiki/active-sprint.md` | Read top of file; confirm sprint ID, status, and date are current |
|
|
43
|
-
| `.cleargate/wiki/index.md` | Read; confirm new artifacts (epics, stories, CRs) appear in the relevant sections |
|
|
44
|
-
| `.cleargate/wiki/product-state.md` | Read; confirm shipped capabilities are listed |
|
|
45
|
-
| `.cleargate/wiki/roadmap.md` | Read; confirm closed sprint moved from Active to Completed section |
|
|
46
|
-
|
|
47
|
-
### 6. INDEX surfaces (manual updates)
|
|
48
|
-
| Surface | Trigger condition |
|
|
49
|
-
|---|---|
|
|
50
|
-
| `.cleargate/INDEX.md` | If maintained as a curated roadmap; update when sprint closes |
|
|
51
|
-
| `.cleargate/delivery/INDEX.md` | Update epic/sprint map when new artifacts archived |
|
|
52
|
-
|
|
53
|
-
### 7. Frontmatter version stamps
|
|
54
|
-
| Surface | Action |
|
|
55
|
-
|---|---|
|
|
56
|
-
| Any `.cleargate/templates/*.md` modified this sprint | Run `cleargate stamp <path>` to bump `updated_at_version` |
|
|
57
|
-
| `.cleargate/knowledge/cleargate-protocol.md` (post-EPIC-024 slim) | Same |
|
|
58
|
-
| `.cleargate/knowledge/cleargate-enforcement.md` (post-EPIC-024 split) | Same |
|
|
59
|
-
| Any other `.cleargate/knowledge/*.md` modified | Same |
|
|
60
|
-
|
|
61
|
-
### 8. Knowledge doc cross-references
|
|
62
|
-
| Surface | Action |
|
|
63
|
-
|---|---|
|
|
64
|
-
| Any knowledge doc that cites `§N` of protocol or enforcement.md | Verify post-rewrite resolution still works (covered for SPRINT-17 specifically by STORY-024-02; revisit if any future § reorganization happens) |
|
|
65
|
-
|
|
66
|
-
### 9. Mirror parity audit
|
|
67
|
-
| Surface | Action |
|
|
68
|
-
|---|---|
|
|
69
|
-
| `cleargate-planning/.claude/` | `diff -r .claude/agents/ cleargate-planning/.claude/agents/` empty (excluding skills/ flashcards/, hooks/, settings.json which differ intentionally) |
|
|
70
|
-
| `cleargate-planning/.cleargate/templates/` | `diff -r .cleargate/templates/ cleargate-planning/.cleargate/templates/` empty |
|
|
71
|
-
| `cleargate-planning/.cleargate/knowledge/` | `diff -r .cleargate/knowledge/ cleargate-planning/.cleargate/knowledge/` empty |
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* _migrate-schema-v3.mjs — Strip-on-read migrator: execution_mode → retired, schema v2 → v3
|
|
4
|
-
*
|
|
5
|
-
* STORY-070-01: collapse execution_mode vocabulary.
|
|
6
|
-
*
|
|
7
|
-
* Usage (CLI): node _migrate-schema-v3.mjs --state-path <path>
|
|
8
|
-
* Usage (import): import { migrateStateToV3 } from './_migrate-schema-v3.mjs';
|
|
9
|
-
*
|
|
10
|
-
* Contract:
|
|
11
|
-
* - Reads state.json at <path>
|
|
12
|
-
* - If execution_mode present: deletes the key
|
|
13
|
-
* - If schema_version !== 3: bumps to 3
|
|
14
|
-
* - If anything changed: logs "[migrator] STORY-070-01: stripped execution_mode from <path>" to stderr
|
|
15
|
-
* - Atomic write via tmp+rename (no partial writes on crash)
|
|
16
|
-
* - Idempotent: if execution_mode absent and schema_version already 3 → no-op, no write, no log
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import fs from 'node:fs';
|
|
20
|
-
import path from 'node:path';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Migrate a parsed state object in-place: strip execution_mode + bump schema_version to 3.
|
|
25
|
-
* Does NOT write to disk. Returns { state, changed }.
|
|
26
|
-
*
|
|
27
|
-
* @param {Record<string, unknown>} state - Parsed state.json object (mutated in-place)
|
|
28
|
-
* @param {string} statePath - Path to the state.json file (used only for the log line)
|
|
29
|
-
* @returns {{ state: Record<string, unknown>, changed: boolean }}
|
|
30
|
-
*/
|
|
31
|
-
export function migrateStateToV3(state, statePath) {
|
|
32
|
-
let changed = false;
|
|
33
|
-
|
|
34
|
-
if ('execution_mode' in state) {
|
|
35
|
-
delete state.execution_mode;
|
|
36
|
-
changed = true;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (state.schema_version !== 3) {
|
|
40
|
-
state.schema_version = 3;
|
|
41
|
-
changed = true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (changed) {
|
|
45
|
-
process.stderr.write(`[migrator] STORY-070-01: stripped execution_mode from ${statePath}\n`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return { state, changed };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Read, migrate (if needed), and atomically write state.json at the given path.
|
|
53
|
-
* Exported for use by scripts that need the full read-migrate-write cycle.
|
|
54
|
-
*
|
|
55
|
-
* @param {string} statePath - Absolute path to state.json
|
|
56
|
-
* @returns {{ changed: boolean }} whether a write occurred
|
|
57
|
-
*/
|
|
58
|
-
export function migrateStateFileToV3(statePath) {
|
|
59
|
-
let raw;
|
|
60
|
-
try {
|
|
61
|
-
raw = fs.readFileSync(statePath, 'utf8');
|
|
62
|
-
} catch (err) {
|
|
63
|
-
process.stderr.write(`[migrator] ERROR: cannot read ${statePath}: ${err.message}\n`);
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let state;
|
|
68
|
-
try {
|
|
69
|
-
state = JSON.parse(raw);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
process.stderr.write(`[migrator] ERROR: cannot parse ${statePath}: ${err.message}\n`);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const { changed } = migrateStateToV3(state, statePath);
|
|
76
|
-
|
|
77
|
-
if (changed) {
|
|
78
|
-
const tmpFile = `${statePath}.tmp.${process.pid}`;
|
|
79
|
-
try {
|
|
80
|
-
fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
81
|
-
fs.renameSync(tmpFile, statePath);
|
|
82
|
-
} catch (err) {
|
|
83
|
-
// Clean up tmp if write/rename failed
|
|
84
|
-
try { fs.unlinkSync(tmpFile); } catch { /* ignore */ }
|
|
85
|
-
process.stderr.write(`[migrator] ERROR: cannot write ${statePath}: ${err.message}\n`);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return { changed };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ── CLI entry point ────────────────────────────────────────────────────────────
|
|
94
|
-
// When invoked as a script: node _migrate-schema-v3.mjs --state-path <path>
|
|
95
|
-
// Detect CLI mode via import.meta.url vs process.argv[1]
|
|
96
|
-
|
|
97
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
98
|
-
const isMain = process.argv[1] && (
|
|
99
|
-
process.argv[1] === __filename ||
|
|
100
|
-
path.resolve(process.argv[1]) === path.resolve(__filename)
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (isMain) {
|
|
104
|
-
const args = process.argv.slice(2);
|
|
105
|
-
const statePathIdx = args.indexOf('--state-path');
|
|
106
|
-
|
|
107
|
-
if (statePathIdx !== -1 && args[statePathIdx + 1]) {
|
|
108
|
-
const statePath = path.resolve(args[statePathIdx + 1]);
|
|
109
|
-
const { changed } = migrateStateFileToV3(statePath);
|
|
110
|
-
if (!changed) {
|
|
111
|
-
// Always emit the path on no-op so callers can confirm the file was checked.
|
|
112
|
-
// Uses a plain prefix (not '[migrator]') to distinguish from real migration logs.
|
|
113
|
-
process.stderr.write(`${statePath}: already at schema_version 3, no migration needed\n`);
|
|
114
|
-
}
|
|
115
|
-
process.exit(0);
|
|
116
|
-
} else {
|
|
117
|
-
process.stderr.write('Usage: node _migrate-schema-v3.mjs --state-path <path>\n');
|
|
118
|
-
process.exit(2);
|
|
119
|
-
}
|
|
120
|
-
}
|