@jrpool/kilotest 46.1.2 → 47.0.1
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/.claude/settings.local.json +13 -1
- package/.devin/config.local.json +5 -1
- package/.github/workflows/ci.yml +0 -3
- package/.github/workflows/periodic-smoke-tests.yml +30 -0
- package/.github/workflows/publish-mcp.yml +4 -4
- package/{alerts.test.js → alerts.test.ts} +17 -29
- package/{alerts.js → alerts.ts} +10 -13
- package/api/{getReport.test.js → getReport.test.ts} +9 -10
- package/api/{getReport.js → getReport.ts} +23 -24
- package/api/{listDiagnoses.test.js → listDiagnoses.test.ts} +26 -27
- package/api/{listDiagnoses.js → listDiagnoses.ts} +46 -70
- package/api/{listIssues.test.js → listIssues.test.ts} +21 -14
- package/api/{listIssues.js → listIssues.ts} +75 -74
- package/api/{listReports.test.js → listReports.test.ts} +17 -16
- package/api/listReports.ts +87 -0
- package/api/{listViolators.test.js → listViolators.test.ts} +19 -20
- package/api/{listViolators.js → listViolators.ts} +45 -70
- package/api/requestFeature.test.ts +54 -0
- package/api/{requestFeature.js → requestFeature.ts} +18 -15
- package/api/{requestRetest.test.js → requestRetest.test.ts} +33 -28
- package/api/{requestRetest.js → requestRetest.ts} +32 -25
- package/api/{requestTest.test.js → requestTest.test.ts} +35 -30
- package/api/{requestTest.js → requestTest.ts} +35 -28
- package/api/{routes.test.js → routes.test.ts} +5 -5
- package/api/{routes.js → routes.ts} +6 -6
- package/api/{schemas.js → schemas.ts} +69 -50
- package/api/{util.test.js → util.test.ts} +31 -11
- package/api/{util.js → util.ts} +61 -36
- package/api/version.ts +8 -0
- package/{apiIntegration.test.js → apiIntegration.test.ts} +12 -13
- package/balances.ts +87 -0
- package/{checkinfo.js → checkinfo.ts} +24 -20
- package/docs/AI-TOOLS.md +4 -4
- package/docs/BACKLOG.md +356 -0
- package/docs/SERVICE.md +144 -17
- package/docs/decisions/002-coverage-tool.md +40 -0
- package/docs/decisions/003-adr-threshold.md +34 -0
- package/docs/decisions/004-docs-directory.md +23 -0
- package/docs/decisions/005-observability.md +28 -0
- package/docs/decisions/006-git-workflow.md +28 -0
- package/docs/decisions/007-internal-testing.md +34 -0
- package/docs/decisions/008-dependencies.md +28 -0
- package/docs/decisions/009-qai-integration.md +40 -0
- package/docs/decisions/QAI-HISTORICAL-CONTEXT.md +42 -0
- package/eslint.config.mjs +27 -0
- package/index.html +3 -3
- package/{index.test.js → index.test.ts} +380 -143
- package/{index.js → index.ts} +382 -283
- package/{indexBalances.test.js → indexBalances.test.ts} +21 -17
- package/{indexServe.test.js → indexServe.test.ts} +9 -9
- package/linkValidation.test.ts +190 -0
- package/{mcp.test.js → mcp.test.ts} +53 -26
- package/{mcp.js → mcp.ts} +29 -20
- package/memory/MEMORY.md +7 -0
- package/memory/user_maintainer_context.md +24 -0
- package/openapi.yaml +115 -68
- package/package.json +35 -15
- package/pm2.config.cjs +29 -0
- package/{routes.test.js → routes.test.ts} +8 -7
- package/{routing.test.js → routing.test.ts} +11 -12
- package/scripts/{generate-openapi.js → generate-openapi.ts} +13 -13
- package/serve.ts +15 -0
- package/smokeTest.ts +234 -0
- package/test/dbFixture.ts +21 -0
- package/test/fixtures/{buildFixtures.js → buildFixtures.ts} +14 -13
- package/test/{setup.cjs → setup.ts} +2 -2
- package/tsconfig.json +16 -0
- package/{util.test.js → util.test.ts} +350 -257
- package/util.ts +921 -0
- package/web/ai0BalanceForm/{ai0BalanceForm.test.js → ai0BalanceForm.test.ts} +15 -15
- package/web/ai0BalanceForm/{index.js → index.ts} +10 -18
- package/web/enqueue/index.ts +56 -0
- package/web/enqueueForm/index.html +9 -9
- package/web/enqueueForm/index.ts +43 -0
- package/web/expungeReportsForm/{expungeReportsForm.test.js → expungeReportsForm.test.ts} +18 -20
- package/web/expungeReportsForm/index.ts +18 -0
- package/web/hideReportForm/{hideReportForm.test.js → hideReportForm.test.ts} +14 -14
- package/web/hideReportForm/{index.js → index.ts} +17 -21
- package/web/listDiagnoses/{index.js → index.ts} +48 -58
- package/web/listDiagnoses/{listDiagnoses.test.js → listDiagnoses.test.ts} +39 -34
- package/web/listIssues/{index.js → index.ts} +67 -92
- package/web/listIssues/{listIssues.test.js → listIssues.test.ts} +45 -51
- package/web/listReports/index.html +3 -3
- package/web/listReports/{index.js → index.ts} +48 -53
- package/web/listReports/{listReports.test.js → listReports.test.ts} +41 -43
- package/web/listRules/{index.js → index.ts} +18 -23
- package/web/listRules/{listRules.test.js → listRules.test.ts} +26 -19
- package/web/listTopIssues/{index.js → index.ts} +36 -47
- package/web/listTopIssues/{listTopIssues.test.js → listTopIssues.test.ts} +26 -17
- package/web/listViolators/{index.js → index.ts} +51 -71
- package/web/listViolators/{listViolators.test.js → listViolators.test.ts} +43 -36
- package/web/manage/index.ts +22 -0
- package/web/pruneReportsForm/index.ts +18 -0
- package/web/pruneReportsForm/{pruneReportsForm.test.js → pruneReportsForm.test.ts} +11 -11
- package/web/reannotate/index.ts +70 -0
- package/web/reannotate/{reannotate.test.js → reannotate.test.ts} +19 -22
- package/web/reannotateForm/{index.js → index.ts} +41 -50
- package/web/reannotateForm/{reannotateForm.test.js → reannotateForm.test.ts} +13 -16
- package/web/renewWCAG/{index.js → index.ts} +9 -9
- package/web/renewWCAG/{renewWCAG.test.js → renewWCAG.test.ts} +17 -20
- package/web/renewWCAGForm/index.ts +22 -0
- package/web/{expungeReportsForm/index.js → reportDeletion.ts} +56 -38
- package/web/requestRetest/index.html +7 -7
- package/web/requestRetest/index.ts +31 -0
- package/web/requestRetest/requestRetest.test.ts +116 -0
- package/web/requestRetestForm/{index.js → index.ts} +10 -16
- package/web/requestRetestForm/{requestRetestForm.test.js → requestRetestForm.test.ts} +11 -12
- package/web/requestTest/index.html +7 -7
- package/web/requestTest/{index.js → index.ts} +5 -5
- package/web/requestTest/{requestTest.test.js → requestTest.test.ts} +21 -21
- package/web/requestTestForm/index.html +4 -4
- package/web/requestTestForm/index.ts +21 -0
- package/web/requestTestForm/{requestTestForm.test.js → requestTestForm.test.ts} +9 -9
- package/web/rewindReportsForm/index.ts +18 -0
- package/web/rewindReportsForm/{rewindReportsForm.test.js → rewindReportsForm.test.ts} +11 -11
- package/web/tutorialAI/index.html +184 -0
- package/web/tutorialAI/index.ts +80 -0
- package/web/tutorialAI/tutorialAI.test.ts +192 -0
- package/web/{tutorial → tutorialWeb}/index.html +9 -8
- package/web/tutorialWeb/index.ts +78 -0
- package/web/tutorialWeb/tutorialWeb.test.ts +195 -0
- package/web/unhideReportForm/{index.js → index.ts} +17 -21
- package/web/unhideReportForm/{unhideReportForm.test.js → unhideReportForm.test.ts} +16 -16
- package/aceconfig.js +0 -28
- package/api/listReports.js +0 -89
- package/api/requestFeature.test.js +0 -58
- package/api/version.js +0 -8
- package/indexFallback.test.js +0 -33
- package/indexWorkers.test.js +0 -79
- package/jsconfig.json +0 -8
- package/pm2.config.js +0 -18
- package/smokeTest.js +0 -128
- package/util.js +0 -744
- package/web/enqueue/index.js +0 -67
- package/web/enqueueForm/index.js +0 -49
- package/web/manage/index.js +0 -22
- package/web/pruneReportsForm/index.js +0 -123
- package/web/reannotate/index.js +0 -58
- package/web/renewWCAGForm/index.js +0 -22
- package/web/requestRetest/index.js +0 -31
- package/web/requestRetest/requestRetest.test.js +0 -119
- package/web/requestTestForm/index.js +0 -21
- package/web/rewindReportsForm/index.js +0 -121
- package/web/tutorial/index.js +0 -63
- package/web/tutorial/tutorial.test.js +0 -97
- /package/test/fixtures/db/jobs/{recs.json → testRequests.json} +0 -0
- /package/web/{tutorial → tutorialWeb}/images/newsletter-form.png +0 -0
package/docs/BACKLOG.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Backlog
|
|
2
|
+
|
|
3
|
+
Engineering tasks and risks that are not yet scheduled.
|
|
4
|
+
|
|
5
|
+
Items marked completed are preserved for about 2 weeks in case of production bugs.
|
|
6
|
+
|
|
7
|
+
## Document alerting
|
|
8
|
+
|
|
9
|
+
Document when and how Kilotest sends alerts to its maintainer, including the subscription to Resend and the DNS configuration for Resend authentication.
|
|
10
|
+
|
|
11
|
+
## Decide whether to tighten the DMARC policy and add SPF alignment for Resend
|
|
12
|
+
|
|
13
|
+
Live DNS for `kilotest.com`, checked 2026-09-16, shows a DMARC record of `v=DMARC1; p=none;` (monitoring only, no `rua=` reporting address) and an SPF record (`v=spf1 include:_spf.porkbun.com ~all`) that does not include `resend.com`. Mail sent via Resend is currently authenticated only by the DKIM record at `resend._domainkey.kilotest.com`. This may be an intentional, working minimal configuration, or an oversight from Resend's onboarding; it has not been confirmed against the Resend dashboard's domain-verification status. Consider confirming, in the Resend dashboard, whether SPF alignment is recommended or required for this domain's deliverability, and once alert deliverability has been stable for a period, whether to tighten DMARC from `p=none` to `p=quarantine` and add an `rua=` address so DMARC aggregate reports reach the maintainer.
|
|
14
|
+
|
|
15
|
+
## Widen test-request duplicate detection
|
|
16
|
+
|
|
17
|
+
The duplicate check in `addTestRequest` (`util.ts`), reached by all four test/retest request paths (UI `/requestTest.html` and `/requestRetest.html`; API `requestTest` and `requestRetest` operations), compares an incoming request only against other requests currently pending in `db/jobs/testRequests.json`, i.e., submitted but not yet manually approved by the maintainer into a job. Once a pending request is approved into a job (`web/enqueue/index.ts`), all pending requests for that URL are cleared from `testRequests.json`, so this shared check cannot recognize a duplicate against a request that has already been approved, is currently running, or has already completed. An identical request submitted after approval triggers a fresh alert rather than being filtered.
|
|
18
|
+
|
|
19
|
+
The four paths differ in what, if anything, they check before reaching this shared dedup, and none of the four closes the gap:
|
|
20
|
+
|
|
21
|
+
- UI `requestTest` (`web/requestTest/index.ts`) additionally calls `getRequestability`, rejecting the request outright if the URL matches a claimed or queued job. This screens out most already-approved, not-yet-completed requests for a new test, but not ones for a completed report, since `getRequestability` does not consult reports.
|
|
22
|
+
- API `requestTest` (`api/requestTest.ts`) additionally checks `getReportExtracts` and rejects the request if a report already exists for the same description and URL, but, unlike the UI path, does not call `getRequestability`, so it does not screen out a URL that is currently claimed or queued as a job.
|
|
23
|
+
- UI `requestRetest` (`web/requestRetest/index.ts`) and API `requestRetest` (`api/requestRetest.ts`) call neither `getRequestability` nor a same-description-and-URL report check; each only confirms the report being retested is still the latest one for its page before falling through to the shared dedup.
|
|
24
|
+
|
|
25
|
+
Consider widening the shared `addTestRequest` check itself, so that all four paths benefit uniformly, to also compare against approved, claimed, queued, or completed requests, for example recently created jobs or reports for the same URL and description, and if so over what time window, rather than continuing to rely on each path's own inconsistent pre-check.
|
|
26
|
+
|
|
27
|
+
## Improve MCP-zero discoverability
|
|
28
|
+
|
|
29
|
+
### Advice from Gemini
|
|
30
|
+
|
|
31
|
+
To make your **kilotest** MCP server discoverable by **MCP-Zero** agent loops (or enterprise gateways using active tool discovery architectures), you need to optimize how your server represents itself to semantic routers.
|
|
32
|
+
|
|
33
|
+
Because MCP-Zero operates as a **two-stage semantic routing system** (first matching the server domain, then matching the specific tools via vector embeddings), a standard MCP list_tools setup is not enough. You must optimize your metadata and schemas so that indexers can crawl and agents can query your server dynamically on demand.
|
|
34
|
+
|
|
35
|
+
Follow these steps to make your server fully discoverable:
|
|
36
|
+
|
|
37
|
+
### **1. Re-Architect Your Tool Naming & Descriptions**
|
|
38
|
+
|
|
39
|
+
MCP-Zero relies heavily on the LLM’s ability to articulate an *Active Tool Request* based on semantic gaps. Ambiguous tool names like `listReports` are the primary reason a server gets passed over during a vector lookup.
|
|
40
|
+
|
|
41
|
+
Change your tool declarations to be unambiguous and explicitly tied to your domain (ensemble front-end quality and accessibility testing):
|
|
42
|
+
|
|
43
|
+
*`// ❌ Old Anti-Pattern for MCP-Zero`*
|
|
44
|
+
`{`
|
|
45
|
+
`name: "listReports",`
|
|
46
|
+
`description: "Lists all reports."`
|
|
47
|
+
`}`
|
|
48
|
+
|
|
49
|
+
*`// ✅ Optimized Pattern for MCP-Zero Discovery`*
|
|
50
|
+
`{`
|
|
51
|
+
`name: "kilotest_list_web_quality_reports",`
|
|
52
|
+
`description: "Retrieves a historical catalog of ensemble front-end quality test reports for web pages. Use this to check whether a specific URL has already been audited for accessibility (WCAG), usability, and W3C standards conformity."`
|
|
53
|
+
`}`
|
|
54
|
+
|
|
55
|
+
**Prefix your tools:** Use a uniform prefix like `kilotest_` to prevent namespaces from colliding with other standard testing tools when flattened into an agent’s runtime.
|
|
56
|
+
|
|
57
|
+
**Inject key synonyms in descriptions:** Notice how the bad example just repeats the name. The optimized description explicitly seeds semantic keywords (**accessibility, WCAG, usability, standards, audit**). If an agent thinks, *“I need to check if this site is accessible for screen readers,”* the embedding vectors will match your tool.
|
|
58
|
+
|
|
59
|
+
### **2. Deepen Your Parameter Schemas**
|
|
60
|
+
|
|
61
|
+
MCP-Zero models often inspect input expectations before mounting a server to ensure they can fulfill the schema requirements. Provide dense descriptions for arguments instead of generic labels:
|
|
62
|
+
|
|
63
|
+
`"url_filter": {`
|
|
64
|
+
`"type": "string",`
|
|
65
|
+
`"description": "Optional target web page URL to filter reports for (e.g., 'https://example.com'). Helps find existing accessibility and front-end compliance history for this specific site."`
|
|
66
|
+
`}`
|
|
67
|
+
|
|
68
|
+
(Note from maintainer: This feature does not yet exist, so this is an example illustrating a principle but cannot be applied verbatim.)
|
|
69
|
+
|
|
70
|
+
### **3. Maximize Your Centralized Registry Metadata (package.json)**
|
|
71
|
+
|
|
72
|
+
Since kilotest is a public npm-published server already registered on community indexes (like Smithery and Glama), ensure your package file is effectively feeding their background indexers. MCP-Zero ingestion scripts crawl these registries and rely on the keywords and description tags to build their top-level server embedding maps.
|
|
73
|
+
|
|
74
|
+
Update your package.json to feature dense, functional keywords:
|
|
75
|
+
|
|
76
|
+
`{`
|
|
77
|
+
`"name": "kilotest",`
|
|
78
|
+
`"description": "Model Context Protocol (MCP) server for Kilotest ensemble web testing. Runs over 1,300 front-end tests across multiple rule engines covering WCAG accessibility, usability, performance, and web standards.",`
|
|
79
|
+
`"keywords": [`
|
|
80
|
+
`"mcp",`
|
|
81
|
+
`"model-context-protocol",`
|
|
82
|
+
`"mcp-server",`
|
|
83
|
+
`"accessibility-testing",`
|
|
84
|
+
`"wcag",`
|
|
85
|
+
`"web-quality-audit",`
|
|
86
|
+
`"automated-qa",`
|
|
87
|
+
`"ensemble-testing"`
|
|
88
|
+
`]`
|
|
89
|
+
`}`
|
|
90
|
+
|
|
91
|
+
### **4. Provide a "Capability Discovery" Tool**
|
|
92
|
+
|
|
93
|
+
A useful pattern for MCP-Zero discovery loops is exposing a zero-argument metadata tool, such as kilotest_get_server_capabilities. If an agent routes to your server but needs to confirm exactly what it can check before running an audit, this lightweight tool can return a deterministic list summarizing the specific rule engines inside kilotest.
|
|
94
|
+
|
|
95
|
+
Here are both the **TypeScript schema definitions** to update your tool definitions in code, and an **automated script** to generate an optimized manifest file (`mcp-manifest.json`) for your npm release.
|
|
96
|
+
|
|
97
|
+
### **1. The Optimized TypeScript Tool Schemas**
|
|
98
|
+
|
|
99
|
+
Replace your existing tool definitions with these semantically rich schemas. They are designed to rank highly when evaluated by an MCP-Zero semantic routing loop.
|
|
100
|
+
|
|
101
|
+
`import {Tool} from "@modelcontextprotocol/sdk/types.js";`
|
|
102
|
+
|
|
103
|
+
`export const KILOTEST_TOOLS: Tool[] = [`
|
|
104
|
+
`{`
|
|
105
|
+
`name: "kilotest_list_web_quality_reports",`
|
|
106
|
+
`description: "Retrieves a historical catalog of ensemble front-end quality and compliance test reports. Use this tool to check if a specific web page or URL has already been audited for accessibility (WCAG), performance benchmarks, semantic HTML correctness, or usability standards.",`
|
|
107
|
+
`inputSchema: {`
|
|
108
|
+
`type: "object",`
|
|
109
|
+
`properties: {`
|
|
110
|
+
`url_filter: {`
|
|
111
|
+
`type: "string",`
|
|
112
|
+
`description: "Optional URL to filter reports for (e.g., 'https://example.com'). Helps identify existing compliance test history for a specific website."`
|
|
113
|
+
`},`
|
|
114
|
+
`limit: {`
|
|
115
|
+
`type: "number",`
|
|
116
|
+
`description: "Maximum number of historical reports to return. Defaults to 10.",`
|
|
117
|
+
`default: 10`
|
|
118
|
+
`}`
|
|
119
|
+
`}`
|
|
120
|
+
`}`
|
|
121
|
+
`},`
|
|
122
|
+
`{`
|
|
123
|
+
`name: "kilotest_run_ensemble_audit",`
|
|
124
|
+
`description: "Triggers a live, comprehensive front-end engineering audit on a target URL. Runs 1,300+ automated test checks across 12 rule engines simultaneously to evaluate WCAG accessibility compliance, mobile responsiveness, SEO configurations, and core web vitals performance.",`
|
|
125
|
+
`inputSchema: {`
|
|
126
|
+
`type: "object",`
|
|
127
|
+
`properties: {`
|
|
128
|
+
`url: {`
|
|
129
|
+
`type: "string",`
|
|
130
|
+
`description: "The absolute web page URL to inspect (must include http:// or https://)."`
|
|
131
|
+
`},`
|
|
132
|
+
`scan_depth: {`
|
|
133
|
+
`type: "string",`
|
|
134
|
+
`enum: ["single_page", "shallow_crawl"],`
|
|
135
|
+
`description: "Depth of the quality sweep. 'single_page' tests only the target URL; 'shallow_crawl' includes immediately linked internal assets."`
|
|
136
|
+
`}`
|
|
137
|
+
`},`
|
|
138
|
+
`required: ["url"]`
|
|
139
|
+
`}`
|
|
140
|
+
`},`
|
|
141
|
+
`{`
|
|
142
|
+
`name: "kilotest_get_server_capabilities",`
|
|
143
|
+
`description: "Exposes a metadata summary of the 12 underlying rule engines configured inside this kilotest instance. Run this zero-argument tool to inspect exactly what front-end compliance standards (such as WCAG 2.2, W3C HTML, or performance budgets) this server is currently capable of evaluating.",`
|
|
144
|
+
`inputSchema: {`
|
|
145
|
+
`type: "object",`
|
|
146
|
+
`properties: {}`
|
|
147
|
+
`}`
|
|
148
|
+
`}`
|
|
149
|
+
`];`
|
|
150
|
+
|
|
151
|
+
### **2. Auto-Generation Script (`generate-manifest.js`)**
|
|
152
|
+
|
|
153
|
+
MCP-Zero frameworks and platform indexers often ingest a static metadata file to cache tool definitions before spawning your server.
|
|
154
|
+
|
|
155
|
+
Create this file in your project root as `scripts/generate-manifest.js`. It pulls details directly from your package config and spits out a completely optimized index file.
|
|
156
|
+
|
|
157
|
+
`import fs from 'fs';`
|
|
158
|
+
`import path from 'path';`
|
|
159
|
+
|
|
160
|
+
*`// Read your existing package.json`*
|
|
161
|
+
`const packageJsonPath = path.resolve(process.cwd(), 'package.json');`
|
|
162
|
+
`const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));`
|
|
163
|
+
|
|
164
|
+
*`// Construct the high-fidelity MCP discovery manifest`*
|
|
165
|
+
`const mcpManifest = {`
|
|
166
|
+
`manifest_version: "1.0.0",`
|
|
167
|
+
`server: {`
|
|
168
|
+
`name: "kilotest",`
|
|
169
|
+
`version: pkg.version,`
|
|
170
|
+
`description: "Official Model Context Protocol (MCP) server for Kilotest ensemble web testing. Programmatically evaluates front-end code bases across 1,300 validation checks covering WCAG 2.1/2.2 accessibility, performance budgets, markup standards, and cross-browser UX compliance.",`
|
|
171
|
+
`homepage: pkg.homepage || "https://kilotest.com",`
|
|
172
|
+
`routing_tags: [`
|
|
173
|
+
`"accessibility", "wcag", "web-standards", "html-validation",`
|
|
174
|
+
`"ux-audit", "front-end-qa", "performance-benchmarks", "automated-testing"`
|
|
175
|
+
`]`
|
|
176
|
+
`},`
|
|
177
|
+
`// Static schema fallback for zero-install discovery loops`
|
|
178
|
+
`discovery_schemas: {`
|
|
179
|
+
`transport: "stdio",`
|
|
180
|
+
`recommended_namespace: "kilotest"`
|
|
181
|
+
`}`
|
|
182
|
+
`};`
|
|
183
|
+
|
|
184
|
+
*`// Write it to your distribution/root folder`*
|
|
185
|
+
`const outputPath = path.resolve(process.cwd(), 'mcp-manifest.json');`
|
|
186
|
+
`fs.writeFileSync(outputPath, JSON.stringify(mcpManifest, null, 2));`
|
|
187
|
+
|
|
188
|
+
``console.log(`✅ MCP-Zero Discovery Manifest successfully written to: ${outputPath}`);``
|
|
189
|
+
|
|
190
|
+
### **How to use this in your workflow**
|
|
191
|
+
|
|
192
|
+
> 1. Add a generation step to your package.json scripts:
|
|
193
|
+
> `"scripts": {`
|
|
194
|
+
> `"build": "tsc && node scripts/generate-manifest.js"`
|
|
195
|
+
> `}`
|
|
196
|
+
> 2. When you run your build pipeline, it will ensure that both your active runtime code and your static metadata file match perfectly.
|
|
197
|
+
> 3. Make sure `mcp-manifest.json` is included in your files array in `package.json` so it gets published to **npm**.
|
|
198
|
+
|
|
199
|
+
### **Other actions**
|
|
200
|
+
|
|
201
|
+
1. Create the **underlying handler function** for the new `kilotest_get_server_capabilities` tool.
|
|
202
|
+
2. Create a **GitHub Action configuration** to automatically validate these schemas on push.
|
|
203
|
+
|
|
204
|
+
## Add observability of request metrics
|
|
205
|
+
|
|
206
|
+
Record per-endpoint request counts, latencies, and error rates so that Kilotest managers can observe which API operations are most used and identify performance regressions.
|
|
207
|
+
|
|
208
|
+
## Serve explanation page for human MCP requests (completed)
|
|
209
|
+
|
|
210
|
+
When the `/mcp` path is requested without a `text/event-stream` accept header, serve an HTML page explaining that the endpoint is for AI agents and providing a link to the AI tutorial.
|
|
211
|
+
|
|
212
|
+
## Deploy revisions (completed)
|
|
213
|
+
|
|
214
|
+
Deploy the revisions to the codebase.
|
|
215
|
+
|
|
216
|
+
This requires:
|
|
217
|
+
|
|
218
|
+
- Update the configuration of [UptimeRobot](https://dashboard.uptimerobot.com/) to monitor Kilotest as a whole via the site root. Reconfigure the existing monitor from `https://kilotest.com/qai` to the Kilotest path `https://kilotest.com/`, so that it continues monitoring service health.
|
|
219
|
+
- Merge the changes to `main`.
|
|
220
|
+
- Pull the new `main` branch to the server.
|
|
221
|
+
- Update the actual server copy of `/etc/caddy/Caddyfile` (as root) and run `sudo systemctl reload caddy`.
|
|
222
|
+
- Restart Kilotest in PM2.
|
|
223
|
+
|
|
224
|
+
## QAI integration (completed)
|
|
225
|
+
|
|
226
|
+
The `jrpool/qai` repository is independent of this `jrpool/kilotest` repository, and they are published as two distict packages. That separation is due to an organizational requirement that no longer exists. Since the QAI application is a tutorial showing users how to use Kilotest, and the tutorial of Kilotest also shows users how to use Kilotest, it is appropriate to convert QAI to a part of the Kilotest codebase. QAI is currently deployed with the URL `https://kilotest.com/qai`, and that would not need to change. `Caddyfile` would be simplified (see the copy in `docs/SERVICE.md`). The QAI code would need to be copied into Kilotest. Any architectural incompatibilities would need to be discovered and resolved. Locally, `qai` is a sibling repository of `kilotest` on this host. Note that QAI health is currently monitored by UptimeRobot, and periodic health monitoring of Kilotest is proposed as the next backlog item after this one, so health monitoring should be handled in such a way that it will be appropriate after both backlog items are completed.
|
|
227
|
+
|
|
228
|
+
## Implement periodic monitoring (completed)
|
|
229
|
+
|
|
230
|
+
Status: Implementation complete; UptimeRobot reconfiguration remains.
|
|
231
|
+
|
|
232
|
+
A periodic GitHub Actions workflow has been implemented in `.github/workflows/periodic-smoke-tests.yml`, configured to run daily at 12:00 PM UTC (noon). The workflow runs `smokeTest.ts` against the deployed service at `kilotest.com`, validating that all public GET and POST paths are reachable and forwarded by Caddy (i.e., not returning bare 404 errors). This allows code and infrastructure to be deployed together, then validated by the periodic check independently. The workflow can also be triggered manually from the GitHub Actions interface.
|
|
233
|
+
|
|
234
|
+
Documentation for the periodic monitoring workflow has been added to `docs/SERVICE.md` in a new “Periodic monitoring” section, including notes on UptimeRobot health monitoring.
|
|
235
|
+
|
|
236
|
+
Remaining task: update the configuration of UptimeRobot to monitor Kilotest as a whole via the site root. Use the UptimeRobot console to reconfigure the existing monitor from `https://kilotest.com/qai` to the Kilotest path `https://kilotest.com/`, so that it continues monitoring service health after the QAI integration is deployed and verified live.
|
|
237
|
+
|
|
238
|
+
## Details
|
|
239
|
+
|
|
240
|
+
### QAI integration details (completed)
|
|
241
|
+
|
|
242
|
+
#### Context
|
|
243
|
+
|
|
244
|
+
QAI (`/Users/pool/Documents/Topics/repos/a11yTesting/qai`, sibling repo) is a small standalone Node HTTP server deployed at `https://kilotest.com/qai` on port 3001, reverse-proxied separately from Kilotest (port 3000) by Caddy. The two-package split was required by an organizational constraint that no longer applies. QAI’s own purpose, teaching users how to connect an AI platform to Kilotest’s MCP tools, makes it naturally a part of Kilotest’s own tutorial content, so the backlog calls for folding QAI into the Kilotest codebase as a single deployable service.
|
|
245
|
+
|
|
246
|
+
Investigation turned up an important existing fact. `kilotest/web/tutorial/` and QAI’s own tutorial-and-comments feature share the same alert env vars (`MANAGER_EMAIL`, `ALERT_API_HOST`, `ALERT_API_PATH`, `ALERT_API_KEY`, `ALERT_FROM`) and the same comment-collection shape. Kilotest’s tutorial pre-dates QAI, so this similarity reflects QAI having been built by copying Kilotest’s tutorial-and-comments architecture, not the reverse. Kilotest’s `web/tutorial/` has since diverged into its own, much larger “Accessibility Testing Strategies” tutorial (641 lines) unrelated to QAI’s “add an MCP connector to Claude” walkthrough content, but it remains the established, proven pattern for a page-plus-comments module in this codebase (`answer{}`/`tutorialComment.html` wiring in `index.ts`). This is **not** a duplicate-content problem to reconcile: it is the existing template QAI itself was originally modeled on, now being reused directly instead of through a second, separately-deployed reimplementation. The work is: retire QAI’s standalone server/repo, and add QAI’s tutorial and comments pages to Kilotest as a second `web/<topic>` module pair, following the `web/tutorial/` pattern QAI was descended from.
|
|
247
|
+
|
|
248
|
+
Work will happen on the already-checked-out `qai` branch in the `kilotest` repo.
|
|
249
|
+
|
|
250
|
+
**Governing principle for structural and naming differences**: wherever QAI and Kilotest diverge in file naming, directory structure, or convention, do not default to Kilotest’s existing choice simply because Kilotest is the surviving repository. Evaluate both approaches on their merits and let the better design win, exactly as already decided above for documentation file naming (see “QAI’s documentation” below). This applies to structural questions too, not only naming: for example, QAI serves its static HTML and assets from a dedicated `public/` directory with a generic extension-to-content-type lookup (`web/qaiTutorial/`’s planned structure, described in the implementation steps below, instead follows Kilotest’s existing per-page `web/<topic>/index.ts` convention, since the new pages need the request-handling logic a plain static directory could not provide, but the underlying question of whether Kilotest’s codebase would benefit from a general-purpose static-asset directory or helper, rather than one `else if` branch per static file as `index.ts` currently has, is a legitimate structural question raised by this merge and worth a brief evaluation during implementation, even though it is not required to complete the merge itself).
|
|
251
|
+
|
|
252
|
+
#### What gets ported vs. retired
|
|
253
|
+
|
|
254
|
+
**Ported (QAI content has no Kilotest equivalent):**
|
|
255
|
+
|
|
256
|
+
- QAI’s “connect an AI platform to Kilotest” tutorial content (`public/tutorial.html`, 65 lines): becomes a new `qaiTutorial.html` page.
|
|
257
|
+
- QAI’s comment form (`public/comments.html`): becomes a new `web/qaiComment/` module, with GET and POST both handled there (reworked to Kilotest’s inline `fetch`-based submission convention rather than QAI’s full-page POST; QAI’s separate `public/comment-ack.html` acknowledgment page is not ported, see Decisions below).
|
|
258
|
+
- QAI’s comment-spam-management logic (length bounds of 20–1000 characters, and duplicate-resubmission rejection, that is rejecting identical text resubmitted within roughly 1000 seconds). This is generalized into a shared helper (see Decisions below) used by **both** the new `web/qaiComment/` module and the existing `web/tutorial/index.ts`’s `handleComment`, so Kilotest’s original tutorial comments gain the same spam protection.
|
|
259
|
+
|
|
260
|
+
**Retired (superseded by existing Kilotest infrastructure, or QAI-only machinery no longer needed once folded into one process):**
|
|
261
|
+
|
|
262
|
+
- `public/favicon.ico`: identical to Kilotest’s existing root `favicon.ico`; no porting needed, since the existing one is already served at `/favicon.ico`.
|
|
263
|
+
- `src/main.ts`, `src/requestHandler.ts`, `ecosystem.json`: QAI’s standalone server, routing, and PM2 config. Kilotest’s `index.ts` dispatch and `pm2.config.cjs` take over entirely.
|
|
264
|
+
- `src/alerts.ts`: Kilotest’s own `alerts.ts` (`sendAlert(subject, body)`) is a direct match; reuse it rather than porting a second copy.
|
|
265
|
+
- QAI’s own `tsconfig.json`, `eslint.config.mjs`, `package.json`, and CI workflow (`checks.yaml`): Kilotest’s root configs (`tsconfig.json`, `eslint.config.mjs`, `.github/workflows/ci.yml`) already apply to the whole repo, so no per-directory config is needed. Note that Kilotest’s `tsconfig` adds `noUncheckedIndexedAccess`, which QAI’s code was never checked against (expect to fix a few index-access sites when the ported code moves under Kilotest’s typecheck).
|
|
266
|
+
- QAI’s `db/comments.json` runtime data: not migrated (it is transient, ephemeral demo data, and the file is already gitignored in QAI); a fresh `web/qaiComment/comments.json` starts empty, exactly as `web/tutorial/comments.json` does today.
|
|
267
|
+
|
|
268
|
+
**QAI’s documentation, merged, not discarded:** QAI was built under an organizational requirement to exemplify best-practice software documentation, and its `docs/` is genuinely richer than Kilotest’s in several respects (Kilotest currently has only 2 ADRs; QAI has 9, plus architecture, requirements, and deployment docs Kilotest lacks entirely). The goal is for the merge to raise Kilotest’s documentation standard, not lose QAI’s. Concretely:
|
|
269
|
+
|
|
270
|
+
- **File and directory naming when both sides have a version of “the same” doc**: use whichever naming is more industry-standard, decided at implementation time. Do not default to “Kilotest’s name wins because it is the surviving repo”. QAI’s file names were themselves prescribed to follow industry-standard documentation practice, per its organizational requirement, so where the two conventions differ, QAI’s naming is the likely winner; confirm case by case rather than assuming.
|
|
271
|
+
- **ADRs** (`qai/docs/decisions/001`–`009`): QAI’s ADR format (frontmatter with `date`/`status`, `Context and problem`/`Considered options`/`Decision` structure) already matches Kilotest’s own (`kilotest/docs/decisions/001-api-design.md`, `002-coverage-tool.md`), the same convention. QAI’s own `002-adr-threshold.md` states the governing principle for what the ADR set should contain: its confirmation test is that “the ADRs describe all and only the strategic decisions manifested in the codebase.” That principle conflicts with the classic Nygard convention of never editing an ADR once written, since a set that must always describe current architecture cannot also be a frozen, append-only log. Kilotest resolves this conflict by adopting mutable, kept-current ADRs as its own explicit convention: a documented, deliberate departure from the stricter classic convention, and a recognized alternative position in the industry, not an idiosyncratic one. An ADR’s content can be edited as architecture evolves, and it remains named an ADR throughout; historical context belongs inside a current ADR when it helps a future maintainer (for example, “X was tried, but it had defects Y and Z, so X was replaced with R” helps prevent a regrettable reversion to X), rather than requiring a separate, frozen, superseded file to be kept around for that purpose. This convention itself should be written up as a revision to the ported `002-adr-threshold.md` (see below), so it is documented rather than assumed.
|
|
272
|
+
- Only ADRs describing still-operative decisions are ported into `kilotest/docs/decisions/` as new, renumbered files, continuing on from Kilotest’s existing 001–002. Candidates for porting: `001-docs-directory.md` (the `docs/` plus `docs/decisions/` layout both repositories already use), `002-adr-threshold.md` (the ADR-writing policy itself, revised per the mutability convention above), `006-observability.md`, `007-git-workflow.md`, `008-internal-testing.md`, and `009-dependencies.md` (general engineering policies that are candidates to become repository-wide Kilotest policy, not QAI-specific). Confirm each candidate against Kilotest’s actual current practice during implementation, and edit its content as needed (reframing QAI-specific wording to repository-wide, updating anything no longer accurate) before porting: this editing is now consistent with Kilotest’s stated mutable-ADR convention, not an exception to it.
|
|
273
|
+
- ADRs describing decisions specific to QAI’s former existence as an independent deployment are folded into the still-operative ADRs as historical context, not ported as separate files: `003-initial-routing.md` (chose independent deployment, the opposite of this merge), `004-prerelease-versions.md` (QAI’s own version-0.1.0 scoping), and `005-infrastructure-health.md` (QAI needing its own external monitoring because Kilotest lacked any). Where one of these adds genuinely useful context to a still-operative decision (for example, `003-initial-routing.md`’s reasoning belongs inside whatever ADR now documents Kilotest’s routing architecture, as a brief note on the independent-deployment approach that was tried and superseded by this merge), fold a short summary of it in; otherwise leave it recoverable only from `jrpool/qai`’s git history.
|
|
274
|
+
- This merge is itself exactly the kind of decision QAI’s own `002-adr-threshold.md` says merits an ADR: a strategic choice with substantial effects on maintainability. Write a new Kilotest ADR documenting the QAI-integration decision itself, positioned last in the ported sequence.
|
|
275
|
+
- **`architecture.md`, `requirements.md`**: read these fully during implementation and fold any content still accurate and relevant to the merged codebase into Kilotest’s docs. Kilotest currently has no equivalent top-level architecture or requirements doc, while QAI’s organizational requirement produced these as a matter of course; evaluate whether Kilotest’s documentation would genuinely benefit from adopting a dedicated architecture or requirements doc at this level (per the governing principle above) rather than assuming `docs/SERVICE.md` is automatically the right destination just because it already exists. Content describing QAI-as-independent-service specifics, such as its own deployment topology, becomes historical once folded in.
|
|
276
|
+
- **`deployment.md`**: covers the same subject as `docs/SERVICE.md`, which already documents the real, merged deployment, so the two should become one file rather than two. Per the governing principle above, do not assume `SERVICE.md`’s name and existing shape automatically wins just because Kilotest already has it: read both in full during implementation and decide, on the merits of each (completeness, clarity, industry-standard naming for a deployment doc), which name and structure the merged file should keep, folding in whatever content the other has that it lacks.
|
|
277
|
+
- **`repository.md`, `project-vision.md`**: likely QAI-specific project-management framing (course requirements, repo conventions for a now-retired standalone repo); read in full during implementation to confirm, but expect these to be summarized into a short historical note rather than ported whole.
|
|
278
|
+
- **`future.md`, `manual-verification.md`, `ai-implementation-review.md`**: read during implementation. `manual-verification.md` may contain durable QA procedure worth folding into Kilotest’s own testing docs; `future.md` and `ai-implementation-review.md` are more likely point-in-time project-status artifacts, and are candidates for summary rather than full porting.
|
|
279
|
+
- **`progress-reports/`, `reviews/`**: sprint reports, a demo video, and peer reviews, purely historical, course-context artifacts with no forward-looking maintainer value. Retain only as a brief pointer or summary (for example one paragraph in the merge’s own ADR, or a short history-style note: “QAI’s original development history, including sprint reports and peer reviews, is preserved in the `jrpool/qai` repository’s git history”) rather than copying the files into Kilotest.
|
|
280
|
+
- **`docs/rulesets/qai-main.json`**: QAI’s GitHub branch-protection ruleset export. Kilotest already has its own equivalent (`docs/Smoke Test.json`, per `docs/SERVICE.md`); compare during implementation for any protection QAI enforced that Kilotest does not yet (for example QAI’s required-testing-before-merge ADR, `008-internal-testing.md`, appears to match Kilotest’s own branch protection already, but confirm), and otherwise do not port the file itself.
|
|
281
|
+
- **`README.md`**: QAI’s root README is project-management framing for a now-retired standalone repo; it is not ported as a file, but confirm that Kilotest’s own README and docs adequately cover the “connect an AI platform” use case QAI’s README foregrounded (they may already, via the new `qaiTutorial.html` page itself).
|
|
282
|
+
|
|
283
|
+
#### Decisions locked in
|
|
284
|
+
|
|
285
|
+
- **Route placement**: QAI’s content gets its own new page(s), independent of the existing `tutorial.html` (which has evolved into unrelated Kilotest-specific content). Working names: `qaiTutorial.html` (the “connect an AI platform to Kilotest” walkthrough) and `qaiComment.html` (the comment form), with a POST to `qaiComment.html` handling submission, matching Kilotest’s convention of a GET page and a same-named POST action seen elsewhere (for example `requestTest.html`).
|
|
286
|
+
- **Module structure follows the established one-topic-per-directory convention**: every existing `web/<topic>/index.ts` module (`web/enqueueForm/`, `web/ai0BalanceForm/`, `web/hideReportForm/`, and others) exports one `answer(pathTail, search)` function and pairs with exactly one `index.html` in the same directory, rendered via the shared `populateTemplate(import.meta.dirname, query)` helper in `util.ts` (already used by essentially every existing GET page). QAI’s content is ported as two such modules, not one directory holding differently-named files: `web/qaiTutorial/` (the walkthrough page) and `web/qaiComment/` (the comment form, exporting both `answer` for its GET page and `handleComment` for its POST action, mirroring `web/tutorial/index.ts`’s existing two-export shape). This was corrected during implementation from an earlier draft of this plan that proposed a single `web/qaiTutorial/` directory with distinctly-named HTML files and a new hand-rolled substitution helper; that draft duplicated `populateTemplate`, which already does the same job. A separate `web/qaiCommentAck/` module, considered at one point for an HTML acknowledgment page, was dropped once the response-shape decision below was reversed.
|
|
287
|
+
- **Comment validation and unified length limits**: `web/qaiTutorial/`’s and `web/qaiComment/`’s GET pages return `{status: ‘error’, message}` on failure exactly like any other `web/<topic>/index.ts` module, and Kilotest’s existing dispatch in `index.ts` already calls `serveError(getAbuseError(request, answerData.message), response, true)` automatically whenever an `answer[topic]` function returns an error status (see the generic `.html` GET dispatch at line 504), rendering the existing `error.html` template; no new error-response helper is needed there. The comment-submission POST path uses a different, JSON-based error convention instead (see the Acknowledgment response entry below). During implementation, validation was split into two helpers in `util.ts`: `checkCommentLength` (reusable length validation) and `checkCommentDuplicate` (reusable duplicate-submission detection, rejecting identical text resubmitted within roughly 1000 seconds). Both are called by `web/qaiComment/`’s `handleComment` and the existing `web/tutorial/index.ts`’s `handleComment`, so both comment endpoints enforce identical checks. Kilotest’s original tutorial comment length limit (500 characters) was raised to 1000 to match QAI’s limit, unifying the bounds; both endpoints now enforce a 20–1000 character range. This is a behavior change to `web/tutorial/` beyond the QAI port itself, and is worth calling out in the PR description as a deliberate, bundled improvement rather than scope creep.
|
|
288
|
+
- **Acknowledgment response: unify on `web/tutorial/`’s JSON/AJAX convention, not QAI’s HTML-page convention (reversed from an earlier draft of this plan)**: investigation during implementation found that `web/tutorial/index.html`’s comment form is a single-page AJAX widget (a `fetch('/tutorialComment.html', ...)` call that parses a JSON `{status}` response and updates an inline status region, with a live character counter, and no page navigation), while QAI’s `comments.html`/`comment-ack.html` is a traditional full-page POST that navigates to a separate acknowledgment page. Per this plan’s own “let the better design win” governing principle, `web/tutorial/`’s inline-feedback UX is the better design, so the new QAI comment form is built to match it, not the reverse: `web/qaiComment/` gets its own small `fetch`-based submission script (adapted from `web/tutorial/index.html`’s pattern) with an inline status region, submitting to `qaiComment.html` and expecting a JSON `{status: 'ok'}` or `{status: 'error', message}` response, exactly like the existing tutorial comment form. No HTML acknowledgment page, and no `populateTemplate`-based ack module, is built for either flow; `populateTemplate` is still used for the ordinary GET pages (`web/qaiTutorial/`, `web/qaiComment/`) themselves, just not for a comment acknowledgment.
|
|
289
|
+
|
|
290
|
+
#### Implementation steps
|
|
291
|
+
|
|
292
|
+
The steps below are sequential, but not each individually required to pass Kilotest’s full check suite (100% coverage, typecheck, lint, tests) on its own: for example, after step 1 alone the new `util.ts` helper exists but nothing calls it yet, which the 100%-coverage requirement would fail, and after step 4 alone the new modules are wired into routing but the Caddyfile and documentation steps are still pending. Commit and push freely at intermediate points on the `qai` branch (or a further feature branch off it) as work progresses, but only open the pull request to `main` once every step is complete and the full check suite passes, per Kilotest’s branch-protection rules (`docs/SERVICE.md`, “Branch protection”).
|
|
293
|
+
|
|
294
|
+
1. **Add shared comment-validation helpers to `util.ts`**: `checkCommentLength(content: string)` validates that a comment’s raw length is within the 20–1000 character bounds, returning either an ok result or an error result with the applicable message (“shorter than 20” or “longer than 1000”). `checkCommentDuplicate(comments: {timeStamp: string; content: string}[], content: string)` validates that a comment does not repeat, verbatim, a comment submitted within the last 1000 seconds, returning either an ok result or the duplicate-rejection message. Factor these out of QAI’s `requestHandler.ts` logic (lines 78–110 of that file) rather than rewriting from scratch. These are validation logic only, not response rendering, since rendering is already handled by the existing `populateTemplate`/`serveError` machinery (see Decisions above). `checkCommentDuplicate` uses Kilotest’s `timeStamp` format (not QAI’s ISO `dateTime` format) to integrate with the shape already used by `web/tutorial/index.ts`’s comments.json.
|
|
295
|
+
|
|
296
|
+
2. **Update `web/tutorial/index.ts`’s `handleComment` and raise its length limit from 500 to 1000**:
|
|
297
|
+
- Call `checkCommentLength` on the raw content before sanitizing, and `checkCommentDuplicate` on the sanitized content before appending, so both checks apply to Kilotest’s original tutorial comments, returning `{status: ‘error’, message}` on any failure.
|
|
298
|
+
- Raise the character limit from 500 to 1000 to unify with QAI’s limit, and update the form label in `web/tutorial/index.html` (maxlength and character-counter logic) to reflect the new bound.
|
|
299
|
+
- No response-shape change: `handleComment`’s existing bare `{status: ‘ok’}`/`{status: ‘error’, message}` JSON responses, and the `index.ts` POST branch for `tutorialComment.html` stay as they are; only the validation calls and the limit are new.
|
|
300
|
+
- Update `web/tutorial/tutorial.test.ts` for the new rejection paths (too short, too long, duplicate) and the raised limit.
|
|
301
|
+
|
|
302
|
+
3. **Create a single new web module**, `web/qaiTutorial/`, following the established one-topic-per-directory pattern and mirroring `web/tutorial/`’s unified approach:
|
|
303
|
+
- `web/qaiTutorial/index.ts` exports both `answer()` (to serve the tutorial page) and `handleComment()` (to handle comment submissions), eliminating the need for a separate comments module.
|
|
304
|
+
- `web/qaiTutorial/index.html` combines QAI’s tutorial content (from `qai/public/tutorial.html`), adapted with Kilotest-style title and meta tags (dropping “QAI” branding, for example `Tutorial: Connect an AI Platform | Kilotest`), followed by an integrated comment-submission section at the end, following the pattern established by `web/tutorial/index.html`. The comment form is a `fetch`-based, inline widget (character counter, `comment-diagnosis` status region, `fetch(‘/qaiTutorialComment.html’, {method: ‘POST’, ...})` parsing a JSON response) rather than a separate page, so users can submit comments without leaving the tutorial. This allows readers to reference the tutorial content while commenting, and to submit multiple comments on the same visit.
|
|
305
|
+
- `handleComment` in `index.ts` enforces the unified 20–1000 character range via `checkCommentLength` and inline duplicate-checking (using ISO `dateTime` format for QAI comment timestamps, consistent with how the separate comments.json is structured), sanitizes, appends to a colocated `comments.json`, and calls the shared `sendAlert` from `../../alerts.ts` with subject `New QAI tutorial comment received`.
|
|
306
|
+
- GitHub issue links in the ported HTML: repointed from `jrpool/qai/issues` to `jrpool/kilotest/issues`, since bugs against this content are now Kilotest issues (repo disposition is a separate post-merge decision, but the *content* should reflect its new home immediately, regardless).
|
|
307
|
+
|
|
308
|
+
4. **Rename modules for clarity: `web/tutorial/` → `web/tutorialWeb/`, `web/qaiTutorial/` → `web/tutorialAI/`**: The original tutorial is for humans using Kilotest’s web interface directly; the new tutorial is for humans who want to enable their AI agents to use Kilotest (by connecting the MCP server). Parallel naming makes this distinction clear to contributors and users alike. Rename all related identifiers:
|
|
309
|
+
- Directory: `web/tutorial/` → `web/tutorialWeb/`
|
|
310
|
+
- Directory: `web/qaiTutorial/` → `web/tutorialAI/`
|
|
311
|
+
- Environment variables: `TUTORIAL_COMMENTS_PATH` → `TUTORIAL_WEB_COMMENTS_PATH`, `QAI_TUTORIAL_COMMENTS_PATH` → `TUTORIAL_AI_COMMENTS_PATH`
|
|
312
|
+
- Route names: `tutorialComment.html` → `tutorialWebComment.html`, `qaiTutorialComment.html` → `tutorialAIComment.html`
|
|
313
|
+
- Imports in `index.ts`: aliases like `answer as tutorialWeb`, `handleComment as handleTutorialWebComment`, `answer as tutorialAI`, `handleComment as handleTutorialAIComment`
|
|
314
|
+
- Test files: `web/tutorialWeb/tutorialWeb.test.ts`, `web/tutorialAI/tutorialAI.test.ts`
|
|
315
|
+
- HTML titles and content: "web tutorial" and "AI agent tutorial" terminology
|
|
316
|
+
- Redirects: `/qai/` and `/qai/comments` still 301-redirect to `/tutorialAI.html` (keeping the old paths working for backward compatibility)
|
|
317
|
+
|
|
318
|
+
5. **Wire into `index.ts`** (mirroring the `tutorialWebComment.html` pattern at lines 34, 82–154, 189, 930–941):
|
|
319
|
+
- Import `answer` and `handleComment` from `web/tutorialWeb/` and `web/tutorialAI/`, with appropriate aliases (following the existing import-aliasing convention at lines 38–69).
|
|
320
|
+
- Add both `tutorialWeb` and `tutorialAI` to the `answer{}` dispatch table. Both are plain `.html` GET pages, so they are picked up automatically by the existing generic `pageName.endsWith(‘.html’)` dispatch (line 504), with no new `else if` branches needed for GET.
|
|
321
|
+
- Add `tutorialWeb.html` and `tutorialAI.html` to `routes.GET`, and add `tutorialWebComment.html` and `tutorialAIComment.html` to `routes.POST` (lines 159–193).
|
|
322
|
+
- Add `pageName === ‘tutorialWebComment.html’` and `pageName === ‘tutorialAIComment.html’` branches in the POST dispatch chain, calling the respective `handleComment` functions and responding with the same bare-JSON `{status: ‘ok’}`/`{status: ‘error’, message}` shape, so both comment forms share one response convention.
|
|
323
|
+
- Redirect QAI’s old live paths to their new equivalents: add branches that 301-redirect both `/qai/comments` (the old `https://kilotest.com/qai/comments` path) and `/qai` (the old `https://kilotest.com/qai` root) to `/tutorialAI.html`. Check the longer `/qai/comments` path first, before checking the shorter `/qai` path (since pathname matching on path segments would match the shorter one too), so that existing bookmarks, search-index entries, and inbound links to the old QAI paths continue working rather than breaking once Caddy’s separate `/qai` proxy block is removed. Add `/qai` and `/qai/comments` to `routes.GET` alongside the other redirect-only aliases such as `/swagger.json`.
|
|
324
|
+
|
|
325
|
+
6. **Merge QAI’s documentation into Kilotest’s `docs/`** (see “QAI’s documentation, merged, not discarded” above for the full breakdown):
|
|
326
|
+
- Revise `002-adr-threshold.md` while porting it, to state Kilotest’s mutable, kept-current ADR convention explicitly (see above), since this convention is what governs how the rest of this step is carried out.
|
|
327
|
+
- Confirm each of `001-docs-directory.md`, `006-observability.md`, `007-git-workflow.md`, `008-internal-testing.md`, and `009-dependencies.md` still accurately describes Kilotest’s actual practice; port only the ones that do, as new, renumbered files in `kilotest/docs/decisions/`, continuing on from Kilotest’s existing 001–002, editing content as needed to reflect current, repository-wide reality.
|
|
328
|
+
- For `003-initial-routing.md`, `004-prerelease-versions.md`, and `005-infrastructure-health.md`: fold any genuinely useful historical context from each into whichever still-operative ADR now covers the same architectural area (for example, `003-initial-routing.md`’s independent-deployment reasoning becomes a brief historical note inside the ADR that documents Kilotest’s current routing architecture). Do not port these three as separate files; their full original text stays recoverable from `jrpool/qai`’s git history.
|
|
329
|
+
- Read `architecture.md`, `requirements.md`, `repository.md`, `project-vision.md`, `future.md`, `manual-verification.md`, `ai-implementation-review.md`, and `deployment.md` in full; fold durable, accurate content into Kilotest’s docs, evaluating for each whether the receiving doc should be an existing Kilotest file or, per the governing principle above, a new doc named and structured after whichever side’s convention is better (for `deployment.md` specifically, decide with `docs/SERVICE.md` whether the merged file keeps Kilotest’s name and shape, QAI’s, or a new one, rather than assuming `SERVICE.md` simply absorbs it). Write a brief historical-summary note for content in the remaining files that does not merit folding in.
|
|
330
|
+
- Add a short pointer note (in the new merge ADR, or a dedicated short doc) preserving the existence of `progress-reports/`, `reviews/`, and `docs/rulesets/qai-main.json`, referencing `jrpool/qai`’s git history as the archive rather than copying those files in.
|
|
331
|
+
- Write the new ADR documenting this integration decision itself, positioned last in the ported sequence.
|
|
332
|
+
|
|
333
|
+
7. **Update `smokeTest.ts`**: add `concretePaths.GET`/`.POST` entries for the new page names (`tutorialAI.html`, `tutorialAIComment.html`) and for the `/qai`/`/qai/comments` redirect aliases, and a `postBodies` entry if the comment POST needs a sample body.
|
|
334
|
+
|
|
335
|
+
8. **Update the Caddyfile copy** (`/etc/caddy/Caddyfile` on the server is tracked as a copy in `docs/SERVICE.md`):
|
|
336
|
+
- Remove the `redir /qai /qai/ 301` and `handle_path /qai* { reverse_proxy localhost:3001 }` blocks, since QAI no longer runs as a separate process; the old `/qai` and `/qai/comments` paths are now redirected at the application layer instead (see step 5), so Caddy needs no special-case handling for them beyond forwarding them like any other allowed GET path.
|
|
337
|
+
- Add the new page path(s), including `/qai` and `/qai/comments`, to the `@allowedGET`/`@allowedPOST` matchers.
|
|
338
|
+
- Update `docs/SERVICE.md`’s copy of the Caddyfile to match, and note in that doc that QAI is no longer a separately-deployed application (the `docs/SERVICE.md` “Applications” section currently implies other apps like QAI could live at `/opt/jpdev/xyz`; leave that generic note as is, and just remove the QAI-specific proxy config).
|
|
339
|
+
- The user will update the actual server copy and run `systemctl reload caddy` just before restarting Kilotest in PM2 after pulling a version containing the QAI integration.
|
|
340
|
+
|
|
341
|
+
9. **Tests**: The test files are already in place from step 4 (`web/tutorialWeb/tutorialWeb.test.ts` and `web/tutorialAI/tutorialAI.test.ts`), mirroring the structure and unit-testing both `answer()` and `handleComment()` directly. Rely on the existing `index.test.ts` integration tests to exercise the renamed routes once wired into `index.ts`’s dispatch (adding assertions there for the new paths and POST handler). Confirm 100% coverage is maintained per the `c8` config in `package.json` (branches, functions, lines, and statements all at 100%).
|
|
342
|
+
|
|
343
|
+
10. **Retire the QAI repo**:
|
|
344
|
+
- Confirm with the user whether `jrpool/qai` on GitHub should be archived, left as read-only history, or deleted, once the port is verified working in Kilotest. This plan does not delete anything in `/Users/pool/Documents/Topics/repos/a11yTesting/qai`; that is a separate, explicit decision for the user to make after the merge lands and is verified in production.
|
|
345
|
+
- Do not remove QAI’s existing UptimeRobot monitor. Instead, once the new `qaiTutorial.html`/`qaiComment.html` paths are live, reconfigure that monitor’s target so it continues watching a Kilotest path (for example the site root, or the new tutorial path) rather than the now-defunct `https://kilotest.com/qai`, effectively expanding its existing scope from monitoring QAI’s health specifically to monitoring Kilotest’s health as a whole. This closes the monitoring gap the original backlog note flagged, and does so by extending a monitor that already works rather than waiting on a new one (Kilotest’s own health-monitoring backlog item, or the “periodic smoke-test session” item, can still be pursued separately and later, but neither is a precondition for this reconfiguration).
|
|
346
|
+
|
|
347
|
+
#### Open question to resolve after the merge is verified live
|
|
348
|
+
|
|
349
|
+
- Disposition of the `jrpool/qai` GitHub repo once the new pages are confirmed working in production (archive, delete, or leave as read-only history). This is not needed to implement the merge itself.
|
|
350
|
+
|
|
351
|
+
#### Verification
|
|
352
|
+
|
|
353
|
+
- `npm run typecheck`, `npm run lint`, and `npm test` all pass in `kilotest` with the new module included, and coverage remains at 100%.
|
|
354
|
+
- Run `npm start` locally, then manually visit the new tutorial path and submit a test comment; confirm it is appended to the new `comments.json` and, with alert env vars set, triggers `sendAlert`.
|
|
355
|
+
- Run `node smokeTest.ts` against a local instance (or the deployed one, post-deploy) to confirm the new paths are both allowlisted in `routes` and reachable.
|
|
356
|
+
- After deploying: visit the old `https://kilotest.com/qai` and `https://kilotest.com/qai/comments` URLs directly and confirm each returns a 301 redirect to `/qaiTutorial.html` and `/qaiComment.html` respectively, per the redirect added in step 4, rather than a bare 404, now that Caddy’s separate `/qai` proxy block is gone.
|