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.
@@ -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;
@@ -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:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knoxis-helper",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "Local helper for Knoxis pair programming - connects your machine to Knoxis on qig.ai",
5
5
  "bin": {
6
6
  "knoxis-helper": "./bin/knoxis-helper.js"