@ktpartners/dgs-platform 2.6.3 → 2.7.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.
@@ -0,0 +1,96 @@
1
+ # Sync Hooks
2
+
3
+ Standard sync pattern for workflow boundaries. @-include this reference from any workflow that needs sync support.
4
+
5
+ Set `WORKFLOW_NAME` from the workflow's init call before following these steps.
6
+
7
+ ## Pre-Workflow Sync (Pull)
8
+
9
+ Execute BEFORE the workflow starts any DGS state modifications.
10
+
11
+ ### Step: sync_before
12
+
13
+ ```bash
14
+ SYNC_CHECK=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-pull "${WORKFLOW_NAME}" --check-only)
15
+ ```
16
+
17
+ Parse `SYNC_CHECK` JSON.
18
+
19
+ **If `first_run_hint` is not null:** Display it as a one-liner to stderr.
20
+ ```bash
21
+ node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-pull "${WORKFLOW_NAME}" --mark-hint-shown
22
+ ```
23
+
24
+ **If `should_pull` is false:** Skip pull entirely. Continue to workflow.
25
+
26
+ **If `stale.stale` is true and mode is `"off"`:** Display one-liner:
27
+ `Warning: origin/{branch} is {commitsBehind} commits ahead. Consider pulling.`
28
+
29
+ **If `should_pull` is true:**
30
+
31
+ - **If `mode` is `"auto"` OR `suppressed` is true:** Pull silently:
32
+ ```bash
33
+ PULL_RESULT=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-pull "${WORKFLOW_NAME}")
34
+ ```
35
+ Parse result. If `action` is `"aborted"`: report `diagnosis` and halt workflow.
36
+
37
+ - **If `mode` is `"prompt"` and `suppressed` is false:**
38
+ Prompt user: `Pull from remote before starting? [Y/n]`
39
+ - If Yes (or empty/default):
40
+ ```bash
41
+ node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-pull "${WORKFLOW_NAME}" --record-yes
42
+ PULL_RESULT=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-pull "${WORKFLOW_NAME}")
43
+ ```
44
+ Parse result. If `action` is `"aborted"`:
45
+ - Display diagnosis
46
+ - Prompt: `Continue without pulling? [y/N]`
47
+ - If No (default): halt workflow
48
+ - If Yes: continue with warning
49
+ - If No: skip pull, continue to workflow
50
+
51
+ ## Post-Workflow Sync (Push)
52
+
53
+ Execute AFTER the workflow completes all state modifications and commits.
54
+
55
+ ### Step: sync_after
56
+
57
+ ```bash
58
+ PUSH_CHECK=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-push "${WORKFLOW_NAME}" --check-only)
59
+ ```
60
+
61
+ Parse `PUSH_CHECK` JSON.
62
+
63
+ **If `should_push` is false:** Skip push entirely. Done.
64
+
65
+ **If `should_push` is true:**
66
+
67
+ - **If `mode` is `"auto"` OR `suppressed` is true:** Push silently:
68
+ ```bash
69
+ PUSH_RESULT=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-push "${WORKFLOW_NAME}")
70
+ ```
71
+ Parse result. Display `summary` if action is `"pushed"`. Display `message` if action is `"warning"`.
72
+
73
+ - **If `mode` is `"prompt"` and `suppressed` is false:**
74
+ Prompt user: `Push to remote? [Y/n]`
75
+ - If Yes (or empty/default):
76
+ ```bash
77
+ node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-push "${WORKFLOW_NAME}" --record-yes
78
+ PUSH_RESULT=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-push "${WORKFLOW_NAME}")
79
+ ```
80
+ Parse result. Display summary.
81
+ - If No: skip push, done
82
+
83
+ ## Mid-Workflow Push
84
+
85
+ For workflows that push at intermediate points (execute-phase after each plan, run-job after each phase).
86
+
87
+ ### Step: sync_mid_push
88
+
89
+ ```bash
90
+ PUSH_RESULT=$(node "$HOME/.claude/deliver-great-systems/bin/dgs-tools.cjs" sync workflow-push "${WORKFLOW_NAME}" --mid-workflow)
91
+ ```
92
+
93
+ Parse result. Mid-workflow pushes are always silent (no prompt). User consented at workflow start.
94
+
95
+ - If `action` is `"warning"`: accumulate warning for end-of-workflow summary
96
+ - If `action` is `"pushed"`: continue silently (optionally log if `message` mentions new remote branch creation)
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Tests for cadence table — workflow sync classification and init integration
3
+ */
4
+
5
+ const { describe, it } = require('node:test');
6
+ const assert = require('node:assert');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ const { getCadence, CADENCE_TABLE } = require('../bin/lib/sync.cjs');
11
+
12
+ // ─── Cadence Table Tests ─────────────────────────────────────────────────────
13
+
14
+ describe('CADENCE_TABLE', () => {
15
+ it('has exactly 70 entries', () => {
16
+ assert.strictEqual(Object.keys(CADENCE_TABLE).length, 70);
17
+ });
18
+ });
19
+
20
+ describe('getCadence — Pull + Push workflows (35)', () => {
21
+ const pullPush = [
22
+ 'execute-phase', 'plan-phase', 'discuss-phase', 'research-phase',
23
+ 'run-job', 'create-milestone-job', 'cancel-job', 'rollback-job',
24
+ 'new-milestone', 'complete-milestone', 'audit-milestone', 'plan-milestone-gaps',
25
+ 'cleanup', 'discuss-idea', 'develop-idea', 'research-idea',
26
+ 'consolidate-ideas', 'undo-consolidation', 'write-spec', 'refine-spec',
27
+ 'approve-spec', 'quick', 'fast', 'add-todo', 'check-todos',
28
+ 'new-project', 'init-product', 'add-phase', 'insert-phase', 'remove-phase',
29
+ 'audit-phase', 'validate-phase', 'verify-work', 'add-tests', 'settings',
30
+ ];
31
+
32
+ assert.strictEqual(pullPush.length, 35, 'Expected 35 pull+push workflows');
33
+
34
+ for (const name of pullPush) {
35
+ it(`${name} -> pull:true, push:true`, () => {
36
+ const result = getCadence(name);
37
+ assert.deepStrictEqual(result, { pull: true, push: true });
38
+ });
39
+ }
40
+ });
41
+
42
+ describe('getCadence — Push only workflows (17)', () => {
43
+ const pushOnly = [
44
+ 'pause-work', 'add-idea', 'import-spec', 'reject-idea', 'restore-idea',
45
+ 'update-idea', 'add-doc', 'remove-doc', 'add-repo', 'remove-repo',
46
+ 'capture-principle', 'complete-project', 'reactivate-project', 'switch-project',
47
+ 'set-profile', 'map-codebase', 'reapply-patches',
48
+ ];
49
+
50
+ assert.strictEqual(pushOnly.length, 17, 'Expected 17 push-only workflows');
51
+
52
+ for (const name of pushOnly) {
53
+ it(`${name} -> pull:false, push:true`, () => {
54
+ const result = getCadence(name);
55
+ assert.deepStrictEqual(result, { pull: false, push: true });
56
+ });
57
+ }
58
+ });
59
+
60
+ describe('getCadence — Pull only workflows (4)', () => {
61
+ const pullOnly = [
62
+ 'resume-work', 'progress', 'find-related-ideas', 'list-phase-assumptions',
63
+ ];
64
+
65
+ assert.strictEqual(pullOnly.length, 4, 'Expected 4 pull-only workflows');
66
+
67
+ for (const name of pullOnly) {
68
+ it(`${name} -> pull:true, push:false`, () => {
69
+ const result = getCadence(name);
70
+ assert.deepStrictEqual(result, { pull: true, push: false });
71
+ });
72
+ }
73
+ });
74
+
75
+ describe('getCadence — No sync workflows (14)', () => {
76
+ const noSync = [
77
+ 'help', 'join-discord', 'list-ideas', 'list-specs', 'list-docs',
78
+ 'list-jobs', 'list-projects', 'search', 'overlap-check', 'health',
79
+ 'update', 'debug', 'node-repair', 'sync-upstream',
80
+ ];
81
+
82
+ assert.strictEqual(noSync.length, 14, 'Expected 14 no-sync workflows');
83
+
84
+ for (const name of noSync) {
85
+ it(`${name} -> pull:false, push:false`, () => {
86
+ const result = getCadence(name);
87
+ assert.deepStrictEqual(result, { pull: false, push: false });
88
+ });
89
+ }
90
+ });
91
+
92
+ describe('getCadence — unknown workflows', () => {
93
+ it('returns no-sync for unknown workflow names', () => {
94
+ assert.deepStrictEqual(getCadence('unknown-command'), { pull: false, push: false });
95
+ });
96
+
97
+ it('returns no-sync for empty string', () => {
98
+ assert.deepStrictEqual(getCadence(''), { pull: false, push: false });
99
+ });
100
+
101
+ it('returns no-sync for undefined', () => {
102
+ assert.deepStrictEqual(getCadence(undefined), { pull: false, push: false });
103
+ });
104
+ });
105
+
106
+ describe('getCadence — group totals', () => {
107
+ it('all entries sum to 70 (35 + 17 + 4 + 14)', () => {
108
+ const entries = Object.entries(CADENCE_TABLE);
109
+ const pullPush = entries.filter(([, v]) => v.pull && v.push).length;
110
+ const pushOnly = entries.filter(([, v]) => !v.pull && v.push).length;
111
+ const pullOnly = entries.filter(([, v]) => v.pull && !v.push).length;
112
+ const noSync = entries.filter(([, v]) => !v.pull && !v.push).length;
113
+
114
+ assert.strictEqual(pullPush, 35, '35 pull+push');
115
+ assert.strictEqual(pushOnly, 17, '17 push-only');
116
+ assert.strictEqual(pullOnly, 4, '4 pull-only');
117
+ assert.strictEqual(noSync, 14, '14 no-sync');
118
+ assert.strictEqual(pullPush + pushOnly + pullOnly + noSync, 70, 'total = 70');
119
+ });
120
+ });
121
+
122
+ // ─── Init Integration Tests ─────────────────────────────────────────────────
123
+
124
+ describe('Init cadence output (integration)', () => {
125
+ // Only run integration tests if we are in a DGS product directory
126
+ const hasPlanningDir = fs.existsSync(path.join(process.cwd(), '.planning'));
127
+ const hasConfig = fs.existsSync(path.join(process.cwd(), '.planning', 'dgs.config.json'));
128
+
129
+ if (!hasPlanningDir && !hasConfig) {
130
+ it('skipped — not running in a DGS product directory', () => {
131
+ // Informational skip
132
+ });
133
+ return;
134
+ }
135
+
136
+ const { execSync } = require('child_process');
137
+ const toolsPath = path.join(__dirname, '..', 'bin', 'dgs-tools.cjs');
138
+
139
+ it('execute-phase init returns cadence_pull:true, cadence_push:true', () => {
140
+ const raw = execSync(`node ${toolsPath} init execute-phase 114 --raw`, {
141
+ cwd: process.cwd(),
142
+ encoding: 'utf-8',
143
+ stdio: ['pipe', 'pipe', 'pipe'],
144
+ });
145
+ const data = JSON.parse(raw.trim());
146
+ assert.strictEqual(data.cadence_pull, true);
147
+ assert.strictEqual(data.cadence_push, true);
148
+ });
149
+
150
+ it('progress init returns cadence_pull:true, cadence_push:false', () => {
151
+ const raw = execSync(`node ${toolsPath} init progress --raw`, {
152
+ cwd: process.cwd(),
153
+ encoding: 'utf-8',
154
+ stdio: ['pipe', 'pipe', 'pipe'],
155
+ });
156
+ const data = JSON.parse(raw.trim());
157
+ assert.strictEqual(data.cadence_pull, true);
158
+ assert.strictEqual(data.cadence_push, false);
159
+ });
160
+ });