knoxis-helper 1.8.2 → 1.8.3
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/lib/knoxis-interactive-pair.js +4 -1
- package/lib/knoxis-pair-program.js +5 -1
- package/lib/templates/feature-kickoff.js +38 -1
- package/lib/templates/index.js +2 -0
- package/lib/templates/integrate.js +252 -0
- package/lib/templates/kickoff-schema.json +75 -0
- package/lib/templates/session-end.js +62 -0
- package/package.json +1 -1
|
@@ -379,7 +379,10 @@ async function runKitMode(kitMode, task, identity, scaffoldResult) {
|
|
|
379
379
|
workspace: identity.workspace,
|
|
380
380
|
userId: identity.userId,
|
|
381
381
|
workspaceId: identity.workspaceId,
|
|
382
|
-
parentTaskId: (identity.taskIds && identity.taskIds.length) ? identity.taskIds[0] : null
|
|
382
|
+
parentTaskId: (identity.taskIds && identity.taskIds.length) ? identity.taskIds[0] : null,
|
|
383
|
+
// Auto-doc opt-in flows through env var since the interactive runner
|
|
384
|
+
// sources its identity from the QIG portal rather than CLI args.
|
|
385
|
+
integrate: process.env.KNOXIS_INTEGRATE === 'true' || process.env.KNOXIS_INTEGRATE === '1'
|
|
383
386
|
});
|
|
384
387
|
} catch (e) {
|
|
385
388
|
console.error('Kit template error: ' + e.message);
|
|
@@ -804,7 +804,11 @@ async function run() {
|
|
|
804
804
|
pattern: recoveryPattern,
|
|
805
805
|
productSlug: args['product-slug'] || null,
|
|
806
806
|
projectSlug: args['project-slug'] || null,
|
|
807
|
-
workspace
|
|
807
|
+
workspace,
|
|
808
|
+
// Operator opt-in for auto-doc generation. When --integrate is passed
|
|
809
|
+
// alongside --mode feature-kickoff, the kickoff template appends a
|
|
810
|
+
// phase-integrate phase that the session-end ritual will run later.
|
|
811
|
+
integrate: Boolean(args.integrate)
|
|
808
812
|
});
|
|
809
813
|
scheduledSteps = [{
|
|
810
814
|
key: tpl.key,
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
// docs/features/<slug>/. Multiple features can coexist; each is independently
|
|
9
9
|
// trackable from the QIG dashboard via its manifest.json.
|
|
10
10
|
//
|
|
11
|
+
// Canonical structural constraints (slug pattern, phase counts, manifest shape)
|
|
12
|
+
// live in ./kickoff-schema.json and are also consumed by the server-side
|
|
13
|
+
// KickoffPrepService that turns meeting transcripts into kickoff-ready JSON.
|
|
14
|
+
// Keep this file's prose in sync with that schema if you change the numbers.
|
|
15
|
+
//
|
|
11
16
|
// State machine (similar shape to resume.js, autonomous-first):
|
|
12
17
|
// A: docs/features/<slug>/ missing → write the roadmap
|
|
13
18
|
// B: directory exists with unanswered _(fill in)_ entries → wait
|
|
@@ -93,6 +98,36 @@ touches. Steps must be small enough to ship in one pair-program session.
|
|
|
93
98
|
**Don't pad.** A 4-step feature is fine. A 30-step feature with 3 phases of
|
|
94
99
|
filler is not.
|
|
95
100
|
|
|
101
|
+
### Optional final phase: \`phase-integrate\` (operator opt-in)
|
|
102
|
+
|
|
103
|
+
If the session header above includes \`Integrate: yes\`, append one extra
|
|
104
|
+
phase as the **last phase** of the feature, with this exact shape (id is
|
|
105
|
+
load-bearing — the auto-doc generator looks for it):
|
|
106
|
+
|
|
107
|
+
\`\`\`json
|
|
108
|
+
{
|
|
109
|
+
"id": "phase-integrate",
|
|
110
|
+
"title": "Integrate (auto-doc)",
|
|
111
|
+
"goal": "Generate the public-contract API doc and register it for the chatbot",
|
|
112
|
+
"done_when": "docs/features/<slug>/PUBLIC_API.md exists and the integration-docs index has been notified",
|
|
113
|
+
"status": "pending",
|
|
114
|
+
"steps": [
|
|
115
|
+
{ "id": "phase-integrate-step-1", "action": "Generate PUBLIC_API.md from manifest + live OpenAPI", "touches": "docs/features/<slug>/PUBLIC_API.md", "status": "pending" },
|
|
116
|
+
{ "id": "phase-integrate-step-2", "action": "POST the doc to the backend integration-docs index for vectorization", "touches": "/api/public/integration-docs/index", "status": "pending" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
Do NOT include \`phase-integrate\` when the session header does not say
|
|
122
|
+
\`Integrate: yes\`. This is opt-in only — features that don't ship a public
|
|
123
|
+
contract should not have this phase, and adding it half-heartedly creates
|
|
124
|
+
half-baked docs in the vector store.
|
|
125
|
+
|
|
126
|
+
When the operator opts in, mirror the phase into ROADMAP.md (add it to the
|
|
127
|
+
phases list) and PHASES.md (with the two checkbox steps). The session-end
|
|
128
|
+
ritual auto-detects \`phase-integrate\` as the eligible-pending phase and runs
|
|
129
|
+
the doc generator without an extra command.
|
|
130
|
+
|
|
96
131
|
## Step 4 — Capture genuine open questions only
|
|
97
132
|
|
|
98
133
|
If something truly cannot be decided autonomously (missing API decision,
|
|
@@ -306,7 +341,8 @@ function buildFeatureKickoffPrompt({
|
|
|
306
341
|
workspace,
|
|
307
342
|
userId,
|
|
308
343
|
workspaceId,
|
|
309
|
-
parentTaskId
|
|
344
|
+
parentTaskId,
|
|
345
|
+
integrate
|
|
310
346
|
} = {}) {
|
|
311
347
|
const header = ['Mode: feature-kickoff'];
|
|
312
348
|
if (productSlug) header.push(`Product: ${productSlug}`);
|
|
@@ -315,6 +351,7 @@ function buildFeatureKickoffPrompt({
|
|
|
315
351
|
if (userId) header.push(`User ID: ${userId}`);
|
|
316
352
|
if (workspaceId) header.push(`Workspace ID: ${workspaceId}`);
|
|
317
353
|
if (parentTaskId) header.push(`Parent task ID: ${parentTaskId}`);
|
|
354
|
+
if (integrate) header.push('Integrate: yes');
|
|
318
355
|
if (taskDescription) header.push(`Feature description (use as-is for the title and summary):\n${taskDescription}`);
|
|
319
356
|
const ctx = `Session context:\n${header.join('\n')}\n\n`;
|
|
320
357
|
return ctx + FEATURE_KICKOFF_BODY;
|
package/lib/templates/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const kickoff = require('./kickoff');
|
|
4
4
|
const featureKickoff = require('./feature-kickoff');
|
|
5
|
+
const featureIntegrate = require('./integrate');
|
|
5
6
|
const resume = require('./resume');
|
|
6
7
|
const sessionEnd = require('./session-end');
|
|
7
8
|
const recovery = require('./recovery');
|
|
@@ -10,6 +11,7 @@ const codingRuleset = require('./coding-ruleset');
|
|
|
10
11
|
const MODES = {
|
|
11
12
|
kickoff,
|
|
12
13
|
'feature-kickoff': featureKickoff,
|
|
14
|
+
'feature-integrate': featureIntegrate,
|
|
13
15
|
resume,
|
|
14
16
|
'session-end': sessionEnd,
|
|
15
17
|
recovery
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Feature Integrate — single-shot, autonomous (v1)
|
|
4
|
+
//
|
|
5
|
+
// Operator opt-in: a feature includes a phase with id "phase-integrate" at
|
|
6
|
+
// kickoff time, signaling that this feature ships a public-contract surface.
|
|
7
|
+
// When all preceding phases are marked "done" and phase-integrate is the
|
|
8
|
+
// next-pending phase, this template generates docs/features/<slug>/PUBLIC_API.md
|
|
9
|
+
// from the live OpenAPI spec + the feature's manifest, marks phase-integrate
|
|
10
|
+
// done, and (when the backend is reachable) posts the doc to the vectorizer
|
|
11
|
+
// at /api/public/integration-docs/index so the chatbot can serve it as
|
|
12
|
+
// integration guidance.
|
|
13
|
+
//
|
|
14
|
+
// State machine:
|
|
15
|
+
// A: PUBLIC_API.md missing or feature manifest changed since last write → write the doc
|
|
16
|
+
// B: PUBLIC_API.md present and manifest unchanged → no-op (idempotent re-run)
|
|
17
|
+
// C: phase-integrate is not pending or prior phases incomplete → halt with a one-line note
|
|
18
|
+
|
|
19
|
+
const FEATURE_INTEGRATE_BODY = `# Feature Integrate (single-shot, autonomous)
|
|
20
|
+
|
|
21
|
+
You are running in single-shot mode. You produce one response and the process
|
|
22
|
+
exits. Your job is to take the operator's feature slug (in the session header
|
|
23
|
+
above), generate \`docs/features/<slug>/PUBLIC_API.md\` from the live API
|
|
24
|
+
surface, mark the feature's \`phase-integrate\` step done, and (best-effort)
|
|
25
|
+
register the doc with the backend so it can be vectorized for the chatbot.
|
|
26
|
+
|
|
27
|
+
This is **a doc generation pass** — do not change application code, controllers,
|
|
28
|
+
or any file outside \`docs/features/<slug>/\`.
|
|
29
|
+
|
|
30
|
+
## Step 0 — Resolve the feature slug
|
|
31
|
+
|
|
32
|
+
The slug is in the session header (\`Feature slug: <slug>\`). If absent, abort
|
|
33
|
+
with a one-line note: "Feature integrate requires --prompt <slug>."
|
|
34
|
+
|
|
35
|
+
Verify \`docs/features/<slug>/\` exists and contains \`manifest.json\`. If not,
|
|
36
|
+
abort: "No feature found at docs/features/<slug>/. Run feature-kickoff first."
|
|
37
|
+
|
|
38
|
+
## Step 1 — Confirm the operator opted in
|
|
39
|
+
|
|
40
|
+
Open \`docs/features/<slug>/manifest.json\`. Look for a phase with
|
|
41
|
+
\`id: "phase-integrate"\`.
|
|
42
|
+
|
|
43
|
+
- **Not found** → halt: "This feature did not opt into integrate phase. To
|
|
44
|
+
enable, re-run feature-kickoff with the --integrate flag, or hand-edit
|
|
45
|
+
manifest.json to add a phase with id 'phase-integrate'."
|
|
46
|
+
- **Found and \`status: "done"\` already** → State B → check whether
|
|
47
|
+
\`PUBLIC_API.md\` exists. If yes, idempotent no-op: print "PUBLIC_API.md
|
|
48
|
+
already up to date." If no, treat as State A.
|
|
49
|
+
- **Found, \`status: "pending"\`, prior phases all \`status: "done"\`** → State A.
|
|
50
|
+
Continue.
|
|
51
|
+
- **Found, \`status: "pending"\`, prior phases NOT all done** → State C → halt:
|
|
52
|
+
"phase-integrate is not yet eligible — phases X, Y, Z still pending."
|
|
53
|
+
|
|
54
|
+
## Step 2 — Identify the public surface this feature shipped
|
|
55
|
+
|
|
56
|
+
From \`manifest.json\`, walk every \`phases[].steps[].touches\` value.
|
|
57
|
+
|
|
58
|
+
Filter to **Java files under \`src/main/java/com/yourcompany/voice/controller/\`**
|
|
59
|
+
or any file whose path contains \`/controller/\`. These are the candidates for
|
|
60
|
+
the public surface.
|
|
61
|
+
|
|
62
|
+
For each touched controller file:
|
|
63
|
+
- Read its \`@RequestMapping\` base path.
|
|
64
|
+
- Read its \`@PostMapping\`, \`@GetMapping\`, \`@PutMapping\`, \`@DeleteMapping\`
|
|
65
|
+
annotations to enumerate routes.
|
|
66
|
+
- Note the request body shape (look for \`@RequestBody\`, \`@RequestParam\`,
|
|
67
|
+
\`@RequestPart\` types).
|
|
68
|
+
- Note the return type and any \`@Operation(summary=...)\` Swagger annotation
|
|
69
|
+
for prose.
|
|
70
|
+
|
|
71
|
+
If a step's \`touches\` field references a non-controller file (service, DTO,
|
|
72
|
+
config), skip it. Only the public-contract surface goes in PUBLIC_API.md.
|
|
73
|
+
|
|
74
|
+
If after filtering you have zero controllers, this feature does not ship a
|
|
75
|
+
public contract — abort: "No controllers in manifest's touched files; nothing
|
|
76
|
+
to document. Use a different doc kind for this feature."
|
|
77
|
+
|
|
78
|
+
## Step 3 — Fetch the live OpenAPI spec (best effort)
|
|
79
|
+
|
|
80
|
+
Hit \`http://localhost:8080/v3/api-docs\` (Spring's default) and parse the JSON.
|
|
81
|
+
If the backend is not running locally, look for an env var \`OPENAPI_URL\` and
|
|
82
|
+
use that. If neither is reachable, skip live OpenAPI and rely solely on the
|
|
83
|
+
controller source you read in Step 2 — the doc just won't include exact
|
|
84
|
+
response schemas, only the route + summary + request body shape.
|
|
85
|
+
|
|
86
|
+
For each route from Step 2, look up the matching path in the OpenAPI spec
|
|
87
|
+
(\`paths.<basePath><route>\`) and pull:
|
|
88
|
+
- \`summary\` and \`description\`
|
|
89
|
+
- request schema (\`requestBody.content."application/json".schema\`)
|
|
90
|
+
- response schema (\`responses."200".content.*.schema\`)
|
|
91
|
+
- error responses
|
|
92
|
+
|
|
93
|
+
## Step 4 — Generate \`docs/features/<slug>/PUBLIC_API.md\`
|
|
94
|
+
|
|
95
|
+
Use this skeleton, populated from Steps 2–3. Keep it tight; integrators will
|
|
96
|
+
read this in 60 seconds.
|
|
97
|
+
|
|
98
|
+
\`\`\`markdown
|
|
99
|
+
---
|
|
100
|
+
feature_slug: <slug>
|
|
101
|
+
generated_at: <ISO date>
|
|
102
|
+
api_kind: public-contract
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# <Feature Title> — Public API
|
|
106
|
+
|
|
107
|
+
<2–3 sentence summary from manifest.feature_title + manifest.proposedSummary
|
|
108
|
+
or the feature's ROADMAP.md Summary section.>
|
|
109
|
+
|
|
110
|
+
**Base URL (deployed):** \`<from PUBLIC_VOICE_API.md or operator-known\`
|
|
111
|
+
|
|
112
|
+
## 60-second quickstart
|
|
113
|
+
|
|
114
|
+
\\\`\\\`\\\`bash
|
|
115
|
+
# <smallest curl that exercises the headline endpoint>
|
|
116
|
+
\\\`\\\`\\\`
|
|
117
|
+
|
|
118
|
+
## Endpoints
|
|
119
|
+
|
|
120
|
+
| Path | Method | Use when |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
<one row per route from Step 2-3>
|
|
123
|
+
|
|
124
|
+
## Authentication
|
|
125
|
+
|
|
126
|
+
<Same shared-key pattern as PUBLIC_VOICE_API.md if these endpoints sit behind
|
|
127
|
+
PublicVoiceGuard, or whatever the controllers actually enforce.>
|
|
128
|
+
|
|
129
|
+
## <One section per endpoint>
|
|
130
|
+
|
|
131
|
+
### Request
|
|
132
|
+
\\\`\\\`\\\`json
|
|
133
|
+
<request schema, example values>
|
|
134
|
+
\\\`\\\`\\\`
|
|
135
|
+
|
|
136
|
+
### Response
|
|
137
|
+
\\\`\\\`\\\`json
|
|
138
|
+
<response schema, example values>
|
|
139
|
+
\\\`\\\`\\\`
|
|
140
|
+
|
|
141
|
+
| Field | Type | Required | Notes |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
<rows>
|
|
144
|
+
|
|
145
|
+
## Error reference
|
|
146
|
+
|
|
147
|
+
<reuse the standard envelope; reference codes the controllers actually emit>
|
|
148
|
+
|
|
149
|
+
## Limits and defaults
|
|
150
|
+
|
|
151
|
+
<rate limits, concurrency caps, etc. if known>
|
|
152
|
+
|
|
153
|
+
## Need anything
|
|
154
|
+
|
|
155
|
+
<Match the footer style in docs/PUBLIC_VOICE_API.md.>
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
**Don't pad.** If a section has nothing to say, leave it out. Match the tone
|
|
159
|
+
of the existing \`docs/PUBLIC_VOICE_API.md\` (terse, integrator-focused, no
|
|
160
|
+
filler).
|
|
161
|
+
|
|
162
|
+
## Step 5 — Mark phase-integrate done in the manifest
|
|
163
|
+
|
|
164
|
+
Update \`docs/features/<slug>/manifest.json\`:
|
|
165
|
+
- The phase with \`id: "phase-integrate"\` → \`status: "done"\`
|
|
166
|
+
- All steps within that phase → \`status: "done"\`
|
|
167
|
+
- If \`current_phase\` was \`phase-integrate\`, advance it to \`null\` (or to
|
|
168
|
+
the next pending phase if any exist after integrate).
|
|
169
|
+
|
|
170
|
+
Mirror the change into \`docs/features/<slug>/STATUS.md\` (check off the box).
|
|
171
|
+
Mirror into \`docs/features/<slug>/PHASES.md\` (\`- [x]\` for the integrate
|
|
172
|
+
steps).
|
|
173
|
+
|
|
174
|
+
## Step 6 — Best-effort: register the doc with the backend vectorizer
|
|
175
|
+
|
|
176
|
+
POST the generated markdown to the backend's integration-docs index endpoint:
|
|
177
|
+
|
|
178
|
+
\`\`\`bash
|
|
179
|
+
curl -s -X POST "$BACKEND/api/public/integration-docs/index" \\
|
|
180
|
+
-H "Content-Type: application/json" \\
|
|
181
|
+
-H "X-Tenant-Id: $TENANT" \\
|
|
182
|
+
-H "X-Public-Voice-Key: $KEY" \\
|
|
183
|
+
-d '{"slug": "<slug>", "markdown": "<contents>"}'
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
Read \`BACKEND\` from env or default to \`http://localhost:8080\`. Read
|
|
187
|
+
\`TENANT\` from env (\`KNOXIS_TENANT_ID\`) — if missing, skip vectorization
|
|
188
|
+
with a one-line note "Skipped vectorization: KNOXIS_TENANT_ID not set."
|
|
189
|
+
Read \`KEY\` from env \`PUBLIC_VOICE_API_SHARED_KEY\` (optional).
|
|
190
|
+
|
|
191
|
+
If the curl returns non-2xx, do not fail — the doc was still written to disk.
|
|
192
|
+
Print one line: "Vectorize call returned <code>; doc was generated. The
|
|
193
|
+
operator can re-run vectorization manually."
|
|
194
|
+
|
|
195
|
+
## Step 7 — Output for the operator
|
|
196
|
+
|
|
197
|
+
End with a copyable block:
|
|
198
|
+
|
|
199
|
+
\`\`\`
|
|
200
|
+
Feature integrate complete: <slug>
|
|
201
|
+
|
|
202
|
+
Files written:
|
|
203
|
+
docs/features/<slug>/PUBLIC_API.md
|
|
204
|
+
docs/features/<slug>/manifest.json (phase-integrate → done)
|
|
205
|
+
docs/features/<slug>/STATUS.md (checkbox)
|
|
206
|
+
docs/features/<slug>/PHASES.md (checkboxes)
|
|
207
|
+
|
|
208
|
+
Vectorization: <success | skipped | failed>
|
|
209
|
+
|
|
210
|
+
Next: the doc is ready to share with integrators and (when registered) to
|
|
211
|
+
serve via the chatbot's "I need a feature for X" intent.
|
|
212
|
+
\`\`\`
|
|
213
|
+
|
|
214
|
+
## Non-negotiables
|
|
215
|
+
|
|
216
|
+
- Write \`PUBLIC_API.md\` to disk. Do not describe what you would write.
|
|
217
|
+
- Match the tone and structure of \`docs/PUBLIC_VOICE_API.md\` and
|
|
218
|
+
\`docs/PUBLIC_SPEAKER_API.md\` — these are the canonical examples.
|
|
219
|
+
- Keep \`manifest.json\` consistent with the three MD files at all times.
|
|
220
|
+
- Do not modify application code (no edits to anything outside
|
|
221
|
+
\`docs/features/<slug>/\`).
|
|
222
|
+
- One feature per run. The slug is final for this run.
|
|
223
|
+
- Idempotent. Re-running with PUBLIC_API.md already up to date is a no-op.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
`;
|
|
228
|
+
|
|
229
|
+
function buildFeatureIntegratePrompt({
|
|
230
|
+
taskDescription,
|
|
231
|
+
productSlug,
|
|
232
|
+
projectSlug,
|
|
233
|
+
workspace,
|
|
234
|
+
userId,
|
|
235
|
+
workspaceId
|
|
236
|
+
} = {}) {
|
|
237
|
+
const header = ['Mode: feature-integrate'];
|
|
238
|
+
if (productSlug) header.push(`Product: ${productSlug}`);
|
|
239
|
+
if (projectSlug) header.push(`Project: ${projectSlug}`);
|
|
240
|
+
if (workspace) header.push(`Workspace: ${workspace}`);
|
|
241
|
+
if (userId) header.push(`User ID: ${userId}`);
|
|
242
|
+
if (workspaceId) header.push(`Workspace ID: ${workspaceId}`);
|
|
243
|
+
if (taskDescription) header.push(`Feature slug: ${taskDescription}`);
|
|
244
|
+
const ctx = `Session context:\n${header.join('\n')}\n\n`;
|
|
245
|
+
return ctx + FEATURE_INTEGRATE_BODY;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = {
|
|
249
|
+
title: 'Feature Integrate',
|
|
250
|
+
body: FEATURE_INTEGRATE_BODY,
|
|
251
|
+
buildPrompt: buildFeatureIntegratePrompt
|
|
252
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"description": "Single source of truth for the feature-kickoff phase shape. Shared by scripts/knoxis-helper/lib/templates/feature-kickoff.js (operator-facing prompt) and src/main/java/com/yourcompany/voice/service/kickoffprep/* (server-side meeting → kickoff-prep generator). Both read these constraints to keep the JSON shape consistent and prevent drift.",
|
|
4
|
+
|
|
5
|
+
"slug": {
|
|
6
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,39}$",
|
|
7
|
+
"maxLength": 40,
|
|
8
|
+
"rules": [
|
|
9
|
+
"Lowercase alphanumeric and dashes only.",
|
|
10
|
+
"Max 40 characters.",
|
|
11
|
+
"Semantically descriptive of the feature.",
|
|
12
|
+
"Do not include the words 'feature' or 'task'."
|
|
13
|
+
],
|
|
14
|
+
"examples": [
|
|
15
|
+
{ "description": "Add real-time notification toasts", "slug": "realtime-notification-toasts" },
|
|
16
|
+
{ "description": "Replace polling with SSE on the dashboard", "slug": "dashboard-sse-replacement" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"phases": {
|
|
21
|
+
"minCount": 3,
|
|
22
|
+
"maxCount": 6,
|
|
23
|
+
"stepsPerPhase": { "min": 2, "max": 6 },
|
|
24
|
+
"rules": [
|
|
25
|
+
"Phases run sequentially.",
|
|
26
|
+
"Each phase has a clear 'done when' criterion.",
|
|
27
|
+
"Each step is one imperative-voice action plus the file(s) or surface it touches.",
|
|
28
|
+
"Steps must be small enough to ship in one pair-program session.",
|
|
29
|
+
"Don't pad. A 4-step feature is fine. A 30-step feature with 3 phases of filler is not."
|
|
30
|
+
],
|
|
31
|
+
"defaultShape": [
|
|
32
|
+
{ "title": "Discovery", "goal": "Read existing code, find integration points, list constraints" },
|
|
33
|
+
{ "title": "Design", "goal": "Data model / API contract / UI shape decisions" },
|
|
34
|
+
{ "title": "Implementation", "goal": "Commit-sized steps, each touching named files" },
|
|
35
|
+
{ "title": "Testing", "goal": "Unit / integration / manual coverage" },
|
|
36
|
+
{ "title": "Rollout", "goal": "Feature flag, docs, telemetry, deprecations" }
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
"openQuestions": {
|
|
41
|
+
"guidance": "Capture genuine open questions only. Most 'questions' are decisions you should make and capture in DECISIONS instead. If you write more than 3 pending questions you are over-using this.",
|
|
42
|
+
"softMax": 3
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
"manifestShape": {
|
|
46
|
+
"feature_slug": "string (matches slug.pattern)",
|
|
47
|
+
"feature_title": "string",
|
|
48
|
+
"parent_task_id": "string | null",
|
|
49
|
+
"workspace_id": "string | null",
|
|
50
|
+
"user_id": "string | null",
|
|
51
|
+
"product_slug": "string | null",
|
|
52
|
+
"project_slug": "string | null",
|
|
53
|
+
"kicked_off_at": "ISO timestamp",
|
|
54
|
+
"status": "planning | blocked | in-progress | done",
|
|
55
|
+
"current_phase": "string (phase id, e.g. phase-1)",
|
|
56
|
+
"phases": [
|
|
57
|
+
{
|
|
58
|
+
"id": "string (phase-{n})",
|
|
59
|
+
"title": "string",
|
|
60
|
+
"goal": "string",
|
|
61
|
+
"done_when": "string",
|
|
62
|
+
"status": "pending | in-progress | done",
|
|
63
|
+
"steps": [
|
|
64
|
+
{
|
|
65
|
+
"id": "string (phase-{n}-step-{m})",
|
|
66
|
+
"action": "string (imperative)",
|
|
67
|
+
"touches": "string (file or surface)",
|
|
68
|
+
"status": "pending | in-progress | done"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"definition_of_done": ["string"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -345,6 +345,68 @@ For specific archetypes, populate \`archetype_specific_data\`:
|
|
|
345
345
|
|
|
346
346
|
Show me. Ask: "Paste this into the portal, or wait for portal MCP to upload it automatically (if live)?"
|
|
347
347
|
|
|
348
|
+
## Step 9.5 — Auto-integrate eligible features (opt-in features only)
|
|
349
|
+
|
|
350
|
+
**Orientation:** "If any feature in this project opted into auto-doc generation
|
|
351
|
+
by including a \`phase-integrate\` phase, and that phase is now eligible to
|
|
352
|
+
run, generate its public API doc here so the next session opens with it
|
|
353
|
+
already shipped."
|
|
354
|
+
|
|
355
|
+
For each \`docs/features/*/manifest.json\`, check whether **all three** are true:
|
|
356
|
+
|
|
357
|
+
1. A phase exists with \`id: "phase-integrate"\`.
|
|
358
|
+
2. That phase's \`status\` is \`"pending"\`.
|
|
359
|
+
3. Every phase before it has \`status: "done"\`.
|
|
360
|
+
|
|
361
|
+
If no manifest meets all three, say "No features are eligible for auto-integrate
|
|
362
|
+
this session." and skip to Step 10.
|
|
363
|
+
|
|
364
|
+
For each eligible feature, run the integrate playbook autonomously (you do not
|
|
365
|
+
need a separate command — execute the steps inline now):
|
|
366
|
+
|
|
367
|
+
1. Read the controllers listed in the feature's \`phases[].steps[].touches\` —
|
|
368
|
+
anything under \`src/main/java/com/yourcompany/voice/controller/\`.
|
|
369
|
+
2. (Best effort) Fetch \`http://localhost:8080/v3/api-docs\` to enrich with live
|
|
370
|
+
request/response schemas. If unreachable, skip and use the controller source
|
|
371
|
+
directly. Don't fail the step on this.
|
|
372
|
+
3. Generate \`docs/features/<slug>/PUBLIC_API.md\` matching the structure and
|
|
373
|
+
tone of \`docs/PUBLIC_VOICE_API.md\` and \`docs/PUBLIC_SPEAKER_API.md\`.
|
|
374
|
+
Sections: title + 2-3 sentence summary, 60-second quickstart, endpoint
|
|
375
|
+
table, authentication, per-endpoint request/response, error reference,
|
|
376
|
+
limits, footer.
|
|
377
|
+
4. Update the feature's \`manifest.json\`: \`phase-integrate\` → \`status: "done"\`,
|
|
378
|
+
each step within → \`status: "done"\`, advance \`current_phase\` past it.
|
|
379
|
+
5. Mirror the status changes into \`docs/features/<slug>/STATUS.md\` and
|
|
380
|
+
\`docs/features/<slug>/PHASES.md\` (check the boxes).
|
|
381
|
+
6. **Best-effort vectorization.** If \`KNOXIS_TENANT_ID\` is set in the
|
|
382
|
+
environment, POST the generated markdown to:
|
|
383
|
+
|
|
384
|
+
\`\`\`
|
|
385
|
+
POST $BACKEND/api/public/integration-docs/index
|
|
386
|
+
Headers: Content-Type: application/json
|
|
387
|
+
X-Tenant-Id: $KNOXIS_TENANT_ID
|
|
388
|
+
X-Public-Voice-Key: $PUBLIC_VOICE_API_SHARED_KEY (if set)
|
|
389
|
+
Body: { "slug": "<slug>", "markdown": "<file contents>" }
|
|
390
|
+
\`\`\`
|
|
391
|
+
|
|
392
|
+
Default \`BACKEND\` to \`http://localhost:8080\`. If the call fails with a
|
|
393
|
+
non-2xx, **don't fail the session** — note "Vectorization deferred (HTTP
|
|
394
|
+
<code>); operator can re-run via knoxis-helper --mode feature-integrate
|
|
395
|
+
--prompt <slug>." and move on. The doc is on disk regardless.
|
|
396
|
+
|
|
397
|
+
For each feature you processed, output:
|
|
398
|
+
|
|
399
|
+
\`\`\`
|
|
400
|
+
Auto-integrated: <slug>
|
|
401
|
+
PUBLIC_API.md: <created | refreshed>
|
|
402
|
+
manifest.json: phase-integrate → done
|
|
403
|
+
Vectorization: <success | skipped (no tenant) | deferred (HTTP <code>)>
|
|
404
|
+
\`\`\`
|
|
405
|
+
|
|
406
|
+
Do not run integrate logic on features where the conditions aren't met. Don't
|
|
407
|
+
add a \`phase-integrate\` to features that didn't have one — operator opt-in
|
|
408
|
+
is sticky.
|
|
409
|
+
|
|
348
410
|
## Step 10 — Final summary
|
|
349
411
|
|
|
350
412
|
Print a one-paragraph summary:
|