Commitd0bc8c2unknown_key
feat: add session memory system for agent continuity
feat: add session memory system for agent continuity Constitution-style CLAUDE.md with 10 sections: session start/end protocols, hard rules (never forget context, never idle, never ship broken), escape hatches, drift prevention, competitive intelligence. Memory infrastructure: - .memory/ directory with project-state.md, last-session.md, decisions-log.md, open-questions.md for human-readable state - src/lib/memory.ts — persistent key-value memory store backed by DB with in-memory cache for hot-path lookups - src/routes/memory.ts — REST API for memory read/write (GET /api/memory/recall, POST /api/memory/store) Every future session reads memory on start, writes on end. https://claude.ai/code/session_01M6EgoTaZ6HAZiVu77xp11g
8 files changed+485−5d0bc8c2fb71a80b4c14d94af17265b555b0403e5
8 changed files+485−5
Added.memory/decisions-log.md+46−0View fileUnifiedSplit
@@ -0,0 +1,46 @@
1# GlueCron — Decisions Log
2
3> Append-only. Never delete entries. Each decision records what, why, and any constraints.
4
5## 2026-04-16
6
7### D001: Fly.io for hosting
8- **Decision:** Deploy on Fly.io instead of Hetzner/Render/Railway
9- **Why:** Persistent volumes for git repos, auto-migration on release, London region, simple Dockerfile deploy. Hetzner CLI was unusable. Render doesn't support persistent volumes well. Railway lacks volume persistence.
10- **Constraint:** Single server for now (shared-cpu-1x, 512MB). Scale vertically first.
11
12### D002: Bun runtime (not Node.js)
13- **Decision:** Bun for all runtime, testing, and package management
14- **Why:** 2-4x faster than Node.js, native TypeScript, built-in test runner, faster installs
15- **Constraint:** Some TypeScript strictness issues with Uint8Array (Bun uses ArrayBufferLike)
16
17### D003: Server-side rendering only (no client JS framework)
18- **Decision:** Hono JSX for all rendering, no React/Vue/Svelte client-side
19- **Why:** Fastest possible page loads, zero JS bundle, simpler architecture
20- **Constraint:** Limits interactivity. SSE provides real-time updates without full SPA.
21- **Revisit when:** User demand for rich interactions exceeds what SSR+SSE can deliver
22
23### D004: Neon PostgreSQL (serverless)
24- **Decision:** Neon for database, Drizzle ORM for schema/queries
25- **Why:** Serverless scaling, branching for dev, no connection pool management
26- **Constraint:** Cold starts on first query per session. Mitigated by connection caching.
27
28### D005: Flywheel over static rules
29- **Decision:** AI reviews learn from historical outcomes instead of static rule files
30- **Why:** Rules rot. The flywheel improves automatically as developers accept/reject suggestions.
31- **Constraint:** Needs minimum ~20 review outcomes before patterns emerge. Cold start is stateless.
32
33### D006: Free SBOM export
34- **Decision:** Ship SPDX + CycloneDX for free, no paywall
35- **Why:** GitHub gates this behind enterprise pricing. Free SBOM is a differentiator.
36- **Constraint:** Currently only parses declared dependencies, not transitive.
37
38### D007: GateTest as self-healing loop (planned)
39- **Decision:** GateTest will continuously test, auto-fix, and resubmit
40- **Why:** Zero human intervention for routine failures. The platform heals itself.
41- **Constraint:** Need to define repair boundaries — what can be auto-fixed vs what needs human review.
42
43### D008: Memory system for agent continuity
44- **Decision:** Markdown state files + enhanced CLAUDE.md session protocols
45- **Why:** Context loss across sessions causes repeated work. Memory files bridge the gap.
46- **Constraint:** Files must be kept under 500 lines each to fit in agent context windows.
Added.memory/last-session.md+31−0View fileUnifiedSplit
@@ -0,0 +1,31 @@
1# GlueCron — Last Session Summary
2
3> Overwritten each session. Previous session's content moves to session-history.md.
4
5## Session Date: 2026-04-16
6
7## What Was Built
81. **Flywheel learning system** — 3 new DB tables (review_outcomes, review_patterns, gate_metrics), learning engine at src/lib/flywheel.ts, context injection into AI review prompts. Migration: drizzle/0034_flywheel_learning.sql
92. **SSE real-time streaming** — src/lib/sse.ts (channel pub/sub), src/routes/events.ts (stream endpoint), gate.ts wired to broadcast start/completion events
103. **SBOM export** — src/lib/sbom.ts (SPDX 2.3 + CycloneDX 1.5), download buttons on deps page, API at /api/repos/:owner/:repo/sbom
114. **License compliance scanner** — src/lib/license-scan.ts, visual page at /:owner/:repo/dependencies/licenses, API endpoint
125. **161 TypeScript error fixes** — JSX method casing, git.ts param parsing, repository.ts null safety, graphql.ts visibility→isPrivate, open redirect security fix
136. **Agent policy in CLAUDE.md** — "Never idle" rules encoded for all future sessions
147. **Memory system** — .memory/ directory with project-state.md, decisions-log.md, last-session.md, open-questions.md
15
16## What Was Fixed
17- Open redirect vulnerability in src/routes/auth.tsx (all redirects now through safeRedirect())
18- repositories.visibility references in graphql.ts, packages-api.ts, admin.tsx (field doesn't exist, should be isPrivate)
19- Git route param parsing in src/routes/git.ts (Hono parses /:repo.git as "repo.git" not "repo")
20
21## Branch State
22- Branch: `claude/resume-previous-work-KzyLw`
23- Commits: 6 ahead of main
24- All 767 tests passing
25- 10 remaining TypeScript errors (Bun Uint8Array compat only)
26
27## What's Next (priority order)
281. GateTest self-healing loop — continuous test → auto-fix → resubmit
292. Web Push notification wiring
303. Streaming AI responses through SSE
314. Developer velocity metrics dashboard
Added.memory/open-questions.md+32−0View fileUnifiedSplit
@@ -0,0 +1,32 @@
1# GlueCron — Open Questions
2
3> Items that need owner input or a decision before building.
4
5## Active
6
7### Q001: GateTest repair boundaries
8- **Question:** What types of failures should GateTest auto-fix vs escalate to a human?
9- **Context:** Building the self-healing loop. Auto-fixing lint/format is safe. Auto-fixing logic bugs is risky.
10- **Proposed answer:** Auto-fix: formatting, import ordering, unused variables, missing semicolons, secret redaction, simple type errors. Escalate: logic changes, API contract changes, test failures that require new test logic, security vulnerabilities requiring architectural changes.
11- **Status:** Awaiting confirmation
12
13### Q002: Client-side interactivity timeline
14- **Question:** When does the platform need client-side JS beyond SSE?
15- **Context:** Currently pure SSR. SSE handles real-time. But features like drag-and-drop project boards, inline code editing, and live collaborative review need JS.
16- **Proposed answer:** Build with progressive enhancement — SSR first, sprinkle vanilla JS for specific interactions (code editor, drag-drop). No React/Vue/Svelte until proven necessary.
17- **Status:** Low priority, track demand
18
19### Q003: Pricing model
20- **Question:** Free tier limits, paid tier features, enterprise tier
21- **Context:** SBOM is free (differentiator). What else is free vs paid?
22- **Status:** Not yet discussed
23
24### Q004: Legal review of flywheel
25- **Question:** Owner mentioned needing to review legal implications of the flywheel
26- **Context:** The flywheel learns from code review outcomes. Need to verify: data ownership, privacy (does it learn across repos?), GDPR compliance for EU users.
27- **Proposed answer:** Flywheel patterns are scoped per-repo by default. Global patterns only from aggregated, anonymized data. Users can opt out via repo settings.
28- **Status:** Owner flagged for later review
29
30## Resolved
31
32(none yet)
Added.memory/project-state.md+49−0View fileUnifiedSplit
@@ -0,0 +1,49 @@
1# GlueCron — Project State
2
3> Auto-updated at end of each agent session. Human-readable snapshot.
4
5## Current Status
6
7- **Codebase:** 196 files, ~60,000 lines TypeScript
8- **Tests:** 767 passing across 51 test files
9- **TypeScript errors:** 10 remaining (all Bun Uint8Array compat, non-blocking)
10- **Database:** 53+ tables, 35 migrations (Drizzle + Neon PostgreSQL)
11- **Branch:** `claude/resume-previous-work-KzyLw` (6 commits ahead of main)
12
13## What's Shipped (Blocks A–J complete)
14
15- Full GitHub-parity git hosting (Smart HTTP clone/push/fetch)
16- Issue tracker, PR system with AI review + auto-merge conflict resolution
17- Green gate enforcement (GateTest, secret scan, security scan, merge check, AI review)
18- Auto-repair on gate failures (secrets, security issues)
19- Branch protection, CODEOWNERS, rulesets
20- Flywheel learning system (review outcomes, pattern extraction, context injection)
21- SSE real-time streaming (gate updates, notifications)
22- SBOM export (SPDX 2.3 + CycloneDX 1.5)
23- License compliance scanner
24- OAuth2 + PAT tokens + 2FA/TOTP + WebAuthn passkeys
25- Webhooks, deploy pipelines, CI workflows
26- Wiki, discussions, projects, gists, packages (npm registry)
27- Marketplace, sponsors, billing, org management
28- GraphQL API, admin panel, audit log
29
30## Active Gaps
31
32- No GateTest self-healing loop (continuous test → fix → resubmit)
33- Web Push notifications (infrastructure exists, needs final wiring)
34- Streaming AI responses via SSE
35- Developer velocity metrics dashboard
36- No client-side JavaScript interactivity (pure SSR currently)
37- Mobile experience not optimized
38
39## Deployment
40
41- **Target:** Fly.io (London region, persistent volume for repos)
42- **Dockerfile:** Multi-stage, Bun runtime, auto-migration on release
43- **Not yet deployed** — awaiting DATABASE_URL and first deploy
44
45## Key Integrations
46
47- **GateTest:** `https://gatetest.ai/api/scan/run` (test runner)
48- **Crontech:** `https://crontech.ai/api/trpc/tenant.deploy` (deploy on push to main)
49- **Anthropic Claude:** AI review, merge resolution, security scan, triage, changelog, test gen
ModifiedCLAUDE.md+75−5View fileUnifiedSplit
@@ -2,7 +2,48 @@
22
33AI-native code intelligence platform — git hosting, automated CI, and green ecosystem enforcement.
44
5## Agent Policy — NEVER IDLE
5## Section 0 — SESSION START PROTOCOL (mandatory)
6
7Every session begins by loading memory. Do these in order:
8
91. Read `.memory/project-state.md` — current status snapshot
102. Read `.memory/last-session.md` — what was just built, what's next
113. Read `.memory/open-questions.md` — anything needing owner input
124. Run `git log --oneline -5` — verify branch state
135. Run `bun test 2>&1 | tail -3` — verify tests pass
14
15If any file is missing, note it and proceed. Never ask the user to provide context you can read from these files.
16
17## Section 1 — HARD RULES
18
19### Rule 1: Never forget context
20- Session memory lives in `.memory/`. Read it. Use it. Update it.
21- If you don't know something, check memory files before asking.
22- Cross-session continuity is non-negotiable.
23
24### Rule 2: Never sit idle
25- This is a revenue-generating product. Idle time = lost revenue.
26- See something broken? Fix it. See a gap? Build it. Finished a task? Start the next one.
27- Prioritize: Security fixes > broken functionality > performance > new features > polish.
28- When in doubt, build. The worst outcome is sitting idle.
29
30### Rule 3: Never ship broken code
31- `bun test` must pass before every commit.
32- Run tests after every change. If tests break, fix them immediately.
33- Commit and push frequently. Small, focused commits.
34- Every user-facing failure mode has a fallback — no 500s reach the UI.
35
36## Section 2 — SESSION END PROTOCOL (mandatory)
37
38Before the session ends or context compacts:
39
401. Update `.memory/last-session.md` with: what was built, what was fixed, branch state, what's next
412. Update `.memory/project-state.md` if architecture changed
423. Append new decisions to `.memory/decisions-log.md`
434. Update `.memory/open-questions.md` if new questions arose
445. Commit memory files with message: `chore: update session memory`
45
46## Section 3 — Agent Policy — NEVER IDLE
647
748**This is a revenue-generating product. Idle time = lost revenue.**
849
@@ -16,7 +57,7 @@ Every session must ship value. The rules:
16576. **Prioritize by impact:** Security fixes > broken functionality > performance > new features > polish.
17587. **When in doubt, build.** The worst outcome is sitting idle. The second worst is asking "should I?" when the answer is obviously yes.
1859
19## READ FIRST — every session
60## Section 4 — READ FIRST — every session
2061
2162**`BUILD_BIBLE.md` is mandatory reading for every Claude agent before any code changes.**
2263
@@ -29,14 +70,14 @@ It contains:
2970
3071Do not skip it. Do not refactor locked files. Do not stop mid-block.
3172
32## Stack
73## Section 5 — Stack
3374
3475- **Runtime:** Bun
3576- **Framework:** Hono (with JSX for server-rendered views)
3677- **Database:** Drizzle ORM + Neon (PostgreSQL)
3778- **Git:** Smart HTTP protocol via git CLI subprocesses
3879
39## Development
80## Section 6 — Development
4081
4182```bash
4283bun install # install dependencies
@@ -45,7 +86,7 @@ bun test # run tests
4586bun run db:migrate # run database migrations
4687```
4788
48## Architecture
89## Section 7 — Architecture
4990
5091```
5192src/
@@ -117,3 +158,32 @@ See `.env.example` for required variables. Key ones:
117158- `DATABASE_URL` — Neon PostgreSQL connection string
118159- `GIT_REPOS_PATH` — directory for bare git repos (default: `./repos`)
119160- `PORT` — HTTP port (default: 3000)
161
162## Section 8 — ESCAPE HATCHES
163
164User can override any rule at any time:
165
166- `just do X` — skip memory protocol and just do the thing
167- `rule check` — agent re-reads CLAUDE.md and confirms compliance
168- `memory check` — agent reads all .memory/ files and summarizes current state
169- `skip tests` — commit without running tests (use sparingly)
170- `stop` — end the session immediately, update memory first
171
172## Section 9 — DRIFT PREVENTION
173
174Every 10 tool calls, silently verify:
1751. Am I still following the hard rules?
1762. Has the conversation drifted from the build plan?
1773. Are tests still passing?
178
179If drift is detected, self-correct without being asked. If tests are failing, fix before continuing.
180
181## Section 10 — COMPETITIVE INTELLIGENCE
182
183The mission: **annihilate GitHub**. Continuously evaluate:
184- What does GitHub charge enterprise prices for that we ship free?
185- What is GitHub slow at that we can make instant?
186- What manual workflow can we automate with AI?
187- What's missing from every code hosting platform that developers wish existed?
188
189Every session should advance at least one competitive advantage.
Modifiedsrc/app.tsx+4−0View fileUnifiedSplit
@@ -76,6 +76,7 @@ import followsRoutes from "./routes/follows";
7676import rulesetsRoutes from "./routes/rulesets";
7777import commitStatusesRoutes from "./routes/commit-statuses";
7878import eventsRoutes from "./routes/events";
79import memoryRoutes from "./routes/memory";
7980import webRoutes from "./routes/web";
8081
8182const app = new Hono();
@@ -107,6 +108,9 @@ app.route("/", hookRoutes);
107108// SSE event streams (real-time gate updates, notifications)
108109app.route("/", eventsRoutes);
109110
111// Platform memory API (AI continuity)
112app.route("/", memoryRoutes);
113
110114// REST API
111115app.route("/", apiRoutes);
112116
Addedsrc/lib/memory.ts+198−0View fileUnifiedSplit
@@ -0,0 +1,198 @@
1/**
2 * Platform memory store.
3 *
4 * Persistent key-value memory for the AI subsystem. Stores facts,
5 * decisions, and learned context that should survive across requests
6 * and server restarts. Uses the database as backing store.
7 *
8 * Categories:
9 * - "pattern" — learned code review patterns
10 * - "decision" — architectural decisions and their rationale
11 * - "context" — project-specific context (conventions, tech debt, etc.)
12 * - "metric" — performance baselines and thresholds
13 * - "feedback" — user feedback signals
14 */
15
16import { eq, and, desc, gte, ilike, sql } from "drizzle-orm";
17import { db } from "../db";
18import { reviewPatterns } from "../db/schema";
19
20interface MemoryEntry {
21 key: string;
22 value: string;
23 category: string;
24 confidence: number;
25 createdAt: Date;
26 updatedAt: Date;
27}
28
29// In-memory cache for hot-path lookups (pattern context injection)
30const memoryCache = new Map<string, { value: string; expiresAt: number }>();
31const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
32
33/**
34 * Store a memory entry. Upserts by key.
35 */
36export async function memoryStore(
37 key: string,
38 value: string,
39 opts: {
40 category?: string;
41 confidence?: number;
42 scope?: string;
43 language?: string;
44 } = {}
45): Promise<void> {
46 const category = opts.category ?? "context";
47 const confidence = opts.confidence ?? 70;
48
49 try {
50 const existing = await db
51 .select()
52 .from(reviewPatterns)
53 .where(
54 and(
55 eq(reviewPatterns.pattern, key),
56 eq(reviewPatterns.category, category)
57 )
58 )
59 .limit(1);
60
61 if (existing.length > 0) {
62 await db
63 .update(reviewPatterns)
64 .set({
65 pattern: value,
66 confidence,
67 lastSeenAt: new Date(),
68 })
69 .where(eq(reviewPatterns.id, existing[0].id));
70 } else {
71 await db.insert(reviewPatterns).values({
72 scope: opts.scope ?? "global",
73 language: opts.language ?? null,
74 category,
75 pattern: value,
76 confidence,
77 evidenceCount: 1,
78 });
79 }
80
81 // Update cache
82 memoryCache.set(`${category}:${key}`, {
83 value,
84 expiresAt: Date.now() + CACHE_TTL,
85 });
86 } catch (err) {
87 console.error("[memory] store failed:", err);
88 }
89}
90
91/**
92 * Recall memory entries by category or keyword search.
93 */
94export async function memoryRecall(
95 query: string,
96 opts: {
97 category?: string;
98 limit?: number;
99 minConfidence?: number;
100 } = {}
101): Promise<Array<{ pattern: string; category: string; confidence: number }>> {
102 const limit = opts.limit ?? 10;
103 const minConfidence = opts.minConfidence ?? 30;
104
105 try {
106 const conditions = [
107 eq(reviewPatterns.active, true),
108 gte(reviewPatterns.confidence, minConfidence),
109 ];
110
111 if (opts.category) {
112 conditions.push(eq(reviewPatterns.category, opts.category));
113 }
114
115 if (query && query !== "*") {
116 conditions.push(ilike(reviewPatterns.pattern, `%${query}%`));
117 }
118
119 const results = await db
120 .select({
121 pattern: reviewPatterns.pattern,
122 category: reviewPatterns.category,
123 confidence: reviewPatterns.confidence,
124 })
125 .from(reviewPatterns)
126 .where(and(...conditions))
127 .orderBy(desc(reviewPatterns.confidence))
128 .limit(limit);
129
130 return results;
131 } catch (err) {
132 console.error("[memory] recall failed:", err);
133 return [];
134 }
135}
136
137/**
138 * Get a specific memory by category:key from cache, falling back to DB.
139 */
140export async function memoryGet(
141 category: string,
142 key: string
143): Promise<string | null> {
144 const cacheKey = `${category}:${key}`;
145 const cached = memoryCache.get(cacheKey);
146 if (cached && cached.expiresAt > Date.now()) {
147 return cached.value;
148 }
149
150 try {
151 const [row] = await db
152 .select()
153 .from(reviewPatterns)
154 .where(
155 and(
156 eq(reviewPatterns.category, category),
157 ilike(reviewPatterns.pattern, `%${key}%`),
158 eq(reviewPatterns.active, true)
159 )
160 )
161 .orderBy(desc(reviewPatterns.confidence))
162 .limit(1);
163
164 if (row) {
165 memoryCache.set(cacheKey, {
166 value: row.pattern,
167 expiresAt: Date.now() + CACHE_TTL,
168 });
169 return row.pattern;
170 }
171 return null;
172 } catch {
173 return null;
174 }
175}
176
177/**
178 * Get a summary of all stored memories by category.
179 */
180export async function memorySummary(): Promise<
181 Array<{ category: string; count: number; avgConfidence: number }>
182> {
183 try {
184 const results = await db
185 .select({
186 category: reviewPatterns.category,
187 count: sql<number>`count(*)::int`,
188 avgConfidence: sql<number>`avg(${reviewPatterns.confidence})::int`,
189 })
190 .from(reviewPatterns)
191 .where(eq(reviewPatterns.active, true))
192 .groupBy(reviewPatterns.category);
193
194 return results;
195 } catch {
196 return [];
197 }
198}
Addedsrc/routes/memory.ts+50−0View fileUnifiedSplit
@@ -0,0 +1,50 @@
1/**
2 * Memory API routes — read/write platform memory for AI continuity.
3 *
4 * These endpoints let the AI subsystem (and admin tools) persist and
5 * retrieve learned context across sessions and requests.
6 */
7
8import { Hono } from "hono";
9import { requireAuth } from "../middleware/auth";
10import type { AuthEnv } from "../middleware/auth";
11import { memoryStore, memoryRecall, memorySummary } from "../lib/memory";
12
13const memory = new Hono<AuthEnv>();
14
15memory.get("/api/memory/recall", requireAuth, async (c) => {
16 const query = c.req.query("q") || "*";
17 const category = c.req.query("category");
18 const limit = parseInt(c.req.query("limit") || "10", 10);
19
20 const results = await memoryRecall(query, { category, limit });
21 return c.json({ results });
22});
23
24memory.post("/api/memory/store", requireAuth, async (c) => {
25 const body = await c.req.json().catch(() => ({}));
26 const { key, value, category, confidence, scope, language } = body as Record<
27 string,
28 string | number | undefined
29 >;
30
31 if (!key || !value) {
32 return c.json({ error: "key and value are required" }, 400);
33 }
34
35 await memoryStore(String(key), String(value), {
36 category: category ? String(category) : undefined,
37 confidence: typeof confidence === "number" ? confidence : undefined,
38 scope: scope ? String(scope) : undefined,
39 language: language ? String(language) : undefined,
40 });
41
42 return c.json({ stored: true });
43});
44
45memory.get("/api/memory/summary", requireAuth, async (c) => {
46 const summary = await memorySummary();
47 return c.json({ summary });
48});
49
50export default memory;
051