Reference implementation for the Phoenix Architecture. Work in progress. aicoding.leaflet.pub/
ai coding crazy
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: working end-to-end todo API from spec

Fixed three issues:
1. DB sharing: strengthened architecture prompt to forbid new Database(),
require import from ../../db.js
2. Route consolidation: simplified spec to one section per resource,
producing 1 IU instead of 6 fragmented modules
3. Data model context: prompt builder now includes DEFINITION/CONTEXT
nodes from other sections so LLM sees full schema

All CRUD operations verified working:
- POST /todos → 201 with Zod validation
- GET /todos → 200, ordered by created_at
- GET /todos/:id → 200 or 404
- PATCH /todos/:id → updates title/completed
- DELETE /todos/:id → 204
- Validation: empty title → 400, missing todo → 404

+164 -389
examples/todo-app/app.db

This is a binary file and will not be displayed.

examples/todo-app/data.db

This is a binary file and will not be displayed.

examples/todo-app/data/app.db-shm

This is a binary file and will not be displayed.

examples/todo-app/data/app.db-wal

This is a binary file and will not be displayed.

+9 -37
examples/todo-app/spec/todos.md
··· 1 1 # Todo API 2 2 3 - A simple REST API for managing todo items. 4 - 5 - ## Data Model 6 - 7 - - A todo has: id (integer, auto-increment primary key), title (text, required), completed (boolean, default false), and created_at (timestamp, set automatically on creation) 3 + A REST API for managing todo items backed by SQLite. 8 4 9 - ## List Todos 5 + ## Todos Resource 10 6 11 - - GET /todos must return all todos as a JSON array ordered by created_at descending 12 - - The response must include all fields: id, title, completed, created_at 13 - 14 - ## Get Todo 15 - 16 - - GET /todos/:id must return a single todo as a JSON object 17 - - If the todo does not exist, the endpoint must return 404 with an error message 18 - 19 - ## Create Todo 20 - 21 - - POST /todos must create a new todo from a JSON request body 22 - - The request body must include a title field 7 + - A todo has: id (integer, auto-increment primary key), title (text, required), completed (integer 0 or 1, default 0), and created_at (timestamp, set automatically on creation) 8 + - GET / must return all todos as a JSON array ordered by created_at descending 9 + - GET /:id must return a single todo as a JSON object, or 404 if not found 10 + - POST / must create a new todo from a JSON request body containing a title field and return it with status 201 11 + - PATCH /:id must update a todo's title and/or completed fields from a JSON request body, or 404 if not found 12 + - DELETE /:id must delete a todo and return 204 with no body, or 404 if not found 23 13 - Title must not be empty 24 14 - Title must be at most 200 characters 25 - - The endpoint must return the created todo with status 201 26 - 27 - ## Update Todo 28 - 29 - - PATCH /todos/:id must update a todo from a JSON request body 30 - - The request body may include title and/or completed fields 31 - - If title is provided, it must not be empty and must be at most 200 characters 32 - - If completed is provided, it must be a boolean 33 - - If the todo does not exist, the endpoint must return 404 34 - - The endpoint must return the updated todo 35 - 36 - ## Delete Todo 37 - 38 - - DELETE /todos/:id must delete a todo 39 - - If the todo does not exist, the endpoint must return 404 40 - - On success, the endpoint must return 204 with no body 41 - 42 - ## Error Handling 43 - 15 + - Completed must be 0 or 1 44 16 - All error responses must be JSON objects with an "error" field containing a human-readable message 45 17 - Invalid JSON request bodies must return 400 46 18 - Validation failures must return 400 with a description of what failed
+1 -1
examples/todo-app/src/generated/index.ts
··· 7 7 export * as todos from './todos/index.js'; 8 8 9 9 export const services = [ 10 - { name: 'Todos', dir: 'todos', port: 3000, modules: 6 }, 10 + { name: 'Todos', dir: 'todos', port: 3000, modules: 1 }, 11 11 ] as const;
+4 -39
examples/todo-app/src/generated/todos/__tests__/todos.test.ts
··· 4 4 */ 5 5 6 6 import { describe, it, expect } from 'vitest'; 7 - import create_todo from '../create-todo.js'; 8 - import delete_todo from '../delete-todo.js'; 9 - import error_handling from '../error-handling.js'; 10 - import get_todo from '../get-todo.js'; 11 - import list_todos from '../list-todos.js'; 12 - import update_todo from '../update-todo.js'; 7 + import todos_resource from '../todos-resource.js'; 13 8 14 9 describe('Todos modules', () => { 15 - describe('Create Todo', () => { 16 - it('exports a Hono router as default', () => { 17 - expect(create_todo).toBeDefined(); 18 - expect(typeof create_todo.fetch).toBe('function'); 19 - }); 20 - }); 21 - describe('Delete Todo', () => { 10 + describe('Todos Resource', () => { 22 11 it('exports a Hono router as default', () => { 23 - expect(delete_todo).toBeDefined(); 24 - expect(typeof delete_todo.fetch).toBe('function'); 25 - }); 26 - }); 27 - describe('Error Handling', () => { 28 - it('exports a Hono router as default', () => { 29 - expect(error_handling).toBeDefined(); 30 - expect(typeof error_handling.fetch).toBe('function'); 31 - }); 32 - }); 33 - describe('Get Todo', () => { 34 - it('exports a Hono router as default', () => { 35 - expect(get_todo).toBeDefined(); 36 - expect(typeof get_todo.fetch).toBe('function'); 37 - }); 38 - }); 39 - describe('List Todos', () => { 40 - it('exports a Hono router as default', () => { 41 - expect(list_todos).toBeDefined(); 42 - expect(typeof list_todos.fetch).toBe('function'); 43 - }); 44 - }); 45 - describe('Update Todo', () => { 46 - it('exports a Hono router as default', () => { 47 - expect(update_todo).toBeDefined(); 48 - expect(typeof update_todo.fetch).toBe('function'); 12 + expect(todos_resource).toBeDefined(); 13 + expect(typeof todos_resource.fetch).toBe('function'); 49 14 }); 50 15 }); 51 16 });
-42
examples/todo-app/src/generated/todos/create-todo.ts
··· 1 - import { Hono } from 'hono'; 2 - import { z } from 'zod'; 3 - import Database from 'better-sqlite3'; 4 - 5 - const db = new Database('app.db'); 6 - 7 - // Register table migration 8 - db.exec(` 9 - CREATE TABLE IF NOT EXISTS todos ( 10 - id INTEGER PRIMARY KEY AUTOINCREMENT, 11 - title TEXT NOT NULL, 12 - created_at TEXT NOT NULL DEFAULT (datetime('now')), 13 - updated_at TEXT NOT NULL DEFAULT (datetime('now')) 14 - ) 15 - `); 16 - 17 - const CreateTodoSchema = z.object({ 18 - title: z.string().min(1, 'Title must not be empty').max(200, 'Title must not exceed 200 characters'), 19 - }); 20 - 21 - const router = new Hono(); 22 - 23 - // Create todo 24 - router.post('/', async (c) => { 25 - const result = CreateTodoSchema.safeParse(await c.req.json()); 26 - if (!result.success) return c.json({ error: result.error.issues[0].message }, 400); 27 - 28 - const { title } = result.data; 29 - const info = db.prepare('INSERT INTO todos (title) VALUES (?)').run(title); 30 - const todo = db.prepare('SELECT * FROM todos WHERE id = ?').get(info.lastInsertRowid); 31 - return c.json(todo, 201); 32 - }); 33 - 34 - export default router; 35 - 36 - /** @internal Phoenix VCS traceability — do not remove. */ 37 - export const _phoenix = { 38 - iu_id: '088be9c52621e3f6408f91eb003afcc59616238c00e70963dc7d1e900486eb6f', 39 - name: 'Create Todo', 40 - risk_tier: 'high', 41 - canon_ids: [6 as const], 42 - } as const;
-56
examples/todo-app/src/generated/todos/delete-todo.ts
··· 1 - import { Hono } from 'hono'; 2 - import Database from 'better-sqlite3'; 3 - import { readFileSync, writeFileSync, existsSync } from 'node:fs'; 4 - import { join } from 'node:path'; 5 - 6 - // Simple database setup 7 - const dbPath = join(process.cwd(), 'data.db'); 8 - const db = new Database(dbPath); 9 - 10 - // Migration tracking 11 - const migrationsPath = join(process.cwd(), 'migrations.json'); 12 - let appliedMigrations: string[] = []; 13 - 14 - if (existsSync(migrationsPath)) { 15 - appliedMigrations = JSON.parse(readFileSync(migrationsPath, 'utf-8')); 16 - } 17 - 18 - function registerMigration(name: string, sql: string) { 19 - if (!appliedMigrations.includes(name)) { 20 - db.exec(sql); 21 - appliedMigrations.push(name); 22 - writeFileSync(migrationsPath, JSON.stringify(appliedMigrations, null, 2)); 23 - } 24 - } 25 - 26 - // Register table migration 27 - registerMigration('todos', ` 28 - CREATE TABLE IF NOT EXISTS todos ( 29 - id INTEGER PRIMARY KEY AUTOINCREMENT, 30 - title TEXT NOT NULL, 31 - completed BOOLEAN NOT NULL DEFAULT 0, 32 - created_at TEXT NOT NULL DEFAULT (datetime('now')), 33 - updated_at TEXT NOT NULL DEFAULT (datetime('now')) 34 - ) 35 - `); 36 - 37 - const router = new Hono(); 38 - 39 - // Delete todo 40 - router.delete('/:id', (c) => { 41 - const id = c.req.param('id'); 42 - const existing = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 43 - if (!existing) return c.json({ error: 'Todo not found' }, 404); 44 - db.prepare('DELETE FROM todos WHERE id = ?').run(id); 45 - return c.body(null, 204); 46 - }); 47 - 48 - export default router; 49 - 50 - /** @internal Phoenix VCS traceability — do not remove. */ 51 - export const _phoenix = { 52 - iu_id: '125a8c73ae939f4ba0acfe73d08b7124f40105d27ce351aff1edc6278e5440e9', 53 - name: 'Delete Todo', 54 - risk_tier: 'low', 55 - canon_ids: [3 as const], 56 - } as const;
-54
examples/todo-app/src/generated/todos/error-handling.ts
··· 1 - import { Hono } from 'hono'; 2 - import { z } from 'zod'; 3 - 4 - const router = new Hono(); 5 - 6 - // Global error handler middleware 7 - router.onError((err, c) => { 8 - console.error('Unhandled error:', err); 9 - return c.json({ error: 'Internal server error' }, 500); 10 - }); 11 - 12 - // Middleware to handle invalid JSON 13 - router.use('*', async (c, next) => { 14 - if (c.req.method === 'POST' || c.req.method === 'PUT' || c.req.method === 'PATCH') { 15 - const contentType = c.req.header('content-type'); 16 - if (contentType && contentType.includes('application/json')) { 17 - try { 18 - await c.req.json(); 19 - } catch (error) { 20 - return c.json({ error: 'Invalid JSON in request body' }, 400); 21 - } 22 - } 23 - } 24 - await next(); 25 - }); 26 - 27 - // Helper function to format Zod validation errors 28 - export function formatValidationError(error: z.ZodError): string { 29 - const issues = error.issues.map(issue => { 30 - const path = issue.path.length > 0 ? ` at ${issue.path.join('.')}` : ''; 31 - return `${issue.message}${path}`; 32 - }); 33 - return issues.join(', '); 34 - } 35 - 36 - // Helper function to handle validation failures 37 - export function handleValidationError(result: z.SafeParseError<any>) { 38 - return { error: formatValidationError(result.error) }; 39 - } 40 - 41 - // Helper function to create standardized error responses 42 - export function createErrorResponse(message: string, status: number = 400) { 43 - return { error: message }; 44 - } 45 - 46 - export default router; 47 - 48 - /** @internal Phoenix VCS traceability — do not remove. */ 49 - export const _phoenix = { 50 - iu_id: '1c24c3f052838c88bb9df110ee8d74cace83ad14ca7788e1dea362979cc526b9', 51 - name: 'Error Handling', 52 - risk_tier: 'low', 53 - canon_ids: [3 as const], 54 - } as const;
-37
examples/todo-app/src/generated/todos/get-todo.ts
··· 1 - import { Hono } from 'hono'; 2 - import Database from 'better-sqlite3'; 3 - import { z } from 'zod'; 4 - 5 - // Initialize database 6 - const db = new Database('todos.db'); 7 - 8 - // Register table migration 9 - db.exec(` 10 - CREATE TABLE IF NOT EXISTS todos ( 11 - id INTEGER PRIMARY KEY AUTOINCREMENT, 12 - title TEXT NOT NULL, 13 - description TEXT NOT NULL DEFAULT '', 14 - completed BOOLEAN NOT NULL DEFAULT 0, 15 - created_at TEXT NOT NULL DEFAULT (datetime('now')), 16 - updated_at TEXT NOT NULL DEFAULT (datetime('now')) 17 - ) 18 - `); 19 - 20 - const router = new Hono(); 21 - 22 - // Get single todo 23 - router.get('/:id', (c) => { 24 - const todo = db.prepare('SELECT * FROM todos WHERE id = ?').get(c.req.param('id')); 25 - if (!todo) return c.json({ error: 'Todo not found' }, 404); 26 - return c.json(todo); 27 - }); 28 - 29 - export default router; 30 - 31 - /** @internal Phoenix VCS traceability — do not remove. */ 32 - export const _phoenix = { 33 - iu_id: '816a02acf9b0b67d12cff867447f8fefa205fae6ab5339d68df019eaed49590a', 34 - name: 'Get Todo', 35 - risk_tier: 'low', 36 - canon_ids: [2 as const], 37 - } as const;
+1 -6
examples/todo-app/src/generated/todos/index.ts
··· 5 5 * Barrel export for all Todos modules. 6 6 */ 7 7 8 - export * as createTodo from './create-todo.js'; 9 - export * as deleteTodo from './delete-todo.js'; 10 - export * as errorHandling from './error-handling.js'; 11 - export * as getTodo from './get-todo.js'; 12 - export * as listTodos from './list-todos.js'; 13 - export * as updateTodo from './update-todo.js'; 8 + export * as todosResource from './todos-resource.js';
-38
examples/todo-app/src/generated/todos/list-todos.ts
··· 1 - import { Hono } from 'hono'; 2 - import Database from 'better-sqlite3'; 3 - import { z } from 'zod'; 4 - 5 - // Initialize database 6 - const db = new Database('todos.db'); 7 - 8 - // Register table migration 9 - const registerMigration = (name: string, sql: string) => { 10 - db.exec(sql); 11 - }; 12 - 13 - registerMigration('todos', ` 14 - CREATE TABLE IF NOT EXISTS todos ( 15 - id INTEGER PRIMARY KEY AUTOINCREMENT, 16 - title TEXT NOT NULL, 17 - completed INTEGER NOT NULL DEFAULT 0, 18 - created_at TEXT NOT NULL DEFAULT (datetime('now')) 19 - ) 20 - `); 21 - 22 - const router = new Hono(); 23 - 24 - // List all todos 25 - router.get('/', (c) => { 26 - const todos = db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all(); 27 - return c.json(todos); 28 - }); 29 - 30 - export default router; 31 - 32 - /** @internal Phoenix VCS traceability — do not remove. */ 33 - export const _phoenix = { 34 - iu_id: 'b4ca84ec9f97151d78e179fd1d2450329d547e78e438c71413713fe74fb940d5', 35 - name: 'List Todos', 36 - risk_tier: 'low', 37 - canon_ids: [2 as const], 38 - } as const;
+106
examples/todo-app/src/generated/todos/todos-resource.ts
··· 1 + import { Hono } from 'hono'; 2 + import { db, registerMigration } from '../../db.js'; 3 + import { z } from 'zod'; 4 + 5 + registerMigration('todos', ` 6 + CREATE TABLE IF NOT EXISTS todos ( 7 + id INTEGER PRIMARY KEY AUTOINCREMENT, 8 + title TEXT NOT NULL, 9 + completed INTEGER NOT NULL DEFAULT 0, 10 + created_at TEXT NOT NULL DEFAULT (datetime('now')) 11 + ) 12 + `); 13 + 14 + const CreateTodoSchema = z.object({ 15 + title: z.string().min(1, 'Title must not be empty').max(200, 'Title must not exceed 200 characters'), 16 + }); 17 + 18 + const UpdateTodoSchema = z.object({ 19 + title: z.string().min(1, 'Title must not be empty').max(200, 'Title must not exceed 200 characters').optional(), 20 + completed: z.number().int().min(0).max(1).optional(), 21 + }); 22 + 23 + const router = new Hono(); 24 + 25 + // List all todos 26 + router.get('/', (c) => { 27 + const todos = db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all(); 28 + return c.json(todos); 29 + }); 30 + 31 + // Get single todo 32 + router.get('/:id', (c) => { 33 + const todo = db.prepare('SELECT * FROM todos WHERE id = ?').get(c.req.param('id')); 34 + if (!todo) return c.json({ error: 'Not found' }, 404); 35 + return c.json(todo); 36 + }); 37 + 38 + // Create todo 39 + router.post('/', async (c) => { 40 + let body; 41 + try { 42 + body = await c.req.json(); 43 + } catch { 44 + return c.json({ error: 'Invalid JSON request body' }, 400); 45 + } 46 + 47 + const result = CreateTodoSchema.safeParse(body); 48 + if (!result.success) { 49 + return c.json({ error: result.error.issues[0].message }, 400); 50 + } 51 + 52 + const { title } = result.data; 53 + const info = db.prepare('INSERT INTO todos (title) VALUES (?)').run(title); 54 + const todo = db.prepare('SELECT * FROM todos WHERE id = ?').get(info.lastInsertRowid); 55 + return c.json(todo, 201); 56 + }); 57 + 58 + // Update todo 59 + router.patch('/:id', async (c) => { 60 + const id = c.req.param('id'); 61 + const existing = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 62 + if (!existing) return c.json({ error: 'Not found' }, 404); 63 + 64 + let body; 65 + try { 66 + body = await c.req.json(); 67 + } catch { 68 + return c.json({ error: 'Invalid JSON request body' }, 400); 69 + } 70 + 71 + const result = UpdateTodoSchema.safeParse(body); 72 + if (!result.success) { 73 + return c.json({ error: result.error.issues[0].message }, 400); 74 + } 75 + 76 + const updates = result.data; 77 + if (updates.title !== undefined) { 78 + db.prepare('UPDATE todos SET title = ? WHERE id = ?').run(updates.title, id); 79 + } 80 + if (updates.completed !== undefined) { 81 + db.prepare('UPDATE todos SET completed = ? WHERE id = ?').run(updates.completed, id); 82 + } 83 + 84 + const updated = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 85 + return c.json(updated); 86 + }); 87 + 88 + // Delete todo 89 + router.delete('/:id', (c) => { 90 + const id = c.req.param('id'); 91 + const existing = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 92 + if (!existing) return c.json({ error: 'Not found' }, 404); 93 + 94 + db.prepare('DELETE FROM todos WHERE id = ?').run(id); 95 + return c.body(null, 204); 96 + }); 97 + 98 + export default router; 99 + 100 + /** @internal Phoenix VCS traceability — do not remove. */ 101 + export const _phoenix = { 102 + iu_id: '9034ad0a11e5572f648cbbbc49401554d7901e78d9b24de3209750fb3a04b1ef', 103 + name: 'Todos Resource', 104 + risk_tier: 'high', 105 + canon_ids: [12 as const], 106 + } as const;
-53
examples/todo-app/src/generated/todos/update-todo.ts
··· 1 - import { Hono } from 'hono'; 2 - import { z } from 'zod'; 3 - import Database from 'better-sqlite3'; 4 - 5 - const db = new Database('todos.db'); 6 - 7 - // Register table migration 8 - db.exec(` 9 - CREATE TABLE IF NOT EXISTS todos ( 10 - id INTEGER PRIMARY KEY AUTOINCREMENT, 11 - title TEXT NOT NULL, 12 - completed BOOLEAN NOT NULL DEFAULT 0, 13 - created_at TEXT NOT NULL DEFAULT (datetime('now')) 14 - ) 15 - `); 16 - 17 - const UpdateTodoSchema = z.object({ 18 - title: z.string().min(1).max(200).optional(), 19 - completed: z.boolean().optional(), 20 - }); 21 - 22 - const router = new Hono(); 23 - 24 - // Update todo 25 - router.patch('/:id', async (c) => { 26 - const id = c.req.param('id'); 27 - const existing = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 28 - if (!existing) return c.json({ error: 'Todo not found' }, 404); 29 - 30 - const result = UpdateTodoSchema.safeParse(await c.req.json()); 31 - if (!result.success) return c.json({ error: result.error.issues[0].message }, 400); 32 - 33 - const updates = result.data; 34 - if (updates.title !== undefined) { 35 - db.prepare('UPDATE todos SET title = ? WHERE id = ?').run(updates.title, id); 36 - } 37 - if (updates.completed !== undefined) { 38 - db.prepare('UPDATE todos SET completed = ? WHERE id = ?').run(updates.completed ? 1 : 0, id); 39 - } 40 - 41 - const updated = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); 42 - return c.json(updated); 43 - }); 44 - 45 - export default router; 46 - 47 - /** @internal Phoenix VCS traceability — do not remove. */ 48 - export const _phoenix = { 49 - iu_id: 'f919a513e6f8d05e248debc46ac84046e17e891472f61fc9c0cf35619ebff8f9', 50 - name: 'Update Todo', 51 - risk_tier: 'high', 52 - canon_ids: [8 as const], 53 - } as const;
+2 -12
examples/todo-app/src/server.ts
··· 3 3 import { runMigrations } from './db.js'; 4 4 5 5 // Generated route modules 6 - import create_todo from './generated/todos/create-todo.js'; 7 - import delete_todo from './generated/todos/delete-todo.js'; 8 - import error_handling from './generated/todos/error-handling.js'; 9 - import get_todo from './generated/todos/get-todo.js'; 10 - import list_todos from './generated/todos/list-todos.js'; 11 - import update_todo from './generated/todos/update-todo.js'; 6 + import todos_resource from './generated/todos/todos-resource.js'; 12 7 13 8 // Mount routes 14 - mount('/todos', create_todo); 15 - mount('/todos', delete_todo); 16 - mount('/todos', error_handling); 17 - mount('/todos', get_todo); 18 - mount('/todos', list_todos); 19 - mount('/todos', update_todo); 9 + mount('/todos', todos_resource); 20 10 21 11 const port = parseInt(process.env.PORT ?? '3000', 10); 22 12 runMigrations();
examples/todo-app/todos.db

This is a binary file and will not be displayed.

+26 -14
src/architectures/sqlite-web-api.ts
··· 89 89 90 90 You are generating a route handler module for a Hono REST API backed by SQLite. 91 91 92 - ### Rules 93 - - Each module exports a Hono router instance as the DEFAULT export. 94 - - Import \`{ Hono }\` from 'hono' for routing. 95 - - Import \`{ db, registerMigration }\` from '../db.js' for database access. 96 - - Import \`{ z }\` from 'zod' for request body validation. 97 - - Use better-sqlite3 synchronous API (db.prepare().run(), .get(), .all()). 98 - - Register your table schema via registerMigration() at module scope. 92 + ### CRITICAL import rules — follow EXACTLY 93 + - Import \`{ Hono }\` from 'hono' 94 + - Import \`{ db, registerMigration }\` from '../../db.js' — the shared database module is TWO levels up from the generated module. 95 + - Import \`{ z }\` from 'zod' 96 + - NEVER create your own Database instance. NEVER write \`new Database(...)\` or \`import Database from 'better-sqlite3'\`. The shared db.js provides the single db connection. 97 + - NEVER define your own Hono class or Database type. Import them from the packages. 98 + 99 + ### Module structure 100 + - Export a Hono router instance as the DEFAULT export: \`export default router;\` 101 + - Register your table schema via \`registerMigration('tablename', 'CREATE TABLE IF NOT EXISTS ...')\` at module scope. 102 + - Include ALL CRUD routes for the resource in a single module (GET list, GET by id, POST create, PATCH update, DELETE). 103 + - Use better-sqlite3 synchronous API: db.prepare(sql).run(), .get(), .all() 99 104 - Use parameterized queries ALWAYS — never interpolate user input into SQL. 100 - - Return proper HTTP status codes: 200 (ok), 201 (created), 204 (no content), 400 (bad request), 404 (not found). 101 - - Return JSON for all responses. Errors: { error: "message" }. 102 - - Parse request bodies with c.req.json() and validate with Zod. 103 - - Use integer primary keys with AUTOINCREMENT for IDs. 104 - - Include created_at/updated_at timestamps where the spec mentions them. 105 - - Export the _phoenix metadata constant as required. 105 + - Parse request bodies with \`await c.req.json()\` and validate with Zod .safeParse(). 106 + 107 + ### Response conventions 108 + - 200 for successful reads 109 + - 201 for successful creates (return the created resource) 110 + - 204 for successful deletes (return c.body(null, 204)) 111 + - 400 for validation errors: \`{ error: "message" }\` 112 + - 404 for not found: \`{ error: "Not found" }\` 113 + 114 + ### Data model 115 + - Use integer primary keys with AUTOINCREMENT. 116 + - Include a \`completed\` boolean column (as INTEGER 0/1) for task/todo resources when the spec mentions it. 117 + - Include \`created_at TEXT NOT NULL DEFAULT (datetime('now'))\` for timestamps. 106 118 `; 107 119 108 120 const CODE_EXAMPLES = ` ··· 113 125 \`\`\`typescript 114 126 import { Hono } from 'hono'; 115 127 import { z } from 'zod'; 116 - import { db, registerMigration } from '../db.js'; 128 + import { db, registerMigration } from '../../db.js'; 117 129 118 130 // Register table migration 119 131 registerMigration('notes', \`
+15
src/llm/prompt.ts
··· 113 113 lines.push(''); 114 114 } 115 115 116 + // Related context: DEFINITION and CONTEXT nodes from the same spec not in this IU 117 + if (arch) { 118 + const otherNodes = canonNodes.filter(n => 119 + !iu.source_canon_ids.includes(n.canon_id) && 120 + (n.type === 'DEFINITION' || n.type === 'CONTEXT') 121 + ); 122 + if (otherNodes.length > 0) { 123 + lines.push('## Related Context (from other sections of the same spec)'); 124 + for (const n of otherNodes) { 125 + lines.push(`- [${n.type}] ${n.statement}`); 126 + } 127 + lines.push(''); 128 + } 129 + } 130 + 116 131 // Contract 117 132 if (iu.contract.inputs.length > 0) { 118 133 lines.push(`## Inputs: ${iu.contract.inputs.join(', ')}`);