Skip to content

Commit 22536ed

Browse files
committed
Refactor: Implement Dual-Scope Routing paradigm, UI mobile responsiveness, and DO HoniClient abstraction
1 parent 0768bd4 commit 22536ed

851 files changed

Lines changed: 174357 additions & 23815 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Rule: Dual-Scope Routing Paradigm
2+
3+
## Overview
4+
5+
This application enforces a strict **Dual-Scope Paradigm** for all views and API calls.
6+
Every new route, component, and API endpoint must declare which scope it belongs to.
7+
8+
---
9+
10+
## Scope Definitions
11+
12+
### 🌐 Global Scope
13+
- **URL prefix:** `/` (no `/repos/:owner/:repo` prefix)
14+
- **Purview:** cross-repository; holistic planning; master dashboards; settings
15+
- **Route file:** `src/frontend/src/routes/GlobalRoutes.tsx`
16+
- **API pattern:** `/api/projects`, `/api/global/*`, `/api/tasks`, `/api/settings`
17+
- **Examples:** `/projects`, `/kanban`, `/roadmap`, `/dashboard`, `/chat`, `/workshop`
18+
19+
### 🗂️ Active Workspace (Repo-Specific) Scope
20+
- **URL prefix:** `/repos/:owner/:repo/...`
21+
- **Purview:** strictly confined to a single selected GitHub `owner/repo`
22+
- **Route file:** `src/frontend/src/routes/RepoRoutes.tsx`
23+
- **API pattern:** `/api/repos/:owner/:repo/*`
24+
- **Examples:** `/repos/jmbish04/core-github-api/plan`, `.../prs`, `.../explorer`
25+
26+
---
27+
28+
## Rule 1 — Scope Declaration Before Implementation
29+
30+
**When adding any new view, you MUST first determine its scope:**
31+
32+
```
33+
Is this view useful regardless of which repo is selected?
34+
→ YES → Global Scope → add to GlobalRoutes.tsx
35+
→ NO → Repo Scope → add to RepoRoutes.tsx
36+
```
37+
38+
Never add a route without explicitly declaring its scope in a comment:
39+
```tsx
40+
// SCOPE: GLOBAL — shows PRs across all watched repos
41+
<Route path="/pr-center" element={<PRCommandCenter />} />
42+
43+
// SCOPE: REPO — shows PRs only for the active owner/repo workspace
44+
<Route path="prs" element={<RepoPRs />} />
45+
```
46+
47+
---
48+
49+
## Rule 2 — React Router v6 Relative Paths (MANDATORY)
50+
51+
**Never repeat the parent path in a nested child route.**
52+
53+
```tsx
54+
// ❌ WRONG — resolves to /repos/:owner/:repo/repos/:owner/:repo/plan
55+
<Route path="/repos/:owner/:repo" element={<RepoLayout />}>
56+
<Route path="repos/:owner/:repo/plan" element={<RepoPlan />} />
57+
</Route>
58+
59+
// ✅ CORRECT — resolves to /repos/:owner/:repo/plan
60+
<Route path="/repos/:owner/:repo" element={<RepoLayout />}>
61+
<Route path="plan" element={<RepoPlan />} />
62+
</Route>
63+
```
64+
65+
**Checklist for every child route inside `RepoRoutes.tsx`:**
66+
- [ ] Path does NOT start with `/`
67+
- [ ] Path does NOT contain `repos/:owner/:repo`
68+
- [ ] Path is a relative segment only (e.g., `"plan"`, `"projects/kanban"`)
69+
70+
---
71+
72+
## Rule 3 — Hono API Scope Validation
73+
74+
All Hono router definitions must enforce scope validation with Zod.
75+
76+
### Global API endpoints
77+
```typescript
78+
// src/backend/src/routes/api/global/projects.ts
79+
app.get("/api/projects", zValidator("query", GlobalProjectQuerySchema), handler);
80+
```
81+
82+
### Repo-scoped API endpoints
83+
Every repo-scoped route must validate `owner` and `repo` using a shared Zod schema:
84+
85+
```typescript
86+
// src/backend/src/routes/api/repos/[owner]/[repo]/projects.ts
87+
const RepoParamsSchema = z.object({
88+
owner: z.string().min(1),
89+
repo: z.string().min(1),
90+
});
91+
92+
app.get("/api/repos/:owner/:repo/projects",
93+
zValidator("param", RepoParamsSchema),
94+
async (c) => { /* ... */ }
95+
);
96+
```
97+
98+
**Never** serve repo-scoped data from a global endpoint.
99+
**Never** call a global endpoint from a component inside `RepoRoutes.tsx`.
100+
101+
---
102+
103+
## Rule 4 — File Placement
104+
105+
| Artifact | Global scope | Repo scope |
106+
|---|---|---|
107+
| Route definition | `src/frontend/src/routes/GlobalRoutes.tsx` | `src/frontend/src/routes/RepoRoutes.tsx` |
108+
| View component | `src/frontend/src/views/control/global/` | `src/frontend/src/views/repos/` |
109+
| Hono API handler | `src/backend/src/routes/api/...` | `src/backend/src/routes/api/repos/...` |
110+
| TanStack Query hook | uses `/api/*` | uses `/api/repos/:owner/:repo/*` |
111+
| Context dependency | no repo context needed | must consume `useRepoContext()` |
112+
113+
---
114+
115+
## Rule 5 — App.tsx Is a Composition Layer Only
116+
117+
`App.tsx` must remain a thin provider + route composition layer.
118+
It imports `GlobalRoutes` and `RepoRoutes` and renders them inside `<Routes>`.
119+
**No route definitions belong directly in `App.tsx`.**
120+
121+
```tsx
122+
// ✅ Correct App.tsx pattern
123+
function App() {
124+
return (
125+
<AuthProvider>
126+
<BrowserRouter>
127+
<Routes>
128+
{GlobalRoutes()}
129+
{RepoRoutes()}
130+
</Routes>
131+
</BrowserRouter>
132+
</AuthProvider>
133+
);
134+
}
135+
```

.agent/rules/02-do-abstraction.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Rule: Durable Object Abstraction Pattern
2+
3+
## Context
4+
Our backend architecture standardizes Durable Object interactions into two distinct paradigms: AI Agents and WebSocket Broadcasters. Raw mounting of Durable Objects using `idFromName` and `.get()` causes type ambiguity, routing inconsistencies, and runtime errors.
5+
6+
## Core Directives
7+
8+
### 1. Raw DO Instantiation is Forbidden
9+
- **NEVER** use `namespace.idFromName('name')` or `namespace.get(id)` directly within routing files, utility functions, or workflow handlers.
10+
- **NEVER** instantiate raw `Request` objects targeted at `http://agent/...` without a client utility.
11+
12+
### 2. The Agent Paradigm (HoniClient)
13+
All stateful AI Agents in this system are built using the `honidev` framework.
14+
- **RPC Access**: When you need to interact directly with an agent's internal methods (e.g., `stub.chat()`, `stub.workflowComplete()`), you **MUST** use `HoniClient.getStub()`.
15+
- **HTTP Access**: When you need to send an HTTP request to an agent's internal Hono router, you **MUST** use `HoniClient.fetch()`.
16+
- **Import Path**: `import { HoniClient } from '@utils/honi-client';`
17+
18+
### 3. The Broadcast Paradigm (BroadcastClient)
19+
WebSocket broadcasters (e.g., `ROOM_DO`, `JulesWebhookBroadcaster`) are stateful but are not AI agents.
20+
- **Usage**: When dispatching broadcasts or checking room presence, use the unified `BroadcastClient` utility to construct the request.
21+
22+
## Enforcement
23+
This architectural standard replaces legacy utilities such as `getAgentByName` and `routeAgentRequest`, which have been removed from the codebase. Any attempt to reinvent DO mounting wrappers will be blocked during code review.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Rule: Mobile-First Responsive Design
2+
3+
## Core Directive
4+
All UI generation and modification must adhere to a strict mobile-first responsive design pattern. The application shell (Sidebar) handles its own responsive state, but all internal page views and components must adapt smoothly to smaller viewports.
5+
6+
## 1. Container Widths (Fluid Layouts)
7+
- **NEVER** hardcode fixed pixel widths for layout containers (e.g., do not use `w-[800px]`).
8+
- **ALWAYS** use Tailwind's responsive utility classes such as `w-full`, `max-w-7xl`, or percentage-based widths that scale up on larger breakpoints (e.g., `w-full md:w-1/2`).
9+
10+
## 2. Flex and Grid Layouts
11+
- All grid and flex layouts MUST specify behavior for the base (mobile) breakpoint and scale up using `md:` or `lg:` prefixes.
12+
- **Example:** Use `flex-col md:flex-row` instead of just `flex`.
13+
- **Example:** Use `grid-cols-1 md:grid-cols-3` instead of `grid-cols-3`.
14+
15+
## 3. Data Tables and Wide Elements
16+
- Data tables, grids, and abnormally wide elements MUST be wrapped in an `overflow-x-auto` container.
17+
- This prevents the table from breaking the mobile viewport width, allowing users to scroll horizontally while the rest of the page remains contained.
18+
19+
## 4. Touch Targets
20+
- Ensure buttons, links, and inputs are adequately sized for mobile touch interaction. Do not squish interactive elements.

.agent/rules/cloudflare-deployments.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ When operating on Cloudflare Workers that exceed 5MB uncompressed:
88
2. Never rely on the default `wrangler deploy` CLI command directly if timeouts occur; wrap it in a Node script that overrides the `undici` global dispatcher timeout to at least 120,000ms.
99
3. Inject `NODE_OPTIONS=--max-old-space-size=8192` to prevent heap exhaustion during `esbuild` minification phases.
1010
4. Ensure `compatibility_flags = ["nodejs_compat"]` is set and `node_compat = false` is enforced to prevent polyfill bloat.
11+
12+
## Rule: Cloudflare Bindings Philosophy
13+
14+
- **Naming Convention:** When interacting with the Cloudflare API, be aware that `script_name` refers to the `worker_name` defined in `wrangler.jsonc` or `wrangler.toml`.
15+
- **Create & Patch Only:** The automated bindings manager is designed to **provision** resources (e.g., creating a D1 database) and **patch** the repository's `wrangler.jsonc` via a GitHub PR.
16+
- **No Direct Attachment:** Do **NOT** attempt to attach bindings directly to the Worker script via the Cloudflare API. The binding manager stops at PR creation, allowing the native CI/CD deployment pipeline to handle the actual attachment upon merge.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Cloudflare Worker Standards (2026)
2+
3+
- **Routing & Validation**: All APIs must exclusively utilize `Hono` alongside `@hono/zod-openapi` to enforce strict request schemas and automate documentation generation.
4+
- **OpenAPI Enforcement**: Every Worker project must host `/openapi.json` (OpenAPI v3.1.0) and include a mounted `/scalar` and `/swagger` viewer UI.
5+
- **AI Operations**: Standardize on the official `openai` SDK. Connections must be instantiated securely by configuring the `baseURL` to point directly to Cloudflare AI Gateway.
6+
- **Types & Configuration**: Environment typing is maintained strictly through `wrangler.jsonc` (not `wrangler.toml`), and synchronized via `wrangler types`. Manual mapping of types is deprecated.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
description: Strict governance rules for D1 database instances, Drizzle ORM usage, schema changes, and migration management in the core-github-api project.
3+
---
4+
5+
# D1 & Drizzle ORM Governance Rules
6+
7+
## Table Instance Ownership
8+
9+
Every table in this project belongs to exactly ONE D1 binding. When in doubt, check this table first:
10+
11+
| D1 Binding | Migration Config | Owns |
12+
|-----------|-----------------|------|
13+
| `DB` | `drizzle.config.core.ts``migrations/core/` | All application logic tables: `system_logs`, `audit_logs`, `automation_logs`, `repos`, `prs`, `reviews`, `health_*`, `cloudflare_changelog`, `jules_*`, `discord_*`, `agent_*`, `project_*` |
14+
| `DB_WEBHOOKS` | `drizzle.config.webhooks.ts``migrations/webhooks/` | Raw GitHub event tables ONLY: `webhook_deliveries`, `pull_request`, `push`, `check_run`, `workflow_run`, `webhook_configs`, `searches`, `repoAnalysis`, `dailyTrends`, `trendingRepos` |
15+
16+
## ORM Client Selection
17+
18+
| Situation | Client to Use | Import |
19+
|-----------|--------------|--------|
20+
| Reading/writing any table on `DB` | `getDb(env.DB)` | `import { getDb } from '@db'` |
21+
| Reading/writing any table on `DB_WEBHOOKS` | `getWebhooksDb(env.DB_WEBHOOKS)` | `import { getWebhooksDb } from '@db'` |
22+
| ❌ FORBIDDEN | `drizzle(env.DB)` or `drizzle(env.DB_WEBHOOKS)` | Never — always pass schema via the getters |
23+
24+
## Pre-Table-Creation Protocol (MANDATORY)
25+
26+
Before creating any new Drizzle table:
27+
28+
1. **Scan existing tables** — run the following and read the relevant schema files:
29+
```bash
30+
grep -r "sqliteTable" src/backend/src/db/schemas/ --include="*.ts" -l
31+
```
32+
2. **Evaluate reuse** — can you add a column to an existing table? Is there a table in a different domain that serves this purpose?
33+
3. **Only if no existing table fits** — create a new one
34+
4. **Assign to correct D1 instance** — use the ownership table above to determine which Drizzle config and migration dir to use
35+
5. **Add to the correct barrel** — update the domain's `index.ts` so it is picked up by `schema.ts`
36+
37+
## Schema File Locations
38+
39+
```
40+
src/backend/src/db/
41+
├── schema.ts ← master barrel (all domains, used by DB/core)
42+
├── schemas/
43+
│ ├── index.ts ← re-exports all domains
44+
│ ├── agents/
45+
│ ├── app/ ← cloudflare_changelog, etc.
46+
│ ├── discord/
47+
│ ├── github/
48+
│ │ └── webhooks.ts ← webhook event tables (owned by DB_WEBHOOKS)
49+
│ ├── logs/ ← system_logs, audit_logs, health_* (owned by DB)
50+
│ ├── ops/
51+
│ ├── webhooks/
52+
│ │ └── automations.ts ← automation_logs, webhook_configs
53+
│ └── ...
54+
└── index.ts ← exports getDb() and getWebhooksDb()
55+
```
56+
57+
## Migration Discipline
58+
59+
- **NEVER** edit files in `migrations/core/` or `migrations/webhooks/` directly
60+
- Generate: `pnpm run db:generate:core` or `pnpm run db:generate:webhooks`
61+
- Apply: `pnpm run migrate:remote:core` or `pnpm run migrate:remote:webhooks`
62+
- Full reset: `pnpm run db:reset` (creates fresh DB instances, archives old migrations)
63+
- Manual repair is ONLY permitted if a migration script fails AND the user explicitly authorizes it
64+
65+
## Fresh Instance Reset Protocol
66+
67+
When you need a clean slate (structural errors, wrong table locations, D1 corruption):
68+
69+
```bash
70+
pnpm run db:reset
71+
# Then, after deploy completes:
72+
pnpm run db:seed:prep # prepare seed files from pre-delete export
73+
pnpm run db:seed:run # apply seeds to fresh instances
74+
```
75+
76+
### What `pnpm run db:reset` does:
77+
1. Reads current D1 UUIDs **dynamically from `wrangler.jsonc`** — no hardcoded constants
78+
2. Exports all row data to `scripts/db/data_exports/{timestamp}/` (SQL + JSON) before deletion
79+
3. Deletes old D1 instances via CF REST API
80+
4. Creates new fresh instances with canonical names
81+
5. Updates `wrangler.jsonc` with new UUIDs
82+
6. Archives old migration history to `migrations/_archive/`
83+
7. Chains: `db:generate:all → migrate:remote:all → deploy`
84+
85+
### Seeding After Reset:
86+
87+
**Step 1:** `pnpm run db:seed:prep [-- --export-dir scripts/db/data_exports/TIMESTAMP]`
88+
- Reads the JSON export from the pre-delete backup
89+
- Applies truncation limits (e.g. `system_logs` → last 2000 rows, `webhook_deliveries` → last 500)
90+
- Chunks INSERT statements to stay within D1 limits: 100 bound params/query, 90 KB/statement
91+
- Writes `scripts/db/seeds/{timestamp}/DB.seed.sql` and `DB_WEBHOOKS.seed.sql`
92+
93+
**Step 2:** `pnpm run db:seed:run [-- --seeds-dir scripts/db/seeds/TIMESTAMP]`
94+
- Tries bulk `--file` execution first (fastest)
95+
- Falls back to statement-by-statement with D1 error keyword detection
96+
- Retries on transient overload, aborts on fatal errors (column_notfound, schema mismatch)
97+
- Prints instructive fix guidance for every known D1 error type
98+
99+
### D1 Execution Limits (Hardcoded in scripts as source of truth):
100+
| Limit | Value | Source |
101+
|-------|-------|--------|
102+
| Max bound params per query | 100 | CF hard limit |
103+
| Max SQL statement length | 100 KB (scripts use 90 KB) | CF hard limit |
104+
| Max query duration | 30 seconds | CF hard limit |
105+
| Safe INSERT batch size | 100 rows | CF recommendation |
106+
| Max D1 database size | 10 GB | CF hard limit |
107+
108+
⚠️ **Do NOT put seed files in `migrations/` directories** — wrangler will try to apply them as migrations.
109+
110+
## Health Monitors for D1 Staleness
111+
112+
Three health checks run as part of `POST /api/health/run`:
113+
114+
| Check ID | File | What it detects |
115+
|----------|------|-----------------|
116+
| `webhook_staleness` | `health/checks/webhook-staleness.ts` | `webhook_deliveries` freshness vs latest GitHub API event (fails if >24h lag or empty) |
117+
| `log_staleness` | `health/checks/log-staleness.ts` | `system_logs` freshness (fails if empty or >1 day old) |
118+
| `d1_table_scan` | `health/checks/d1-table-scan.ts` | All tables in both instances — flags empty (0 rows) or stale (>30 days) |
119+
120+
To manually check D1 staleness at any time:
121+
```bash
122+
# Quick row count check
123+
wrangler d1 execute DB --remote --command "SELECT count(*) FROM system_logs;"
124+
wrangler d1 execute DB_WEBHOOKS --remote --command "SELECT count(*) FROM webhook_deliveries;"
125+
126+
# Or trigger the full health suite
127+
curl -X POST https://core-github-api.hacolby.workers.dev/api/health/run | jq '.results[] | select(.name | test("Staleness|Table Scan"))'
128+
```
129+
130+
## Adding a New Table — Checklist
131+
132+
```
133+
[ ] Scanned existing schemas, no suitable table found
134+
[ ] Determined correct D1 instance (DB vs DB_WEBHOOKS)
135+
[ ] Created file in correct schemas/<domain>/ directory
136+
[ ] Exported from schemas/<domain>/index.ts
137+
[ ] Ran pnpm run db:generate:<core|webhooks>
138+
[ ] Reviewed generated migration SQL (do not edit it)
139+
[ ] Ran pnpm run migrate:remote:<core|webhooks>
140+
```

.agent/rules/error-handling.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ When a frontend component catches an error, you **MUST** pass the literal error
2828
handleGlobalError(err);
2929
}
3030
```
31-
- **Forbidden**: Do not use `console.error(err)` as the sole failure mechanism. Do not write local `<Alert variant="destructive">` blocks for API failures unless it's a localized form validation constraint.
31+
- **Forbidden**: Do not use `console.error(err)` or raw `toast.error()` directly for system or API errors. Do not write local `<Alert variant="destructive">` blocks for API failures unless it's a localized form validation constraint. Use `handleGlobalError()` exclusively, which handles deduplication and backend metric dispatching automatically.
3232

3333
## 3. Strict Passthrough
3434
Agents must ensure that backend routes return the actual error string generated by the failed service (e.g. GitHub API 404s, Stripe 402s).

0 commit comments

Comments
 (0)