cleargate 0.11.5 → 0.13.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/dist/MANIFEST.json +15 -15
- package/dist/admin-api/index.cjs +3 -1
- package/dist/admin-api/index.cjs.map +1 -1
- package/dist/admin-api/index.d.cts +1 -0
- package/dist/admin-api/index.d.ts +1 -0
- package/dist/admin-api/index.js +3 -1
- package/dist/admin-api/index.js.map +1 -1
- package/dist/{chunk-HZPJ5QX4.js → chunk-EG6YGT2O.js} +315 -33
- package/dist/chunk-EG6YGT2O.js.map +1 -0
- package/dist/cli.cjs +855 -359
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +316 -108
- package/dist/cli.js.map +1 -1
- package/dist/lib/lifecycle-reconcile.cjs +318 -34
- package/dist/lib/lifecycle-reconcile.cjs.map +1 -1
- package/dist/lib/lifecycle-reconcile.d.cts +55 -4
- package/dist/lib/lifecycle-reconcile.d.ts +55 -4
- package/dist/lib/lifecycle-reconcile.js +7 -3
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +6 -4
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +8 -4
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +138 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +162 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
- package/dist/templates/cleargate-planning/CLAUDE.md +4 -0
- package/dist/templates/cleargate-planning/MANIFEST.json +15 -15
- package/package.json +8 -9
- package/templates/cleargate-planning/.claude/agents/architect.md +6 -4
- package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
- package/templates/cleargate-planning/.claude/agents/developer.md +8 -4
- package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
- package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +138 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +162 -0
- package/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
- package/templates/cleargate-planning/CLAUDE.md +4 -0
- package/templates/cleargate-planning/MANIFEST.json +15 -15
- package/dist/chunk-HZPJ5QX4.js.map +0 -1
|
@@ -642,3 +642,141 @@ wiki:
|
|
|
642
642
|
```
|
|
643
643
|
|
|
644
644
|
Exceeding the ceiling fails `cleargate wiki lint` (enforcement mode). Under `--suggest`, the usage percentage is reported but the check does not fail. Reference: EPIC-015.
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## Type & Payload Contract
|
|
649
|
+
|
|
650
|
+
Every item pushed to the MCP server (`cleargate_push_item`) must conform to the following contract. Adapter authors implementing new PM-tool integrations MUST satisfy all requirements below before the server accepts the payload.
|
|
651
|
+
|
|
652
|
+
### Open-type validator
|
|
653
|
+
|
|
654
|
+
`type` is validated as an open vocabulary (new types do not require a server upgrade):
|
|
655
|
+
|
|
656
|
+
```
|
|
657
|
+
z.string().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Applied after lowercase-normalize. Types that fail this regex are rejected at the MCP transport layer with an L1 `TYPE_INVALID` error.
|
|
661
|
+
|
|
662
|
+
### KNOWN_TYPES — advisory registry (8 entries)
|
|
663
|
+
|
|
664
|
+
These 8 types have first-class gate and reporting support. Any type outside this list passes validation but triggers an L2 `TYPE_UNKNOWN` warning in the server log.
|
|
665
|
+
|
|
666
|
+
| Type | Description |
|
|
667
|
+
|---|---|
|
|
668
|
+
| `initiative` | High-level strategic investment |
|
|
669
|
+
| `epic` | Feature-level work breakdown |
|
|
670
|
+
| `story` | Executable unit of work (one sprint, one commit) |
|
|
671
|
+
| `bug` | Defect report + fix |
|
|
672
|
+
| `cr` | Change request — scope amendment or approach change |
|
|
673
|
+
| `proposal` | Stakeholder-authored item awaiting triage |
|
|
674
|
+
| `sprint` | Sprint plan artifact |
|
|
675
|
+
| `sprint_report` | Sprint closeout report |
|
|
676
|
+
|
|
677
|
+
### RESERVED_PAYLOAD_KEYS (5 entries)
|
|
678
|
+
|
|
679
|
+
The following keys are set exclusively by the MCP server or the CLI sync engine. Callers MUST NOT include them in `payload`; the server strips or rejects them on receipt.
|
|
680
|
+
|
|
681
|
+
| Key | Owner | Reason |
|
|
682
|
+
|---|---|---|
|
|
683
|
+
| `cleargate_id` | CLI stamp engine | Must match the frontmatter `cleargate_id` — server validates; caller cannot override |
|
|
684
|
+
| `pushed_by` | Server | Set from JWT `sub` → member lookup; client-supplied value is ignored |
|
|
685
|
+
| `pushed_at` | Server | Set to server UTC timestamp; client-supplied value is ignored |
|
|
686
|
+
| `last_synced_body_sha` | Sync engine | Conflict-detection cursor; tampering causes spurious diffs |
|
|
687
|
+
| `_version` | Server | Internal optimistic-lock counter; client tampering causes 409 |
|
|
688
|
+
|
|
689
|
+
### Minimum payload contract
|
|
690
|
+
|
|
691
|
+
Every push MUST include:
|
|
692
|
+
|
|
693
|
+
| Key | Level | Note |
|
|
694
|
+
|---|---|---|
|
|
695
|
+
| `cleargate_id` | **Required (L1 error if absent)** | `TYPE-NNN` or 5-digit numeric (see formats below) |
|
|
696
|
+
| `type` | **Required (L1 error if absent)** | Open vocabulary, validated by regex above |
|
|
697
|
+
| `title` | Recommended (L2 warning if absent) | Human-readable item title |
|
|
698
|
+
| `status` | Recommended (L2 warning if absent) | From §21 status vocabulary |
|
|
699
|
+
|
|
700
|
+
### `payload.origin` convention
|
|
701
|
+
|
|
702
|
+
`payload.origin` identifies the surface that produced the push. Gates fire or are bypassed based on this value:
|
|
703
|
+
|
|
704
|
+
| Value pattern | Gate behavior |
|
|
705
|
+
|---|---|
|
|
706
|
+
| `cleargate-cli` | All readiness gates run (standard path) |
|
|
707
|
+
| `adapter:<vendor>` | Gates bypass — adapter is assumed to have already validated externally. Example: `adapter:linear`, `adapter:jira` |
|
|
708
|
+
| `system:<service>` | Gates bypass — system-generated push (e.g. `system:reporter`, `system:wiki-ingest`) |
|
|
709
|
+
| absent / null | Treated as `cleargate-cli`; gates run |
|
|
710
|
+
|
|
711
|
+
### `cleargate_id` formats (2 valid forms)
|
|
712
|
+
|
|
713
|
+
| Format | Pattern | Example | Used for |
|
|
714
|
+
|---|---|---|---|
|
|
715
|
+
| Type-prefixed | `TYPE-NNN` (TYPE = uppercase letters, NNN = 1–5 digits) | `STORY-027-05`, `EPIC-003` | All standard work items |
|
|
716
|
+
| 5-digit numeric | Exactly 5 decimal digits | `00042` | Legacy PM-tool remote IDs; internal migration use only |
|
|
717
|
+
|
|
718
|
+
### L1 errorCode taxonomy (7 codes)
|
|
719
|
+
|
|
720
|
+
L1 errors cause the MCP tool call to return a structured error payload with shape `{ code, message, hint }` and HTTP 422 (or equivalent MCP error).
|
|
721
|
+
|
|
722
|
+
| Code | Condition | Hint |
|
|
723
|
+
|---|---|---|
|
|
724
|
+
| `TYPE_INVALID` | `type` fails regex validation | Normalize to lowercase-kebab; max 64 chars |
|
|
725
|
+
| `ID_INVALID` | `cleargate_id` does not match either valid format | Use `TYPE-NNN` or 5-digit numeric |
|
|
726
|
+
| `ID_MISSING` | `cleargate_id` absent from payload | Required field — stamp it before push |
|
|
727
|
+
| `TYPE_MISSING` | `type` absent from payload | Required field |
|
|
728
|
+
| `RESERVED_KEY` | Payload contains a `RESERVED_PAYLOAD_KEYS` member | Remove the key; server owns it |
|
|
729
|
+
| `APPROVED_GATE` | `payload.approved !== true` and `skipApprovedGate` not set | Set `approved: true` after human sign-off |
|
|
730
|
+
| `PROJECT_NOT_FOUND` | JWT `project_id` claim has no matching project row | Re-join with a valid invite URL |
|
|
731
|
+
|
|
732
|
+
### L2 warningCode taxonomy (3 codes)
|
|
733
|
+
|
|
734
|
+
L2 warnings are non-blocking. The push succeeds, but the server appends `[advisory: <code>]` to the item body and logs the warning.
|
|
735
|
+
|
|
736
|
+
| Code | Condition | Field |
|
|
737
|
+
|---|---|---|
|
|
738
|
+
| `TITLE_MISSING` | `title` absent or empty | `title` |
|
|
739
|
+
| `STATUS_MISSING` | `status` absent or not in §21 vocabulary | `status` |
|
|
740
|
+
| `TYPE_UNKNOWN` | `type` is not in `KNOWN_TYPES` advisory registry | `type` |
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Codebase / PM-Tool Boundary
|
|
745
|
+
|
|
746
|
+
**Rule:** `cleargate-cli/src/**` and `.claude/**` MUST NOT import any PM-tool SDK. PM-tool adapters live exclusively in `mcp/src/adapters/`. Credentials and connection config live in admin DB rows, configured via the admin console UI.
|
|
747
|
+
|
|
748
|
+
### Rationale
|
|
749
|
+
|
|
750
|
+
ClearGate must remain PM-tool-agnostic at the CLI and agent-definition layer so that:
|
|
751
|
+
1. The CLI can be installed in any repo regardless of which PM tool the team uses.
|
|
752
|
+
2. Agent prompts (`.claude/**`) do not hard-code vendor assumptions.
|
|
753
|
+
3. A new PM-tool adapter can be added by dropping a file in `mcp/src/adapters/` without touching `cleargate-cli/` or `.claude/`.
|
|
754
|
+
|
|
755
|
+
### Forbidden import patterns
|
|
756
|
+
|
|
757
|
+
Any `import` or `require` statement in `cleargate-cli/src/**/*.ts` or `.claude/**/*.{ts,sh,md}` that references the following patterns fails CI:
|
|
758
|
+
|
|
759
|
+
| Pattern | PM tool |
|
|
760
|
+
|---|---|
|
|
761
|
+
| `@linear/sdk` | Linear |
|
|
762
|
+
| `linear-sdk` | Linear (alt package) |
|
|
763
|
+
| `jira-client` | Jira |
|
|
764
|
+
| `node-jira-client` | Jira (alt package) |
|
|
765
|
+
| `jira.js` | Jira (alt package) |
|
|
766
|
+
| `azure-devops` | Azure DevOps |
|
|
767
|
+
| `@atlassian/` | Atlassian family |
|
|
768
|
+
|
|
769
|
+
### Allowed surface for PM-tool code
|
|
770
|
+
|
|
771
|
+
`mcp/src/adapters/` is the only surface where PM-tool SDK imports are permitted. Each adapter file is responsible for importing its SDK, mapping ClearGate payload shapes to PM-tool API calls, and surfacing errors as structured MCP errors.
|
|
772
|
+
|
|
773
|
+
### CI enforcement
|
|
774
|
+
|
|
775
|
+
`scripts/ci-no-pm-sdk.mjs` scans the two forbidden surfaces and exits non-zero on any match. Run via:
|
|
776
|
+
|
|
777
|
+
```
|
|
778
|
+
node scripts/ci-no-pm-sdk.mjs
|
|
779
|
+
npm run check:no-pm-sdk
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Comments (lines starting with `//`, `#`, `/*`, or `*`) are excluded from the scan. The script prints `✓ no forbidden PM-SDK imports` on a clean tree.
|
|
@@ -407,6 +407,79 @@ async function main() {
|
|
|
407
407
|
process.stdout.write('Step 2.6b skipped: CLEARGATE_SKIP_LIFECYCLE_CHECK=1 set (test seam).\n');
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
// ── Step 2.6c: Parent (Epic/Sprint) Rollup (CR-066) ──────────────────────
|
|
411
|
+
// Runs unconditionally (not gated by CLEARGATE_SKIP_LIFECYCLE_CHECK).
|
|
412
|
+
// For each active parent in delivery/pending-sync/, checks children coverage:
|
|
413
|
+
// auto-flip → rewrite status: Completed atomically, log one line.
|
|
414
|
+
// halt-partial → collect into haltList; exit 1 after processing all.
|
|
415
|
+
// halt-zero-children → collect into haltList; exit 1 after processing all.
|
|
416
|
+
// no-op / skip-deferred → silent.
|
|
417
|
+
// Defensive guard: if walkActiveParents is not a function (stale dist/), skip with warning.
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Atomic in-place status rewrite (raw-bytes regex-replace).
|
|
421
|
+
* Follows FLASHCARD 2026-04-24 #frontmatter #write-back pattern.
|
|
422
|
+
* @param {string} filePath
|
|
423
|
+
* @param {string} newStatus
|
|
424
|
+
*/
|
|
425
|
+
function setFrontmatterStatusAtomic(filePath, newStatus) {
|
|
426
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
427
|
+
const fm = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
428
|
+
if (!fm) throw new Error(`No frontmatter in ${filePath}`);
|
|
429
|
+
const newFm = fm[1].replace(/^status:.*$/m, `status: ${newStatus}`);
|
|
430
|
+
const newRaw = raw.replace(fm[1], newFm);
|
|
431
|
+
const tmp = filePath + '.tmp.' + process.pid;
|
|
432
|
+
fs.writeFileSync(tmp, newRaw, 'utf8');
|
|
433
|
+
fs.renameSync(tmp, filePath);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
process.stdout.write('Step 2.6c: rolling up parent statuses...\n');
|
|
437
|
+
try {
|
|
438
|
+
// Use __dirname-relative path so the import finds the ACTUAL built dist,
|
|
439
|
+
// not a fixture tmpdir override (CLEARGATE_REPO_ROOT may point elsewhere in tests).
|
|
440
|
+
const scriptRepoRoot26c = path.resolve(SCRIPTS_DIR, '..', '..');
|
|
441
|
+
const reconcilerMod26c = await import(
|
|
442
|
+
path.join(scriptRepoRoot26c, 'cleargate-cli', 'dist', 'lib', 'lifecycle-reconcile.js')
|
|
443
|
+
).catch(() => null);
|
|
444
|
+
|
|
445
|
+
if (!reconcilerMod26c || typeof reconcilerMod26c.walkActiveParents !== 'function') {
|
|
446
|
+
process.stdout.write('Step 2.6c skipped: walkActiveParents not in built CLI — rebuild cleargate-cli/.\n');
|
|
447
|
+
} else {
|
|
448
|
+
// Delivery paths come from REPO_ROOT (may be fixture tmpdir in tests)
|
|
449
|
+
const deliveryRoot26c = path.join(REPO_ROOT, '.cleargate', 'delivery');
|
|
450
|
+
const archiveRoot26c = path.join(deliveryRoot26c, 'archive');
|
|
451
|
+
const results26c = await reconcilerMod26c.walkActiveParents({
|
|
452
|
+
deliveryRoot: deliveryRoot26c,
|
|
453
|
+
archiveRoot: archiveRoot26c,
|
|
454
|
+
});
|
|
455
|
+
const flips26c = results26c.filter((r) => r.verdict === 'auto-flip');
|
|
456
|
+
const halts26c = results26c.filter(
|
|
457
|
+
(r) => r.verdict === 'halt-partial' || r.verdict === 'halt-zero-children',
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
for (const f of flips26c) {
|
|
461
|
+
setFrontmatterStatusAtomic(f.parent_path, 'Completed');
|
|
462
|
+
process.stdout.write(
|
|
463
|
+
`Step 2.6c: ${f.parent_id} status ${f.current_status} → Completed` +
|
|
464
|
+
` (${f.terminal_children.length}/${f.terminal_children.length} children Completed:` +
|
|
465
|
+
` ${f.terminal_children.join(', ')})\n`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (halts26c.length > 0) {
|
|
470
|
+
process.stderr.write(`Step 2.6c HALT: ${halts26c.length} parent(s) require manual ack:\n`);
|
|
471
|
+
for (const h of halts26c) {
|
|
472
|
+
process.stderr.write(` - [${h.verdict}] ${h.halt_reason}\n`);
|
|
473
|
+
}
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
process.stdout.write(`Step 2.6c passed: ${flips26c.length} parent(s) auto-flipped; no halts.\n`);
|
|
478
|
+
}
|
|
479
|
+
} catch (e26c) {
|
|
480
|
+
process.stderr.write(`Step 2.6c warning: parent rollup unavailable: ${e26c.message}\n`);
|
|
481
|
+
}
|
|
482
|
+
|
|
410
483
|
// ── Step 2.7: Worktree-Closed Check (CR-022 M1) ──────────────────────────
|
|
411
484
|
// Block close if any .worktrees/STORY-* path is present.
|
|
412
485
|
// v2 enforcing (exit 1); v1 advisory (warn + continue).
|
|
@@ -746,6 +819,95 @@ async function main() {
|
|
|
746
819
|
process.stderr.write('Run `cleargate sync work-items` manually to retry.\n');
|
|
747
820
|
}
|
|
748
821
|
|
|
822
|
+
// ── Step 7.4: MCP push of sprint plan + report ───────────────────────────────
|
|
823
|
+
// CR-064: mcp push sprint plan + report
|
|
824
|
+
// Runs after Step 7 (work-item sync) and BEFORE Step 7.5 (CR-063 wiki ingest).
|
|
825
|
+
// Per SDR-locked ordering: MCP push first, wiki ingest second (CR-064 §0.5 Q4 accepted).
|
|
826
|
+
// Non-fatal on failure: sprint stays Completed. MCP push can be retried manually.
|
|
827
|
+
process.stdout.write('Step 7.4: pushing sprint plan + report to MCP...\n');
|
|
828
|
+
try {
|
|
829
|
+
const cliBin74 = path.join(REPO_ROOT, 'cleargate-cli', 'dist', 'cli.js');
|
|
830
|
+
if (fs.existsSync(cliBin74)) {
|
|
831
|
+
// Resolve sprint plan path: prefer archive/, fall back to pending-sync/
|
|
832
|
+
const deliveryBase = path.join(REPO_ROOT, '.cleargate', 'delivery');
|
|
833
|
+
const archiveDir = path.join(deliveryBase, 'archive');
|
|
834
|
+
const pendingDir = path.join(deliveryBase, 'pending-sync');
|
|
835
|
+
let planPath = null;
|
|
836
|
+
for (const dir of [archiveDir, pendingDir]) {
|
|
837
|
+
if (!fs.existsSync(dir)) continue;
|
|
838
|
+
const match = fs.readdirSync(dir).find(
|
|
839
|
+
(f) => f.startsWith(sprintId + '_') && f.endsWith('.md'),
|
|
840
|
+
);
|
|
841
|
+
if (match) { planPath = path.join(dir, match); break; }
|
|
842
|
+
}
|
|
843
|
+
if (planPath && fs.existsSync(planPath)) {
|
|
844
|
+
try {
|
|
845
|
+
execSync(`node ${JSON.stringify(cliBin74)} push ${JSON.stringify(planPath)}`, {
|
|
846
|
+
stdio: 'inherit',
|
|
847
|
+
env: process.env,
|
|
848
|
+
timeout: 60000,
|
|
849
|
+
});
|
|
850
|
+
process.stdout.write('Step 7.4a passed: sprint plan pushed to MCP.\n');
|
|
851
|
+
} catch (err74a) {
|
|
852
|
+
process.stderr.write(`Step 7.4a warning: sprint plan push failed: ${/** @type {Error} */ (err74a).message}\n`);
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
process.stdout.write('Step 7.4a skipped: sprint plan file not found.\n');
|
|
856
|
+
}
|
|
857
|
+
// Resolve sprint report path: reuses reportFile resolved via legacy-fallback in Step 4
|
|
858
|
+
if (reportFile && fs.existsSync(reportFile)) {
|
|
859
|
+
try {
|
|
860
|
+
execSync(`node ${JSON.stringify(cliBin74)} push ${JSON.stringify(reportFile)}`, {
|
|
861
|
+
stdio: 'inherit',
|
|
862
|
+
env: process.env,
|
|
863
|
+
timeout: 60000,
|
|
864
|
+
});
|
|
865
|
+
process.stdout.write('Step 7.4b passed: sprint report pushed to MCP.\n');
|
|
866
|
+
} catch (err74b) {
|
|
867
|
+
process.stderr.write(`Step 7.4b warning: sprint report push failed: ${/** @type {Error} */ (err74b).message}\n`);
|
|
868
|
+
}
|
|
869
|
+
} else {
|
|
870
|
+
process.stdout.write('Step 7.4b skipped: sprint report file not found (legacy sprint or report not yet written).\n');
|
|
871
|
+
}
|
|
872
|
+
} else {
|
|
873
|
+
process.stdout.write('Step 7.4 skipped: CLI binary not found (non-fatal).\n');
|
|
874
|
+
}
|
|
875
|
+
} catch (err) {
|
|
876
|
+
// Non-fatal — sprint stays Completed; MCP push can be retried manually
|
|
877
|
+
process.stderr.write(`Step 7.4 warning: MCP push failed: ${/** @type {Error} */ (err).message}\n`);
|
|
878
|
+
process.stderr.write('Run `cleargate push <plan-or-report-path>` manually to retry.\n');
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// ── Step 7.5: wiki ingest sprint report ──────────────────────────────────
|
|
882
|
+
// CR-063: wiki ingest sprint report
|
|
883
|
+
// Runs after Step 7 (MCP sync). Non-fatal: sprint stays Completed on failure.
|
|
884
|
+
// CR-064 (Wave 3) inserts its MCP-push step at Step 7.4, immediately before this anchor.
|
|
885
|
+
process.stdout.write('Step 7.5: ingesting sprint report into wiki...\n');
|
|
886
|
+
try {
|
|
887
|
+
const cliBin = path.join(REPO_ROOT, 'cleargate-cli', 'dist', 'cli.js');
|
|
888
|
+
if (fs.existsSync(cliBin)) {
|
|
889
|
+
// Resolve report path using the same legacy-fallback rule as Step 4
|
|
890
|
+
// (SPRINT-NN_REPORT.md preferred; fall back to REPORT.md for legacy sprints)
|
|
891
|
+
const reportPath = reportFile; // reportFile is already resolved via legacy-fallback in Step 4
|
|
892
|
+
if (fs.existsSync(reportPath)) {
|
|
893
|
+
execSync(`node ${JSON.stringify(cliBin)} wiki ingest ${JSON.stringify(reportPath)}`, {
|
|
894
|
+
stdio: 'inherit',
|
|
895
|
+
env: process.env,
|
|
896
|
+
timeout: 60000,
|
|
897
|
+
});
|
|
898
|
+
process.stdout.write('Step 7.5 passed: sprint report ingested into wiki.\n');
|
|
899
|
+
} else {
|
|
900
|
+
process.stdout.write('Step 7.5 skipped: report file not found (legacy sprint or report not yet written).\n');
|
|
901
|
+
}
|
|
902
|
+
} else {
|
|
903
|
+
process.stdout.write('Step 7.5 skipped: CLI binary not found (non-fatal).\n');
|
|
904
|
+
}
|
|
905
|
+
} catch (err) {
|
|
906
|
+
// Non-fatal — sprint stays Completed; ingest can be retried manually
|
|
907
|
+
process.stderr.write(`Step 7.5 warning: wiki ingest sprint report failed: ${/** @type {Error} */ (err).message}\n`);
|
|
908
|
+
process.stderr.write('Run `cleargate wiki ingest <report-path>` manually to retry.\n');
|
|
909
|
+
}
|
|
910
|
+
|
|
749
911
|
// ── Step 8: Verbose post-close handoff list ───────────────────────────────
|
|
750
912
|
// Prints 6 explicit next-step items to stdout (CR-022 §3 M4).
|
|
751
913
|
{
|
|
@@ -31,7 +31,7 @@ parent_ref: "EPIC-{ID} | STORY-{ID}"
|
|
|
31
31
|
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
32
32
|
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
33
33
|
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
34
|
-
status: "Draft | Triaged | In Fix |
|
|
34
|
+
status: "Draft | Triaged | In Fix | Completed"
|
|
35
35
|
severity: "P0-Critical | P1-High | P2-Medium | P3-Low"
|
|
36
36
|
reporter: "{name}"
|
|
37
37
|
approved: false
|
|
@@ -31,7 +31,7 @@ parent_ref: "EPIC-{ID} | STORY-{ID}"
|
|
|
31
31
|
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
32
32
|
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
33
33
|
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
34
|
-
status: "Draft | In Review | Approved"
|
|
34
|
+
status: "Draft | In Review | Approved | Completed"
|
|
35
35
|
approved: false
|
|
36
36
|
created_at: "2026-04-17T00:00:00Z"
|
|
37
37
|
updated_at: "2026-04-17T00:00:00Z"
|
|
@@ -36,7 +36,7 @@ epic_id: "EPIC-{ID}"
|
|
|
36
36
|
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
37
37
|
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
38
38
|
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
39
|
-
status: "Draft"
|
|
39
|
+
status: "Draft" # lifecycle: Draft → Active → Completed
|
|
40
40
|
ambiguity: "🔴 High"
|
|
41
41
|
context_source: "PROPOSAL-{ID}.md"
|
|
42
42
|
owner: "{PM/PO name}"
|
|
@@ -32,7 +32,7 @@ hotfix_id: "{ID}"
|
|
|
32
32
|
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
33
33
|
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
34
34
|
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
35
|
-
status: "Draft"
|
|
35
|
+
status: "Draft" # lifecycle: Draft → In Fix → Completed
|
|
36
36
|
severity: "P2"
|
|
37
37
|
originating_signal: "user-report"
|
|
38
38
|
created_at: "{ISO}"
|
|
@@ -29,7 +29,7 @@ DO NOT output these instructions in the rendered file.
|
|
|
29
29
|
initiative_id: "INITIATIVE-{NNN}"
|
|
30
30
|
remote_id: null
|
|
31
31
|
source_tool: "linear | jira | github | manual-paste"
|
|
32
|
-
status: "{PM native status — e.g. Discovery, In Triage, Triaged}"
|
|
32
|
+
status: "{PM native status — e.g. Discovery, In Triage, Triaged, Completed}"
|
|
33
33
|
synced_at: null
|
|
34
34
|
triaged_at: null
|
|
35
35
|
spawned_items: []
|
|
@@ -46,7 +46,7 @@ template_version: 2
|
|
|
46
46
|
Each CR:bug and UR:bug counts toward Bug-Fix Tax (§3). CR:scope-change increments arch_bounces. -->
|
|
47
47
|
|
|
48
48
|
### STORY-NNN-NN: <Title>
|
|
49
|
-
- **Status:**
|
|
49
|
+
- **Status:** Completed | Escalated | Parking Lot | Carried Over
|
|
50
50
|
- **Complexity:** L<n>
|
|
51
51
|
- **Commit:** `<sha>`
|
|
52
52
|
- **Bounce count:** qa=N arch=N total=N
|
|
@@ -59,7 +59,7 @@ parent_epic_ref: "EPIC-{ID}"
|
|
|
59
59
|
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
60
60
|
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
61
61
|
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
62
|
-
status: "Draft"
|
|
62
|
+
status: "Draft" # lifecycle: Draft → In Review → Completed
|
|
63
63
|
ambiguity: "🔴 High"
|
|
64
64
|
context_source: "PROPOSAL-{ID}.md"
|
|
65
65
|
actor: "{Persona Name}"
|
|
@@ -53,6 +53,10 @@ This repository uses **ClearGate** — a standalone planning framework for AI co
|
|
|
53
53
|
|
|
54
54
|
**Cross-project orchestration.** When running an orchestrator from one project's repo against another project's sprint tree, export `ORCHESTRATOR_PROJECT_DIR=/absolute/path/to/target/repo` in the shell before launching the session. Overrides `CLAUDE_PROJECT_DIR`; sentinel + ledger writes route into the target's `.cleargate/sprint-runs/` tree. If the target has no `.cleargate/sprint-runs/.active` sentinel, writes land in the target's `_off-sprint` bucket — not the orchestrator's own repo.
|
|
55
55
|
|
|
56
|
+
**Codebase / PM-Tool Boundary (EPIC-027).** `cleargate-cli/src/**` and `.claude/**` MUST NOT import any PM-tool SDK (`@linear/sdk`, `jira-client`, `azure-devops`, `@atlassian/`, `linear-sdk`, `node-jira-client`, `jira.js`). PM-tool adapters live exclusively in `mcp/src/adapters/`; credentials live in admin DB rows. The type-and-payload contract (open-type validator, KNOWN_TYPES, RESERVED_PAYLOAD_KEYS, `payload.origin`, `cleargate_id` formats, L1 errorCode + L2 warningCode taxonomies) is fully documented in `.cleargate/knowledge/cleargate-protocol.md` §Type & Payload Contract and §Codebase/PM-Tool Boundary. CI enforcement: `npm run check:no-pm-sdk` (exits non-zero on any forbidden import). Target repos that ran `cleargate init` before SPRINT-27 should re-run `cleargate init` to pick up this boundary rule in their local bounded block.
|
|
57
|
+
|
|
58
|
+
**Single test runner (EPIC-028).** All three packages (mcp/, cleargate-cli/, admin/) use node:test exclusively — vitest is fully eliminated as of 2026-05-18. File naming: `*.node.test.ts`. Run via `tsx --test` (mcp/, cleargate-cli/) or `node --conditions browser --import tsx tests/run-tests.mjs` (admin/). The `--conditions browser` flag is required for admin/ — it enables jsdom-bootstrap via `setup-node-test.mjs`. Adding vitest back is forbidden; `check:no-vitest` pre-commit guard enforces this.
|
|
59
|
+
|
|
56
60
|
**Project overrides.** Content OUTSIDE this `<!-- CLEARGATE:START -->...<!-- CLEARGATE:END -->` block takes precedence where it conflicts with ClearGate defaults.
|
|
57
61
|
|
|
58
62
|
**Scope reminder.** ClearGate is a *planning* framework. It scaffolds how work gets planned and how the four-agent loop runs. It does not replace your project's build system, CI, test runner, or deployment tooling.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cleargate_version": "0.
|
|
3
|
-
"generated_at": "2026-05-
|
|
2
|
+
"cleargate_version": "0.13.0",
|
|
3
|
+
"generated_at": "2026-05-18T17:11:10.027Z",
|
|
4
4
|
"files": [
|
|
5
5
|
{
|
|
6
6
|
"path": ".claude/agents/architect.md",
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "823db34b934d080e648955326dcc3cd31899e90f62ecfa4a9344f5b473237084",
|
|
8
8
|
"tier": "agent",
|
|
9
9
|
"overwrite_policy": "always",
|
|
10
10
|
"preserve_on_uninstall": false
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": ".claude/agents/cleargate-wiki-lint.md",
|
|
28
|
-
"sha256": "
|
|
28
|
+
"sha256": "2a9212d81df9a68e167ec7a18093a73c6da0208f13685c6887d5bd832b56fb3d",
|
|
29
29
|
"tier": "agent",
|
|
30
30
|
"overwrite_policy": "always",
|
|
31
31
|
"preserve_on_uninstall": false
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"path": ".claude/agents/developer.md",
|
|
42
|
-
"sha256": "
|
|
42
|
+
"sha256": "db7963778d68654f2dc96658d60433fa79305a10eec937807f2f0ed0cd05ce89",
|
|
43
43
|
"tier": "agent",
|
|
44
44
|
"overwrite_policy": "always",
|
|
45
45
|
"preserve_on_uninstall": false
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
"path": ".claude/hooks/pre-commit-surface-gate.sh",
|
|
77
|
-
"sha256": "
|
|
77
|
+
"sha256": "8dd817fbe75ee53753e2fe1fc2ccd2b6efdb2b3c0b6ad3191bebd9640afdd6f8",
|
|
78
78
|
"tier": "hook",
|
|
79
79
|
"overwrite_policy": "always",
|
|
80
80
|
"preserve_on_uninstall": false
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"path": ".cleargate/knowledge/cleargate-protocol.md",
|
|
168
|
-
"sha256": "
|
|
168
|
+
"sha256": "0380e74efa8aa782b77952fa68cc0171de035653e777d5c241d84c6e0414957b",
|
|
169
169
|
"tier": "protocol",
|
|
170
170
|
"overwrite_policy": "merge-3way",
|
|
171
171
|
"preserve_on_uninstall": false
|
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
},
|
|
201
201
|
{
|
|
202
202
|
"path": ".cleargate/scripts/close_sprint.mjs",
|
|
203
|
-
"sha256": "
|
|
203
|
+
"sha256": "b14b65f15c5ad57a845df89c069b5417195651b7d3aa7f7f5736416d0db0b868",
|
|
204
204
|
"tier": "script",
|
|
205
205
|
"overwrite_policy": "always",
|
|
206
206
|
"preserve_on_uninstall": false
|
|
@@ -396,35 +396,35 @@
|
|
|
396
396
|
},
|
|
397
397
|
{
|
|
398
398
|
"path": ".cleargate/templates/Bug.md",
|
|
399
|
-
"sha256": "
|
|
399
|
+
"sha256": "cebca344b6b820525c603444cf52626e120ebaa1ac28da099dc19c9cff39302c",
|
|
400
400
|
"tier": "template",
|
|
401
401
|
"overwrite_policy": "merge-3way",
|
|
402
402
|
"preserve_on_uninstall": false
|
|
403
403
|
},
|
|
404
404
|
{
|
|
405
405
|
"path": ".cleargate/templates/CR.md",
|
|
406
|
-
"sha256": "
|
|
406
|
+
"sha256": "ea5acf2087808e0d52806a87a6a7f1ced7473b591857f322fab5285adc80d25a",
|
|
407
407
|
"tier": "template",
|
|
408
408
|
"overwrite_policy": "merge-3way",
|
|
409
409
|
"preserve_on_uninstall": false
|
|
410
410
|
},
|
|
411
411
|
{
|
|
412
412
|
"path": ".cleargate/templates/epic.md",
|
|
413
|
-
"sha256": "
|
|
413
|
+
"sha256": "f9cf44db19288f0756b76bc8b13a075d4089990324db6febd072f5cf93d59cd0",
|
|
414
414
|
"tier": "template",
|
|
415
415
|
"overwrite_policy": "merge-3way",
|
|
416
416
|
"preserve_on_uninstall": false
|
|
417
417
|
},
|
|
418
418
|
{
|
|
419
419
|
"path": ".cleargate/templates/hotfix.md",
|
|
420
|
-
"sha256": "
|
|
420
|
+
"sha256": "de788497b4d224500036773f854801c056d73b08c3c0d66fdb641f17a1610bca",
|
|
421
421
|
"tier": "template",
|
|
422
422
|
"overwrite_policy": "merge-3way",
|
|
423
423
|
"preserve_on_uninstall": false
|
|
424
424
|
},
|
|
425
425
|
{
|
|
426
426
|
"path": ".cleargate/templates/initiative.md",
|
|
427
|
-
"sha256": "
|
|
427
|
+
"sha256": "1170e595f5813c62f86212d6ca1955d84465f396c81aaf34498b8e2d4595d681",
|
|
428
428
|
"tier": "template",
|
|
429
429
|
"overwrite_policy": "merge-3way",
|
|
430
430
|
"preserve_on_uninstall": false
|
|
@@ -445,14 +445,14 @@
|
|
|
445
445
|
},
|
|
446
446
|
{
|
|
447
447
|
"path": ".cleargate/templates/sprint_report.md",
|
|
448
|
-
"sha256": "
|
|
448
|
+
"sha256": "5914d54080f6110be5a5e905e3312811f3d0f80978121b1e15707d5be05cc5b1",
|
|
449
449
|
"tier": "template",
|
|
450
450
|
"overwrite_policy": "merge-3way",
|
|
451
451
|
"preserve_on_uninstall": false
|
|
452
452
|
},
|
|
453
453
|
{
|
|
454
454
|
"path": ".cleargate/templates/story.md",
|
|
455
|
-
"sha256": "
|
|
455
|
+
"sha256": "0badf01a080bca552a06fb64becd9b8c88fbf104e30f32667263522c3ff81051",
|
|
456
456
|
"tier": "template",
|
|
457
457
|
"overwrite_policy": "merge-3way",
|
|
458
458
|
"preserve_on_uninstall": false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cleargate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Planning framework for Claude Code agents — sprint/epic/story protocol, five-role agent team (architect/developer/qa/devops/reporter), Karpathy-style awareness wiki.",
|
|
@@ -47,12 +47,11 @@
|
|
|
47
47
|
"build": "tsup",
|
|
48
48
|
"dev": "tsup --watch",
|
|
49
49
|
"typecheck": "tsc --noEmit",
|
|
50
|
-
"test": "tsx --test --test-reporter=spec 'test/**/*.node.test.ts'",
|
|
51
|
-
"test:file": "tsx --test --test-reporter=spec",
|
|
52
|
-
"test:
|
|
53
|
-
"test:
|
|
54
|
-
"
|
|
55
|
-
"test:node:file": "tsx --test --test-reporter=spec"
|
|
50
|
+
"test": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
|
|
51
|
+
"test:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
|
|
52
|
+
"test:node": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
|
|
53
|
+
"test:node:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
|
|
54
|
+
"check:no-vitest": "node -e \"const r=require('child_process').execSync('grep -rE \\\"\\\\b(vitest|vi\\\\.fn|vi\\\\.mock|vi\\\\.spyOn|vi\\\\.stubGlobal|vi\\\\.useFakeTimers|vi\\\\.useRealTimers|vi\\\\.advanceTimersByTime|vi\\\\.hoisted)\\\\b\\\" --include=\\\"*.ts\\\" --include=\\\"*.js\\\" --include=\\\"*.mjs\\\" --exclude-dir=test/fixtures . 2>/dev/null || true', {encoding:'utf8'}); if(r.trim()){console.error('vitest residue detected:\\\\n'+r); process.exit(1)} console.log('no vitest residue')\""
|
|
56
55
|
},
|
|
57
56
|
"dependencies": {
|
|
58
57
|
"@napi-rs/keyring": "^1.2.0",
|
|
@@ -67,9 +66,9 @@
|
|
|
67
66
|
"@types/js-yaml": "^4.0.9",
|
|
68
67
|
"@types/node": "^24.0.0",
|
|
69
68
|
"@types/pg": "^8.11.10",
|
|
69
|
+
"ts-morph": "28.0.0",
|
|
70
70
|
"tsup": "^8",
|
|
71
71
|
"tsx": "^4.21.0",
|
|
72
|
-
"typescript": "^5.8.0"
|
|
73
|
-
"vitest": "^2.1.0"
|
|
72
|
+
"typescript": "^5.8.0"
|
|
74
73
|
}
|
|
75
74
|
}
|
|
@@ -77,19 +77,21 @@ Before a v2 sprint plan is confirmed by the human, you MUST write Sprint Plan §
|
|
|
77
77
|
|
|
78
78
|
**Trigger:** Orchestrator invokes you with all story files for the sprint milestone AND signals "Design Review requested". You produce §2 content and return it as a markdown block for the orchestrator to insert into the sprint plan file.
|
|
79
79
|
|
|
80
|
-
**§2 Execution Strategy —
|
|
80
|
+
**§2 Execution Strategy — five required subsections:**
|
|
81
81
|
|
|
82
|
-
1. **§2.1 Phase Plan** — Group stories into parallel waves vs sequential chains. Source: `parallel_eligible` field on each story's frontmatter + dependency graph from `## 3. Implementation Guide`. Explicitly state which stories can run concurrently and which must be serialized.
|
|
82
|
+
1. **§2.1 Phase Plan** — Group stories into parallel waves vs sequential chains. Before declaring any cross-story dependency, grep the codebase to verify the cited surface exists today vs. is created in-sprint. Story `## Existing Surfaces` bullets are advisory; the codebase is authoritative. Source: `parallel_eligible` field on each story's frontmatter + dependency graph from `## 3. Implementation Guide`. Explicitly state which stories can run concurrently and which must be serialized.
|
|
83
83
|
|
|
84
84
|
2. **§2.2 Merge Ordering** — Grep each story's "Files to modify" list for overlap. For every file touched by more than one story, determine which story lands first (typically the one that creates the section the other amends). Produce a table: `Shared File | Stories | Order | Rationale`.
|
|
85
85
|
|
|
86
86
|
3. **§2.3 Shared-Surface Warnings** — For each pair of stories that touch the same file, flag the specific risk: section collision, rename hazard, append-vs-insert conflict. One bullet per risk pair.
|
|
87
87
|
|
|
88
|
-
4. **§2.4
|
|
88
|
+
4. **§2.4 Lane Audit** — Per the seven-check Lane Classification rubric below; emit one row per fast-lane story with a ≤80-char rationale. Empty by default — rows added only for non-`standard` lanes. Full mechanics at "Lane Classification" section in this file.
|
|
89
|
+
|
|
90
|
+
5. **§2.5 ADR-Conflict Flags** — Cross-check each story's implementation approach against existing Architectural Decision Records in `.cleargate/knowledge/` and prior sprint decisions captured in flashcards. Flag any story that diverges from a locked decision.
|
|
89
91
|
|
|
90
92
|
**V-Bounce reference:** `skills/agent-team/SKILL.md` §"Architect Sprint Design Review (Phase 2 → Phase 3 transition)" at pinned SHA `2b8477ab65e39e594ee8b6d8cf13a210498eaded`.
|
|
91
93
|
|
|
92
|
-
**Output:** A single markdown block (§§2.1–2.
|
|
94
|
+
**Output:** A single markdown block (§§2.1–2.5 as shown above) ready for insertion into the sprint plan. Not a separate file. The orchestrator writes it into the plan.
|
|
93
95
|
|
|
94
96
|
These rules apply under `execution_mode: v2`. Under v1 the Design Review is informational.
|
|
95
97
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cleargate-wiki-lint
|
|
3
|
-
description: Use BEFORE Gate 1 (Proposal approval) and Gate 3 (Push) to enforce wiki-vs-raw consistency. Default mode (enforcement) exits non-zero on any drift, naming the offending page; halts gate transitions. `--suggest` mode (advisory) exits 0 and prints candidate cross-refs the ingest pass missed (Karpathy discovery). Performance: O(n), one pass per page plus one index cross-check; no all-pairs comparison.
|
|
3
|
+
description: "Use BEFORE Gate 1 (Proposal approval) and Gate 3 (Push) to enforce wiki-vs-raw consistency. Default mode (enforcement) exits non-zero on any drift, naming the offending page; halts gate transitions. `--suggest` mode (advisory) exits 0 and prints candidate cross-refs the ingest pass missed (Karpathy discovery). Performance: O(n), one pass per page plus one index cross-check; no all-pairs comparison."
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
@@ -72,13 +72,17 @@ flashcards_flagged:
|
|
|
72
72
|
|
|
73
73
|
## Inner-loop test runner
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
All tests use **`node:test` + `node:assert/strict`** — this is the single, mandatory runner across all ClearGate packages (EPIC-028, 2026-05-18). vitest is fully eliminated; adding it back is forbidden and blocked by the `check:no-vitest` pre-commit guard.
|
|
76
76
|
|
|
77
|
-
**
|
|
77
|
+
**File naming:** `*.node.test.ts` for all new test files.
|
|
78
78
|
|
|
79
|
-
**
|
|
79
|
+
**Run commands per package:**
|
|
80
|
+
- `mcp/` and `cleargate-cli/`: `tsx --test --test-concurrency=1 --experimental-test-module-mocks 'test/**/*.node.test.ts'`
|
|
81
|
+
- `admin/`: `node --conditions browser --import tsx tests/run-tests.mjs` — the `--conditions browser` flag is required; it triggers jsdom-bootstrap via `setup-node-test.mjs` for Svelte component tests.
|
|
80
82
|
|
|
81
|
-
**
|
|
83
|
+
**Mocking pattern:** prefer constructor-injected DI seams over module-level mocks. Inject the dependency via the constructor or function parameter and pass a fake in tests. For function-level mocks, use `mock.fn()` / `mock.method()` from `node:test`. For static-import un-interceptability in admin/ (e.g. toast, clipboard), use the `__overrides__` pattern: a `__mocks__/` stub with a mutable `__overrides__` object that the test sets before each call — see `admin/TESTING.md` for full pattern description.
|
|
84
|
+
|
|
85
|
+
**Full-suite verification at commit-time.** Use the project's standard test command (`npm test`, etc.) before committing.
|
|
82
86
|
|
|
83
87
|
## Script Invocation
|
|
84
88
|
|
|
@@ -23,6 +23,8 @@ else
|
|
|
23
23
|
echo "[red-gate] BYPASS: SKIP_RED_GATE=1 set — skipping Red-test immutability check. Log bypass in sprint §4." >&2
|
|
24
24
|
fi
|
|
25
25
|
|
|
26
|
+
if ! npm run check:no-vitest -s --prefix mcp 2>/dev/null || ! npm run check:no-vitest -s --prefix cleargate-cli 2>/dev/null || ! npm run check:no-vitest -s --prefix admin 2>/dev/null; then exit 1; fi
|
|
27
|
+
|
|
26
28
|
SCRIPT="${REPO_ROOT}/.cleargate/scripts/file_surface_diff.sh"
|
|
27
29
|
if [[ ! -f "${SCRIPT}" ]]; then
|
|
28
30
|
echo "[surface-gate] WARNING: file_surface_diff.sh not found — skipping" >&2
|