10 KiB
10 KiB
Build Spec Phase 4 — Support Bot Foundation
Date: 2026-06-22 Design: Pi Build: Claude Code Est: 1 session Depends on: Phase 3 (startup ritual) complete — shared-context pointers wired
Overview
Phase 4 builds the foundation for a support bot by generating a machine-readable API reference from the backend source and cross-referencing it with the existing support manual. This enables automated troubleshooting support and sets up the data layer the bot will query.
Two substeps:
- 4.1 — Write
gen-api-ref.pyscript to parse FastAPI routes frommain.py - 4.2 — Cross-reference
support-manual.mdwith generated ref, fix gaps
4.1 Generate support-api-ref.json
File to create
signal/scripts/gen-api-ref.py
What to build
A Python script that:
- Reads
python-backend/api/main.py - Parses the AST for FastAPI route decorators (
@app.get,@app.post,@app.put,@app.delete,@app.patch,@router.*) - Extracts for each route:
- Path (e.g.,
/api/upload-csv) - HTTP method (GET, POST, etc.)
- Summary (first line of function docstring)
- Parameters (from function signature — path params, query params, body params)
- Response codes and descriptions (from
raise HTTPException(...)calls in function body, plus the implicit 200)
- Path (e.g.,
- Writes
signal/docs/support-api-ref.json
AST parsing approach
Use Python's ast module (stdlib — no external dependencies). Parse for:
@app.<method>decorators and@router.<method>decorators- Extract the route string from the decorator argument
- Get the function name from the decorated
FunctionDef - Extract docstring first line for summary
- Scan
raisestatements inside the function body forHTTPException(status_code=...) - Detect
RequestandUploadFileparams for the parameters list
Expected output
{
"generated": "2026-06-22",
"source": "python-backend/api/main.py",
"endpoints": [
{
"path": "/api/health",
"method": "GET",
"summary": "Health check endpoint",
"parameters": [],
"responses": [
{"code": 200, "description": "Service healthy"}
]
},
{
"path": "/api/upload-csv",
"method": "POST",
"summary": "Upload and process a CSV file",
"parameters": [
{"name": "file", "in": "formData", "type": "file", "required": true}
],
"responses": [
{"code": 200, "description": "CSV processed successfully"},
{"code": 400, "description": "Invalid CSV format or missing columns"},
{"code": 401, "description": "Invalid or missing API key"},
{"code": 500, "description": "Server error processing CSV"}
]
}
]
}
Script structure
#!/usr/bin/env python3
"""Generate support-api-ref.json from FastAPI backend source."""
import ast
import json
from pathlib import Path
BACKEND_DIR = Path(__file__).parent.parent / "python-backend"
OUTPUT = Path(__file__).parent.parent / "docs" / "support-api-ref.json"
MAIN_FILE = BACKEND_DIR / "api" / "main.py"
# Route decorator prefixes to match
HTTP_METHODS = {"get", "post", "put", "delete", "patch", "options", "head"}
# Known module names that may contain route definitions
SHARED_ROUTER_MODULES = [
"api.routes",
]
def extract_route(node):
"""Given a decorator Call node, return (method, path) or None."""
# Handle @app.get, @router.post, etc.
if (isinstance(node.func, ast.Attribute)
and isinstance(node.func.value, ast.Name)
and node.func.attr in HTTP_METHODS):
path = node.args[0].value if node.args else ""
return node.func.attr.upper(), path
return None
def extract_summary(docstring):
"""Get the first line of a docstring."""
if not docstring:
return ""
lines = docstring.strip().split("\n")
return lines[0].strip()
def extract_error_codes(body):
"""Scan a function body for HTTPException raises and extract status codes."""
codes = [{"code": 200, "description": "Success"}]
for node in ast.walk(body):
if (isinstance(node, ast.Raise)
and isinstance(node.exc, ast.Call)
and isinstance(node.exc.func, ast.Name)
and node.exc.func.id == "HTTPException"):
for kw in node.exc.keywords:
if kw.arg == "status_code" and isinstance(kw.value, ast.Constant):
detail = ""
for kw2 in node.exc.keywords:
if kw2.arg == "detail" and isinstance(kw2.value, ast.Constant):
detail = kw2.value.value
break
codes.append({
"code": kw.value.value,
"description": detail or f"HTTP {kw.value.value}"
})
return codes
def extract_function_parameters(func_def):
"""Extract parameter info from a function definition."""
params = []
for arg in func_def.args.args:
arg_name = arg.arg
if arg_name == "request":
params.append({"name": arg_name, "in": "path", "type": "Request", "required": True})
elif arg_name in ("file", "csv_file"):
params.append({"name": arg_name, "in": "formData", "type": "file", "required": True})
else:
params.append({"name": arg_name, "in": "query", "type": "string", "required": False})
return params
def parse_file(filepath):
"""
Parse a single Python file for FastAPI route decorators.
Returns list of endpoint dicts.
"""
endpoints = []
tree = ast.parse(filepath.read_text())
for node in ast.walk(tree):
if not isinstance(node, ast.FunctionDef):
continue
func_def = node
for decorator in func_def.decorator_list:
route = extract_route(decorator)
if route:
method, path = route
endpoints.append({
"path": path,
"method": method,
"summary": extract_summary(ast.get_docstring(func_def)),
"parameters": extract_function_parameters(func_def),
"responses": extract_error_codes(func_def),
})
return endpoints
def main():
endpoints = parse_file(MAIN_FILE)
# Optional: scan shared router modules
# for module_name in SHARED_ROUTER_MODULES:
# module_path = BACKEND_DIR / module_name.replace(".", "/") + ".py"
# if module_path.exists():
# endpoints.extend(parse_file(module_path))
output = {
"generated": "2026-06-22",
"source": str(MAIN_FILE.relative_to(BACKEND_DIR.parent)),
"endpoints": endpoints,
}
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
OUTPUT.write_text(json.dumps(output, indent=2) + "\n")
print(f"Generated {len(endpoints)} endpoints → {OUTPUT}")
if __name__ == "__main__":
main()
Verification
python3 signal/scripts/gen-api-ref.py
cat signal/docs/support-api-ref.json | python3 -c "
import json,sys; d=json.load(sys.stdin)
print(f'{len(d[\"endpoints\"])} endpoints extracted from {d[\"source\"]}')
for ep in d['endpoints']:
codes = [str(r['code']) for r in ep['responses']]
print(f' {ep[\"method\"]:6} {ep[\"path\"]:30} → {ep[\"summary\"][:40]} [{','.join(codes)}]')
"
Acceptance
- Script runs without external dependencies (stdlib only)
- Output file written to
signal/docs/support-api-ref.json - All real API endpoints extracted (health, upload, export, etc.)
- Error codes from
raise HTTPException(...)captured - Parameter lists populated
4.2 Cross-reference support-manual.md with API ref
What to do
- Read both
signal/docs/support-manual.mdand the generatedsupport-api-ref.json - For each endpoint in the API ref, check the support manual for:
- Does the manual mention this endpoint? If not, add a section or a link
- Do the error messages in the manual's tables match the actual HTTPException codes from the API ref?
- Do the parameter descriptions match what the backend actually accepts?
- Add a note at the top of
support-manual.mdnoting the generation timestamp:
*Auto-generated API reference: signal/docs/support-api-ref.json (generated YYYY-MM-DD)*
- Add any missing endpoints to the manual as a section:
---
## [METHOD] /api/endpoint-name
**Summary:** description
**Error codes:**
| Code | Description |
|------|-------------|
| 200 | Success |
| 4xx | Error type |
Verification
# Count endpoints in API ref
ENDPOINTS=$(python3 -c "import json; d=json.load(open('signal/docs/support-api-ref.json')); print(len(d['endpoints']))")
# Count sections in support manual that mention endpoints
MANUAL_COVERAGE=$(grep -c '/api/' signal/docs/support-manual.md)
echo "API ref: $ENDPOINTS endpoints, Manual covers: $MANUAL_COVERAGE /api/ references"
If MANUAL_COVERAGE is significantly less than ENDPOINTS, more manual sections are needed.
Acceptance
- support-manual.md has a generation timestamp note at the top
- All API ref endpoints have a corresponding section in the manual
- Error code tables in the manual match the actual codes from the API ref
- No stale/incorrect error descriptions in the manual
Implementation Order
- Write
signal/scripts/gen-api-ref.py - Run it to generate
signal/docs/support-api-ref.json - Read the generated ref alongside the manual
- Update
signal/docs/support-manual.md:- Add generation timestamp note
- Add missing endpoint sections
- Fix any error code mismatches
- Commit both files
Completion Report Template
### Phase 4 (Support Bot Foundation)
#### 4.1 API ref generation
- [ ] gen-api-ref.py written to signal/scripts/
- [ ] Runs with stdlib only (no pip install)
- [ ] support-api-ref.json generated with all N endpoints
#### 4.2 Manual cross-reference
- [ ] Generation timestamp added to support-manual.md
- [ ] All endpoints cross-referenced
- [ ] Error codes match between manual and API ref