wogiflow 2.33.0 → 2.34.2
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/.workflow/templates/partials/methodology-rules.hbs +3 -1
- package/lib/scheduled-mode.js +12 -15
- package/lib/skill-export-claude-plugin.js +41 -1
- package/lib/skill-portability.js +21 -3
- package/lib/workspace-channel-server.js +116 -3
- package/lib/workspace-channel-tracking.js +102 -1
- package/lib/workspace-dispatch-tracking.js +28 -0
- package/lib/workspace-messages.js +32 -4
- package/lib/workspace-subtask-state.js +215 -0
- package/lib/workspace.js +81 -0
- package/package.json +2 -2
- package/scripts/flow +17 -0
- package/scripts/flow-constants.js +3 -1
- package/scripts/flow-io.js +17 -0
- package/scripts/flow-paths.js +81 -0
- package/scripts/flow-schedule.js +23 -6
- package/scripts/flow-scheduled-runner.js +53 -8
- package/scripts/flow-standards-checker.js +37 -0
- package/scripts/flow-utils.js +2 -0
- package/scripts/hooks/adapters/claude-code.js +6 -2
- package/scripts/hooks/core/git-safety-gate.js +34 -15
- package/scripts/hooks/core/long-input-enforcement.js +49 -39
- package/scripts/hooks/core/overdue-dispatches.js +28 -6
- package/scripts/hooks/core/phase-gate.js +34 -5
- package/scripts/hooks/core/phase-read-gate.js +62 -10
- package/scripts/hooks/core/session-start-worker.js +52 -0
- package/scripts/hooks/core/stop-orchestrator.js +17 -2
- package/scripts/hooks/core/validation.js +8 -0
- package/scripts/hooks/core/worker-continuation-gate.js +487 -0
- package/scripts/hooks/core/workspace-stop-gates.js +21 -0
- package/scripts/hooks/core/workspace-stop-notify.js +174 -59
- package/scripts/hooks/entry/claude-code/post-tool-use.js +26 -0
- package/.claude/rules/README.md +0 -36
- package/.claude/rules/_internal/README.md +0 -64
- package/.claude/rules/_internal/document-structure.md +0 -77
- package/.claude/rules/_internal/dual-repo-management.md +0 -174
- package/.claude/rules/_internal/feature-refactoring-cleanup.md +0 -87
- package/.claude/rules/_internal/github-releases.md +0 -71
- package/.claude/rules/_internal/model-management.md +0 -35
- package/.claude/rules/_internal/self-maintenance.md +0 -87
- package/.claude/rules/_internal/worker-tool-first-turn.md +0 -82
- package/.claude/rules/alternative-execpolicy-toml-command-policy.md +0 -11
- package/.claude/rules/alternative-hand-edit-ready-json-to-register-orpha.md +0 -11
- package/.claude/rules/alternative-hook-args-exec-form.md +0 -6
- package/.claude/rules/alternative-permission-ruleset-per-phase.md +0 -11
- package/.claude/rules/alternative-short-name.md +0 -12
- package/.claude/rules/alternative-wogi-flow-as-mcp-client-oauth-manager.md +0 -11
- package/.claude/rules/architecture/component-reuse.md +0 -38
- package/.claude/rules/architecture/hook-three-layer.md +0 -68
- package/.claude/rules/code-style/naming-conventions.md +0 -107
- package/.claude/rules/dual-repo-architecture-2026-02-28.md +0 -18
- package/.claude/rules/github-release-workflow-2026-01-30.md +0 -16
- package/.claude/rules/operations/git-workflows.md +0 -92
- package/.claude/rules/operations/scratch-directory.md +0 -54
- package/.claude/skills/figma-analyzer/knowledge/learnings.md +0 -11
- package/.workflow/specs/architecture.md.template +0 -24
- package/.workflow/specs/stack.md.template +0 -33
- package/.workflow/specs/testing.md.template +0 -36
|
@@ -1,76 +1,191 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Workspace worker
|
|
4
|
+
* Workspace worker signal emission (epic-workspace-sustained-exec / S3, wf-d3ae1717;
|
|
5
|
+
* originally wf-6e31850e A-3 / wf-d3e67abe).
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Two distinct signals so the manager never mistakes a pause for a stop:
|
|
8
|
+
*
|
|
9
|
+
* notifyWorkerProgress() — a HEARTBEAT, emitted when the continuation gate (S2)
|
|
10
|
+
* forces the worker to keep going. "Work ongoing," NOT a stop. Carries the
|
|
11
|
+
* current sub-task progress + git HEAD so the manager can refresh the
|
|
12
|
+
* dispatch deadline instead of polling git.
|
|
13
|
+
*
|
|
14
|
+
* notifyWorkerTerminal() — emitted ONLY on a genuine stop (after all gates
|
|
15
|
+
* decline to continue). Picks a precise terminal type:
|
|
16
|
+
* worker-awaiting-approval — in-progress task sitting in spec_review/exploring
|
|
17
|
+
* (waiting on the manager's GO — NOT done)
|
|
18
|
+
* worker-stopped — in-progress, active phase, but stopping anyway
|
|
19
|
+
* (legacy mid-work stop)
|
|
20
|
+
* worker-idle — nothing in progress and nothing queued
|
|
21
|
+
*
|
|
22
|
+
* The old code emitted `worker-stopped` UNCONDITIONALLY at the top of the Stop
|
|
23
|
+
* sequence — before the gates ran — so the manager saw "stopped mid-work" on
|
|
24
|
+
* every turn boundary, even when the worker was about to be forced to continue.
|
|
25
|
+
* That ordering bug is fixed: emission now happens at the decision point.
|
|
26
|
+
*
|
|
27
|
+
* All emission is best-effort and fail-open; never throws into the Stop path.
|
|
9
28
|
*/
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
|
|
30
|
+
const VALID_NAME = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
31
|
+
const AWAITING_PHASES = new Set(['spec_review', 'exploring', 'routing', 'idle']);
|
|
32
|
+
|
|
33
|
+
function isWorker() {
|
|
34
|
+
return Boolean(process.env.WOGI_REPO_NAME &&
|
|
35
|
+
process.env.WOGI_REPO_NAME !== 'manager' &&
|
|
36
|
+
VALID_NAME.test(process.env.WOGI_REPO_NAME) &&
|
|
37
|
+
process.env.WOGI_WORKSPACE_ROOT);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readState() {
|
|
41
|
+
const nodePath = require('node:path');
|
|
42
|
+
const { PATHS, safeJsonParse } = require('../../flow-utils');
|
|
43
|
+
const ready = safeJsonParse(nodePath.join(PATHS.state, 'ready.json'), {});
|
|
44
|
+
const phaseData = safeJsonParse(nodePath.join(PATHS.state, 'workflow-phase.json'), {});
|
|
45
|
+
const inProgressTask = (ready.inProgress || [])[0] || null;
|
|
46
|
+
const queued = (ready.ready || []).length;
|
|
47
|
+
let lastSha = null;
|
|
13
48
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
49
|
+
lastSha = require('node:child_process').execSync('git rev-parse --short HEAD 2>/dev/null || true', {
|
|
50
|
+
cwd: PATHS.root, encoding: 'utf-8', timeout: 2000
|
|
51
|
+
}).trim() || null;
|
|
52
|
+
} catch (_err) { /* non-critical */ }
|
|
53
|
+
return { inProgressTask, queued, phase: phaseData?.phase || null, lastSha };
|
|
54
|
+
}
|
|
19
55
|
|
|
20
|
-
|
|
21
|
-
|
|
56
|
+
function emit(type, fields) {
|
|
57
|
+
const nodePath = require('node:path');
|
|
58
|
+
const workspaceRoot = process.env.WOGI_WORKSPACE_ROOT;
|
|
59
|
+
const repoName = process.env.WOGI_REPO_NAME;
|
|
60
|
+
const libMessages = nodePath.resolve(__dirname, '..', '..', '..', 'lib', 'workspace-messages');
|
|
61
|
+
const { createMessage, saveMessage } = require(libMessages);
|
|
62
|
+
const msg = createMessage({
|
|
63
|
+
from: repoName,
|
|
64
|
+
to: 'manager',
|
|
65
|
+
type,
|
|
66
|
+
subject: fields.subject,
|
|
67
|
+
body: fields.body,
|
|
68
|
+
priority: fields.priority || 'medium',
|
|
69
|
+
actionRequired: Boolean(fields.actionRequired)
|
|
70
|
+
});
|
|
71
|
+
Object.assign(msg, fields.extra || {});
|
|
72
|
+
saveMessage(workspaceRoot, msg);
|
|
22
73
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
74
|
+
// Real-time nudge to the manager port (best-effort) so it doesn't have to wait
|
|
75
|
+
// for its own next prompt to read the bus.
|
|
76
|
+
try {
|
|
77
|
+
const managerPort = process.env.WOGI_MANAGER_PORT;
|
|
78
|
+
if (managerPort) {
|
|
79
|
+
const http = require('node:http');
|
|
80
|
+
const payload = `[${type}] ${fields.subject}`;
|
|
81
|
+
const buf = Buffer.from(payload, 'utf-8');
|
|
82
|
+
const req = http.request({
|
|
83
|
+
hostname: '127.0.0.1', port: parseInt(managerPort, 10), path: '/', method: 'POST',
|
|
84
|
+
headers: { 'Content-Type': 'text/plain', 'Content-Length': buf.byteLength, 'X-Wogi-From': repoName }
|
|
85
|
+
});
|
|
86
|
+
req.on('error', () => {});
|
|
87
|
+
req.write(buf); req.end();
|
|
88
|
+
}
|
|
89
|
+
} catch (_err) { /* best effort */ }
|
|
90
|
+
}
|
|
28
91
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Heartbeat — the continuation gate forced another turn. NOT a stop.
|
|
94
|
+
* @param {Object} info { taskId, remaining, total, attempt }
|
|
95
|
+
*/
|
|
96
|
+
async function notifyWorkerProgress(info = {}) {
|
|
97
|
+
if (!isWorker()) return;
|
|
98
|
+
try {
|
|
99
|
+
const { lastSha } = readState();
|
|
100
|
+
emit('worker-progress', {
|
|
101
|
+
subject: `Worker ${process.env.WOGI_REPO_NAME} working on ${info.taskId || 'task'} (${info.remaining ?? '?'} sub-task(s) left)`,
|
|
102
|
+
body: [
|
|
103
|
+
`Worker "${process.env.WOGI_REPO_NAME}" is continuing (heartbeat).`,
|
|
104
|
+
info.taskId ? `Task: ${info.taskId}` : null,
|
|
105
|
+
(info.total != null) ? `Sub-tasks: ${(info.total - (info.remaining ?? 0))}/${info.total} done, ${info.remaining ?? '?'} remaining` : null,
|
|
106
|
+
info.attempt != null ? `Continuation: ${info.attempt}` : null,
|
|
107
|
+
lastSha ? `HEAD: ${lastSha}` : null
|
|
108
|
+
].filter(Boolean).join('\n'),
|
|
109
|
+
priority: 'low',
|
|
110
|
+
actionRequired: false,
|
|
111
|
+
extra: {
|
|
112
|
+
reason: 'continuation',
|
|
113
|
+
state: 'in-progress',
|
|
114
|
+
taskId: info.taskId || null,
|
|
115
|
+
taskInProgress: info.taskId || null,
|
|
116
|
+
remaining: info.remaining ?? null,
|
|
117
|
+
total: info.total ?? null,
|
|
118
|
+
continuation: info.attempt ?? null,
|
|
119
|
+
lastSha,
|
|
120
|
+
heartbeatAt: new Date().toISOString()
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (process.env.DEBUG) console.error(`[Stop] worker-progress emit failed: ${err.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
32
127
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Terminal — emitted at a genuine stop. Picks the precise terminal type.
|
|
130
|
+
*/
|
|
131
|
+
async function notifyWorkerTerminal() {
|
|
132
|
+
if (!isWorker()) return;
|
|
133
|
+
try {
|
|
134
|
+
const { inProgressTask, queued, phase, lastSha } = readState();
|
|
135
|
+
const taskId = inProgressTask?.id || null;
|
|
41
136
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
msg.taskId = taskInProgress;
|
|
63
|
-
msg.reason = 'graceful';
|
|
64
|
-
msg.state = state;
|
|
65
|
-
msg.taskInProgress = taskInProgress;
|
|
66
|
-
msg.lastSha = lastSha;
|
|
67
|
-
saveMessage(workspaceRoot, msg);
|
|
68
|
-
} catch (err) {
|
|
69
|
-
if (process.env.DEBUG) console.error(`[Stop] Workspace message write failed: ${err.message}`);
|
|
137
|
+
let type, subject, priority, actionRequired, state;
|
|
138
|
+
if (!inProgressTask) {
|
|
139
|
+
type = 'worker-idle';
|
|
140
|
+
state = 'idle';
|
|
141
|
+
subject = `Worker ${process.env.WOGI_REPO_NAME} idle (${queued} queued)`;
|
|
142
|
+
priority = 'medium';
|
|
143
|
+
actionRequired = queued > 0; // queued-but-not-started is worth the manager's attention
|
|
144
|
+
} else if (AWAITING_PHASES.has(phase)) {
|
|
145
|
+
type = 'worker-awaiting-approval';
|
|
146
|
+
state = 'awaiting-approval';
|
|
147
|
+
subject = `Worker ${process.env.WOGI_REPO_NAME} awaiting approval on ${taskId} (phase: ${phase})`;
|
|
148
|
+
priority = 'high';
|
|
149
|
+
actionRequired = true;
|
|
150
|
+
} else {
|
|
151
|
+
type = 'worker-stopped';
|
|
152
|
+
state = 'mid-work';
|
|
153
|
+
subject = `Worker stopped mid-work on ${taskId}`;
|
|
154
|
+
priority = 'high';
|
|
155
|
+
actionRequired = true;
|
|
70
156
|
}
|
|
157
|
+
|
|
158
|
+
emit(type, {
|
|
159
|
+
subject,
|
|
160
|
+
body: [
|
|
161
|
+
`Worker "${process.env.WOGI_REPO_NAME}" stopped.`,
|
|
162
|
+
`State: ${state}`,
|
|
163
|
+
taskId ? `Task in progress: ${taskId}` : `Queued: ${queued}`,
|
|
164
|
+
phase ? `Phase: ${phase}` : null,
|
|
165
|
+
lastSha ? `Last commit: ${lastSha}` : null
|
|
166
|
+
].filter(Boolean).join('\n'),
|
|
167
|
+
priority,
|
|
168
|
+
actionRequired,
|
|
169
|
+
extra: {
|
|
170
|
+
reason: 'graceful',
|
|
171
|
+
state,
|
|
172
|
+
taskId,
|
|
173
|
+
taskInProgress: taskId,
|
|
174
|
+
phase,
|
|
175
|
+
lastSha
|
|
176
|
+
}
|
|
177
|
+
});
|
|
71
178
|
} catch (err) {
|
|
72
|
-
if (process.env.DEBUG) console.error(`[Stop]
|
|
179
|
+
if (process.env.DEBUG) console.error(`[Stop] worker terminal emit failed: ${err.message}`);
|
|
73
180
|
}
|
|
74
181
|
}
|
|
75
182
|
|
|
76
|
-
module.exports = {
|
|
183
|
+
module.exports = {
|
|
184
|
+
notifyWorkerProgress,
|
|
185
|
+
notifyWorkerTerminal,
|
|
186
|
+
isWorker,
|
|
187
|
+
AWAITING_PHASES,
|
|
188
|
+
// Back-compat alias: the old single unconditional emitter. Now routes to the
|
|
189
|
+
// typed terminal notifier (callers that want a terminal stop signal).
|
|
190
|
+
notifyWorkerStopped: notifyWorkerTerminal
|
|
191
|
+
};
|
|
@@ -57,6 +57,32 @@ runHook('PostToolUse', async ({ parsedInput }) => {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// S1 (wf-e72350bf): mirror the worker's TodoWrite decomposition to the durable
|
|
61
|
+
// sub-task ledger so the continuation gate (S2) and restart-resume (S5) have a
|
|
62
|
+
// disk-readable "work remaining" signal. Worker-mode only by default (solo
|
|
63
|
+
// sessions opt in via workspace.subtaskLedger.alsoInSolo). Fail-open.
|
|
64
|
+
if (toolName === 'TodoWrite' && !toolFailed) {
|
|
65
|
+
try {
|
|
66
|
+
const { getConfig, PATHS, safeJsonParse } = require('../../../flow-utils');
|
|
67
|
+
const cfg = (() => { try { return getConfig()?.workspace?.subtaskLedger || {}; } catch (_err) { return {}; } })();
|
|
68
|
+
const ledgerEnabled = cfg.enabled !== false;
|
|
69
|
+
const isWorker = process.env.WOGI_WORKSPACE_ROOT &&
|
|
70
|
+
process.env.WOGI_REPO_NAME &&
|
|
71
|
+
process.env.WOGI_REPO_NAME !== 'manager';
|
|
72
|
+
if (ledgerEnabled && (isWorker || cfg.alsoInSolo === true)) {
|
|
73
|
+
const ready = safeJsonParse(require('node:path').join(PATHS.state, 'ready.json'), { inProgress: [] });
|
|
74
|
+
const taskId = ready.inProgress?.[0]?.id || null;
|
|
75
|
+
if (taskId) {
|
|
76
|
+
const subtaskState = require('../../../../lib/workspace-subtask-state');
|
|
77
|
+
const subs = subtaskState.subtasksFromTodos(toolInput);
|
|
78
|
+
if (subs.length > 0) subtaskState.write(taskId, subs);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (process.env.DEBUG) console.error(`[post-tool-use] subtask-ledger mirror: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
// Track B B3 fix (2026-04-13): mark when templates are edited, so
|
|
61
87
|
// session-end and the next /wogi-start can remind to run flow bridge sync.
|
|
62
88
|
// Mechanical enforcement of self-maintenance.md §1.
|
package/.claude/rules/README.md
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Auto-Generated Rules
|
|
2
|
-
|
|
3
|
-
This directory is auto-generated from `.workflow/state/decisions.md`.
|
|
4
|
-
|
|
5
|
-
**DO NOT EDIT THESE FILES DIRECTLY.**
|
|
6
|
-
|
|
7
|
-
Edit `decisions.md` instead, then run:
|
|
8
|
-
```bash
|
|
9
|
-
node scripts/flow-rules-sync.js
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Or rules will auto-sync when decisions.md is updated.
|
|
13
|
-
|
|
14
|
-
## How It Works
|
|
15
|
-
|
|
16
|
-
- Each section in decisions.md becomes a separate rule file
|
|
17
|
-
- Rules are path-scoped based on section keywords (e.g., "component" rules only load for component files)
|
|
18
|
-
- Claude Code automatically loads these rules for context-aware guidance
|
|
19
|
-
|
|
20
|
-
## Rule Types
|
|
21
|
-
|
|
22
|
-
Rules have `alwaysApply` frontmatter that determines loading behavior:
|
|
23
|
-
|
|
24
|
-
- **`alwaysApply: true`** - Always loaded (rules with: general, always, project, naming, coding, standard, convention, must, never, critical, security in title)
|
|
25
|
-
- **`alwaysApply: false`** - Agent-requested: Claude decides whether to load based on description relevance to current task
|
|
26
|
-
|
|
27
|
-
This saves tokens by not loading React rules when working on backend code, etc.
|
|
28
|
-
|
|
29
|
-
## Files
|
|
30
|
-
|
|
31
|
-
- dual-repo-architecture-2026-02-28.md
|
|
32
|
-
- github-release-workflow-2026-01-30.md
|
|
33
|
-
- alternative-short-name.md
|
|
34
|
-
- alternative-hand-edit-ready-json-to-register-orpha.md
|
|
35
|
-
|
|
36
|
-
Last synced: 2026-04-24T06:42:44.238Z
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: false
|
|
3
|
-
description: "Meta-documentation about how project rules are organized"
|
|
4
|
-
---
|
|
5
|
-
# Project Rules
|
|
6
|
-
|
|
7
|
-
This directory contains coding rules and patterns for this project, organized by category.
|
|
8
|
-
|
|
9
|
-
## Structure
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
.claude/rules/
|
|
13
|
-
├── code-style/ # Naming conventions, formatting
|
|
14
|
-
│ └── naming-conventions.md
|
|
15
|
-
├── security/ # Security patterns and practices
|
|
16
|
-
│ └── security-patterns.md
|
|
17
|
-
├── architecture/ # Design decisions and patterns
|
|
18
|
-
│ ├── component-reuse.md
|
|
19
|
-
│ └── model-management.md
|
|
20
|
-
└── README.md
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## How Rules Work
|
|
24
|
-
|
|
25
|
-
Rules are automatically loaded by Claude Code based on:
|
|
26
|
-
- **alwaysApply: true** - Rule is always loaded
|
|
27
|
-
- **alwaysApply: false** - Rule is loaded based on `globs` or `description` relevance
|
|
28
|
-
- **globs** - File patterns that trigger rule loading
|
|
29
|
-
|
|
30
|
-
## Adding New Rules
|
|
31
|
-
|
|
32
|
-
1. Choose the appropriate category subdirectory
|
|
33
|
-
2. Create a `.md` file with frontmatter:
|
|
34
|
-
|
|
35
|
-
```yaml
|
|
36
|
-
---
|
|
37
|
-
alwaysApply: false
|
|
38
|
-
description: "Brief description for relevance matching"
|
|
39
|
-
globs: src/**/*.ts # Optional: only load for these files
|
|
40
|
-
---
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
3. Write the rule content in markdown
|
|
44
|
-
|
|
45
|
-
## Categories
|
|
46
|
-
|
|
47
|
-
| Category | Purpose |
|
|
48
|
-
|----------|---------|
|
|
49
|
-
| code-style | Naming conventions, formatting, file structure |
|
|
50
|
-
| security | Security patterns, input validation, safe practices |
|
|
51
|
-
| architecture | Design decisions, component patterns, system organization |
|
|
52
|
-
|
|
53
|
-
## Auto-Generation
|
|
54
|
-
|
|
55
|
-
Some rules can be auto-generated from `.workflow/state/decisions.md`:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
node scripts/flow-rules-sync.js
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
The sync script will route rules to appropriate category subdirectories.
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
Last updated: 2026-01-12
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: false
|
|
3
|
-
description: "All AI-context documents must use PIN markers for targeted context loading"
|
|
4
|
-
globs: ".workflow/**/*.md"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Document Structure for AI Context
|
|
8
|
-
|
|
9
|
-
All documents in `.workflow/` that are used as AI context MUST follow the PIN standard.
|
|
10
|
-
|
|
11
|
-
## Required Structure
|
|
12
|
-
|
|
13
|
-
### 1. Header with PIN List
|
|
14
|
-
Every document starts with a comment listing all pins in the document:
|
|
15
|
-
```markdown
|
|
16
|
-
<!-- PINS: pin1, pin2, pin3 -->
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### 2. Section PIN Markers
|
|
20
|
-
Each major section has a PIN marker comment:
|
|
21
|
-
```markdown
|
|
22
|
-
### Section Title
|
|
23
|
-
<!-- PIN: section-specific-pin -->
|
|
24
|
-
[Content]
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### 3. PIN Naming Convention
|
|
28
|
-
- Use kebab-case: `user-authentication`, not `userAuthentication`
|
|
29
|
-
- Use semantic names: `error-handling`, not `eh`
|
|
30
|
-
- Use compound names for specificity: `json-parse-safety`
|
|
31
|
-
|
|
32
|
-
## Why PINs Matter
|
|
33
|
-
|
|
34
|
-
The PIN system enables:
|
|
35
|
-
1. **Targeted context loading**: Only load sections relevant to current task
|
|
36
|
-
2. **Cheaper model routing**: Haiku can fetch only relevant sections for Opus
|
|
37
|
-
3. **Change detection**: Hash sections independently for smart invalidation
|
|
38
|
-
4. **Cross-reference**: Link sections by PIN across documents
|
|
39
|
-
|
|
40
|
-
## Example Document
|
|
41
|
-
|
|
42
|
-
```markdown
|
|
43
|
-
# Config Reference
|
|
44
|
-
|
|
45
|
-
<!-- PINS: database, authentication, api-keys, environment -->
|
|
46
|
-
|
|
47
|
-
## Database Settings
|
|
48
|
-
<!-- PIN: database -->
|
|
49
|
-
| Setting | Default | Description |
|
|
50
|
-
|---------|---------|-------------|
|
|
51
|
-
|
|
52
|
-
## Authentication
|
|
53
|
-
<!-- PIN: authentication -->
|
|
54
|
-
| Setting | Default | Description |
|
|
55
|
-
|---------|---------|-------------|
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Parsing
|
|
59
|
-
|
|
60
|
-
The PIN system automatically parses documents with:
|
|
61
|
-
- `flow-section-index.js` - Generates section index with pins
|
|
62
|
-
- `flow-section-resolver.js` - Resolves sections by PIN lookup
|
|
63
|
-
- `getSectionsByPins(['auth', 'security'])` - Fetch only relevant sections
|
|
64
|
-
|
|
65
|
-
## Files That Must Have PINs
|
|
66
|
-
|
|
67
|
-
| File | Required PINs |
|
|
68
|
-
|------|---------------|
|
|
69
|
-
| `decisions.md` | Per coding rule/pattern |
|
|
70
|
-
| `app-map.md` | Per component/screen |
|
|
71
|
-
| `product.md` | Per product section |
|
|
72
|
-
| `stack.md` | Per technology |
|
|
73
|
-
|
|
74
|
-
## Validation
|
|
75
|
-
|
|
76
|
-
Run `node scripts/flow-section-index.js --force` to regenerate the index.
|
|
77
|
-
Check `.workflow/state/section-index.json` for indexed sections and their pins.
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: false
|
|
3
|
-
description: "Dual-repo management rules for wogi-flow and wogiflow-cloud"
|
|
4
|
-
globs: "package.json,lib/installer.js,lib/extension-points.js"
|
|
5
|
-
---
|
|
6
|
-
# Dual-Repo Management: wogi-flow + wogiflow-cloud
|
|
7
|
-
|
|
8
|
-
**Added**: 2026-02-28
|
|
9
|
-
**Source**: User directive — formalize dual-repo architecture decisions
|
|
10
|
-
|
|
11
|
-
## Repository Ownership
|
|
12
|
-
|
|
13
|
-
| Repo | Package | Visibility | Purpose |
|
|
14
|
-
|------|---------|-----------|---------|
|
|
15
|
-
| `wogi-flow` | `wogiflow` (npm) | Public (AGPL-3.0) | Free CLI, workflow engine, all local-only features |
|
|
16
|
-
| `wogiflow-cloud` | `@wogiflow/teams` (client), `wogiflow-cloud-server` (server) | Private | Teams backend, client hooks, dashboard, portal logic |
|
|
17
|
-
| `wogiflow-portal` | N/A (static site) | Public | wogi.ai — landing page, login, signup, knowledge base |
|
|
18
|
-
|
|
19
|
-
## Hard Rule: No Teams Code in the Free Repo
|
|
20
|
-
|
|
21
|
-
Team-specific logic MUST NEVER appear in `wogi-flow`. This includes:
|
|
22
|
-
- Auth/login UI beyond the thin `wogi login`/`wogi logout` adapter
|
|
23
|
-
- Sync engines, presence, real-time features
|
|
24
|
-
- Team CRUD, roles, permissions
|
|
25
|
-
- Dashboard pages
|
|
26
|
-
- Server-side API routes
|
|
27
|
-
|
|
28
|
-
The free repo provides **extension points** (hooks in `lib/extension-points.js`) that the cloud client plugs into. The adapter pattern is:
|
|
29
|
-
- `wogi-flow` exports hook interfaces and config schema
|
|
30
|
-
- `@wogiflow/teams` imports those interfaces and adds team behavior
|
|
31
|
-
- All team logic executes from `@wogiflow/teams`, never from `wogiflow` core
|
|
32
|
-
|
|
33
|
-
## Version Management
|
|
34
|
-
|
|
35
|
-
### Independent Versions, Mutual Awareness
|
|
36
|
-
|
|
37
|
-
Each repo has its own semver version. They are NOT locked together.
|
|
38
|
-
|
|
39
|
-
- `wogi-flow` → `package.json` version (currently 1.6.0)
|
|
40
|
-
- `wogiflow-cloud` server → `packages/server/package.json` version (currently 0.1.0)
|
|
41
|
-
- `@wogiflow/teams` client → `packages/client/package.json` version (currently 0.1.0)
|
|
42
|
-
|
|
43
|
-
### Cross-Repo Version File
|
|
44
|
-
|
|
45
|
-
Each repo maintains a `.workflow/state/partner-versions.json` that records the last-known version of the other repo:
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
// In wogi-flow:
|
|
49
|
-
{
|
|
50
|
-
"self": { "package": "wogiflow", "version": "1.6.0" },
|
|
51
|
-
"partners": {
|
|
52
|
-
"wogiflow-cloud-server": { "version": "0.1.0", "checkedAt": "2026-02-28" },
|
|
53
|
-
"wogiflow-teams-client": { "version": "0.1.0", "minCompatible": "1.5.0", "checkedAt": "2026-02-28" }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// In wogiflow-cloud:
|
|
58
|
-
{
|
|
59
|
-
"self": { "package": "wogiflow-cloud", "version": "0.1.0" },
|
|
60
|
-
"partners": {
|
|
61
|
-
"wogiflow": { "version": "1.6.0", "checkedAt": "2026-02-28" }
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Update rule**: When releasing either repo, update `partner-versions.json` in BOTH repos.
|
|
67
|
-
|
|
68
|
-
### Compatibility Contract
|
|
69
|
-
|
|
70
|
-
The `@wogiflow/teams` client declares its minimum compatible `wogiflow` version via peerDependencies:
|
|
71
|
-
|
|
72
|
-
```json
|
|
73
|
-
"peerDependencies": {
|
|
74
|
-
"wogiflow": ">=1.5.0"
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**When to bump the minimum**:
|
|
79
|
-
- When the free repo removes or renames an exported function that the client uses
|
|
80
|
-
- When the free repo changes the shape of config.json, ready.json, or other state files
|
|
81
|
-
- When the free repo changes hook interfaces (entry point signatures, event payloads)
|
|
82
|
-
|
|
83
|
-
**When NOT to bump**:
|
|
84
|
-
- New features added to the free repo (additive changes are always compatible)
|
|
85
|
-
- Internal refactoring that doesn't change exports
|
|
86
|
-
|
|
87
|
-
## Interface Contract (Public API Surface)
|
|
88
|
-
|
|
89
|
-
The cloud client depends on these interfaces from `wogi-flow`. Changes to any of these require updating the client:
|
|
90
|
-
|
|
91
|
-
### Exported Functions (from scripts/)
|
|
92
|
-
- `flow-utils.js`: `getConfig()`, `safeJsonParse()`, `writeJson()`, `generateTaskId()`, `validateTaskId()`, `PATHS`, `getReadyData()`, `saveReadyData()`
|
|
93
|
-
- `flow-session-state.js`: `trackTaskStart()`, `trackBypassAttempt()`
|
|
94
|
-
- `flow-memory-blocks.js`: `setCurrentTask()`
|
|
95
|
-
|
|
96
|
-
### Hook Interfaces (entry point contracts)
|
|
97
|
-
- `PreToolUse` hooks receive: `{ tool, toolInput }` via stdin JSON
|
|
98
|
-
- `PostToolUse` hooks receive: `{ tool, toolInput, toolResult }` via stdin JSON
|
|
99
|
-
- `TaskCompleted` hooks receive: `{ taskId }` via stdin JSON
|
|
100
|
-
- `SessionStart`/`SessionEnd` hooks receive: `{}` via stdin JSON
|
|
101
|
-
|
|
102
|
-
### State File Formats
|
|
103
|
-
- `ready.json`: `{ inProgress: [], ready: [], blocked: [], recentlyCompleted: [], backlog: [] }`
|
|
104
|
-
- `config.json`: Schema documented in `config.schema.json`
|
|
105
|
-
- `decisions.md`: Markdown with `## Section` / `### Rule` structure
|
|
106
|
-
- `session-state.json`: `{ taskId, status, lastBriefingAt, ... }`
|
|
107
|
-
|
|
108
|
-
### Config Keys Used by Cloud
|
|
109
|
-
- `hooks.rules.*` — all hook toggle keys
|
|
110
|
-
- `enforcement.*` — strict mode, task gating
|
|
111
|
-
- `semanticMatching.*` — reuse detection thresholds
|
|
112
|
-
|
|
113
|
-
**When modifying any of the above**: Check wogiflow-cloud for consumers BEFORE releasing.
|
|
114
|
-
|
|
115
|
-
## Change Propagation Rules
|
|
116
|
-
|
|
117
|
-
### OSS Change → Does Cloud Need Updating?
|
|
118
|
-
|
|
119
|
-
| Change Type | Cloud Impact | Action Required |
|
|
120
|
-
|-------------|-------------|-----------------|
|
|
121
|
-
| New feature (additive) | None | No action needed |
|
|
122
|
-
| Bug fix (internal) | None | No action needed |
|
|
123
|
-
| Exported function renamed/removed | BREAKING | Update client, bump peerDep minimum |
|
|
124
|
-
| State file format changed | BREAKING | Update client parsers |
|
|
125
|
-
| Hook interface changed | BREAKING | Update client hooks |
|
|
126
|
-
| Config key renamed/removed | BREAKING | Update client config readers |
|
|
127
|
-
| New config key added | None (additive) | Client can optionally use it |
|
|
128
|
-
|
|
129
|
-
### Cloud Change → Does OSS Need Updating?
|
|
130
|
-
|
|
131
|
-
| Change Type | OSS Impact | Action Required |
|
|
132
|
-
|-------------|-----------|-----------------|
|
|
133
|
-
| New server feature | None | No action needed |
|
|
134
|
-
| New client hook | None | No action needed |
|
|
135
|
-
| Client needs new OSS export | REQUIRES | Add export to OSS, release OSS first |
|
|
136
|
-
| Dashboard changes | None | Entirely separate |
|
|
137
|
-
|
|
138
|
-
### Release Order
|
|
139
|
-
|
|
140
|
-
1. **OSS first**: If the cloud needs a new OSS feature, release OSS first
|
|
141
|
-
2. **Cloud follows**: Cloud releases independently, referencing the OSS version in peerDeps
|
|
142
|
-
3. **Never**: Release cloud with a dependency on an unreleased OSS version
|
|
143
|
-
|
|
144
|
-
```
|
|
145
|
-
OSS v1.7.0 (adds new export)
|
|
146
|
-
↓
|
|
147
|
-
Cloud client v0.2.0 (uses new export, peerDep bumped to >=1.7.0)
|
|
148
|
-
↓
|
|
149
|
-
Cloud server v0.2.0 (may or may not change)
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Web Properties
|
|
153
|
-
|
|
154
|
-
| Property | Repo | Purpose |
|
|
155
|
-
|----------|------|---------|
|
|
156
|
-
| wogi.ai (main site) | `wogiflow-portal` | Landing, login, signup, knowledge base |
|
|
157
|
-
| Dashboard (admin UI) | `wogiflow-cloud/packages/dashboard/` | Team admin — served by cloud server |
|
|
158
|
-
|
|
159
|
-
The portal is a separate deployment from the dashboard. The portal is public-facing (marketing + auth). The dashboard is authenticated (team management).
|
|
160
|
-
|
|
161
|
-
## Verification Checklist (Before Any Release)
|
|
162
|
-
|
|
163
|
-
### Releasing wogi-flow (OSS):
|
|
164
|
-
1. Check `partner-versions.json` — is cloud version current?
|
|
165
|
-
2. If any exported function/interface changed: grep wogiflow-cloud for consumers
|
|
166
|
-
3. Run `node --check` on all modified scripts
|
|
167
|
-
4. Follow GitHub Release Workflow (decisions.md)
|
|
168
|
-
5. After release: update `partner-versions.json` in wogiflow-cloud
|
|
169
|
-
|
|
170
|
-
### Releasing wogiflow-cloud:
|
|
171
|
-
1. Check `partner-versions.json` — is OSS version current?
|
|
172
|
-
2. Verify `peerDependencies.wogiflow` range includes current OSS version
|
|
173
|
-
3. If client needs new OSS export: release OSS first
|
|
174
|
-
4. After release: update `partner-versions.json` in wogi-flow
|