Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
BUILD_BIBLE.md138.5 KB · 811 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
# GLUECRON BUILD BIBLE

**Last updated: 2026-05-29T03:00:00Z**

**This file is the single source of truth for the GlueCron build.**

**Every Claude agent MUST read this file in full before touching code. No exceptions.**

GlueCron is a GitHub replacement — AI-native code intelligence, green ecosystem enforcement, git hosting, automated CI. It is production infrastructure for multiple downstream platforms. Production cannot stop.

> **2030 Vision:** The most advanced git host on the market. Every developer who uses Gluecron ships 2× faster than one who doesn't. Zero caching issues, lightning-fast push-to-live pipeline, AI on every workflow step. Designed for the Claude Code era — any session, any repo, zero friction.

---

## 1. AGENT POLICY (READ FIRST, FOLLOW ALWAYS)

### 1.1 Required reads at session start
1. `BUILD_BIBLE.md` (this file) — complete
2. `CLAUDE.md` — stack + architecture
3. `README.md` — user-facing overview
4. Most recent commit on the current branch (`git log -1 --stat`)

### 1.2 Do-not-undo rule
- Anything listed in **§4 LOCKED BLOCKS** is shipped and must not be deleted, renamed, or semantically altered without the owner's explicit written permission in the current session.
- "Refactor" is not permission. "Clean up" is not permission. "Simplify" is not permission.
- If a locked file seems wrong, open an issue in the plan and keep going on a new block.

### 1.3 Continuous-build rule
- The owner runs many parallel projects. Do not stop work to ask for clarification that can be inferred from this file.
- Default behaviour when a block is partially complete: **finish it, run tests, commit, push, start the next block**.
- Only stop for genuinely blocking decisions: destructive operations, architectural reversals, requests outside this plan, or repeated test failures you can't diagnose.
- Never stop because "the session might run out." Commit what works and keep building.

### 1.4 Branch + commit rules
- Development branch: whatever the current session was told (check session opening message). Fall back to `main` if none given.
- One commit per completed block. Message format: `feat(BLOCK-ID): <summary>`.
- Push after every commit with `git push -u origin <branch>`.
- Never force-push. Never `--no-verify`. Never amend published commits.

### 1.5 Quality bars (non-negotiable)
- `bun test` must pass before every commit.
- New features ship with tests in `src/__tests__/`.
- New routes use `softAuth` or `requireAuth` middleware.
- New DB tables have a corresponding migration in `drizzle/`.
- AI features use `isAiAvailable()` guards and degrade gracefully without `ANTHROPIC_API_KEY`.
- Every user-facing failure mode has a fallback — no 500s reach the UI.

### 1.6 Green-ecosystem-by-default
- Every new repo auto-configures: gates on, branch protection on, labels seeded, CODEOWNERS synced, welcome issue posted.
- Users can opt out per feature but defaults are maximum-green.
- Nothing broken ever reaches production, the website, or the customer.

### 1.7 Parallelism rule (added per owner request)
- **Default to spawning sub-agents whenever work can be parallelised.** Owner-cost of an idle main thread is high; owner-cost of an extra agent is near-zero.
- Independent files = parallel agents. Schema-only edits, new route files, doc updates, test additions, codebase research — all of these run in parallel by default unless they collide.
- Coordinate file ownership: one agent per file. Never let two agents edit the same file. Mounting + middleware integration stay on the main thread to avoid merge conflicts.
- When launching multiple agents, send them in a single message with multiple Agent tool calls so they actually run concurrently.
- The main thread is responsible for: reviewing each agent's output before integrating, running the test suite, and committing. Trust-but-verify — read the changes, don't just rely on the agent's summary.

---

## 2. GITHUB PARITY SCORECARD

Legend: ✅ shipped · 🟡 partial · ❌ not built

### 2.1 Repository hosting
| Feature | Status | Notes |
|---|---|---|
| Git Smart HTTP (clone / push / fetch) | ✅ | `src/routes/git.ts`, `src/git/protocol.ts` |
| SSH keys | ✅ | `ssh_keys` table, `src/routes/settings.tsx` |
| Public / private visibility | ✅ | `repositories.isPrivate` |
| Forking | ✅ | `src/routes/fork.ts` |
| Stars | ✅ | `stars` table, `/:owner/:repo/star` |
| Topics | ✅ | `repo_topics` table |
| Archive / disable repo | ✅ | I1 — `src/routes/repo-settings.tsx` archive toggle; `RepoHeader` renders an "Archived" badge when `is_archived=true`. |
| Repository transfer | ✅ | I3 — `src/routes/repo-settings.tsx` transfer form + `POST /:owner/:repo/settings/transfer`; ownership change recorded in `repo_transfers` audit table. Reject conflicts (target owner already has a repo by that name) with a redirect. |
| Template repositories | ✅ | I2 — `drizzle/0022_repo_templates.sql` adds `is_template`. `src/routes/templates.ts` serves `POST /:owner/:repo/use-template` (git clone --bare into caller's namespace). "Use this template" CTA rendered on the public repo page. |
| Repository mirroring | ✅ | I9 — pull-style mirror of an upstream git URL. `drizzle/0026_repo_mirrors.sql` adds `repo_mirrors` (one-per-repo config) + `repo_mirror_runs` (audit log). `src/lib/mirrors.ts` validates URLs (https/http/git only, rejects ssh/file/shell metacharacters), runs `git fetch --prune --tags` via `Bun.spawn` with a 5-min timeout + `GIT_TERMINAL_PROMPT=0`. `src/routes/mirrors.tsx` exposes `/:owner/:repo/settings/mirror` + `/admin/mirrors/sync-all`. |

### 2.2 Code browsing
| Feature | Status | Notes |
|---|---|---|
| File tree browser | ✅ | `src/routes/web.tsx` |
| Syntax highlighting | ✅ | 40+ languages, `src/lib/highlight.ts` |
| Commit history | ✅ | |
| Diffs | ✅ | |
| Blame | ✅ | |
| Raw file download | ✅ | |
| Branch switcher | ✅ | |
| Tag listing | ✅ | new this build |
| Code search (ILIKE) | ✅ | per-repo + global |
| Semantic / embedding search | ✅ | D1 — `code_chunks` table + lexical fallback, optional Voyage `voyage-code-3`; `src/lib/semantic-search.ts`, `src/routes/semantic-search.tsx` |
| Symbol / xref navigation | ✅ | I8 — `src/lib/symbols.ts` regex-based extractor for ts/js/py/rs/go/rb/java/kt/swift; on-demand indexer persists top-level definitions into `code_symbols` (0025). `src/routes/symbols.tsx` serves `/:owner/:repo/symbols` overview + A–Z list, `/:owner/:repo/symbols/search?q=` prefix search, `/:owner/:repo/symbols/:name` definition detail. Owner-only reindex. |
| Dependency graph | ✅ | J1 — `src/lib/deps.ts` parses package.json / requirements.txt / pyproject.toml / go.mod / Cargo.toml / Gemfile / composer.json without a TOML lib. `src/routes/deps.tsx` serves `/:owner/:repo/dependencies` grouped by ecosystem with per-ecosystem counts; owner-only reindex walks the default-branch tree (max 200 manifests, 1MB each). `drizzle/0028_repo_dependencies.sql` adds `repo_dependencies`. |
| Security advisories / Dependabot alerts | ✅ | J2 — curated 12-entry seed list + minimal semver range matcher cross-referenced against J1 dep rows. `src/lib/advisories.ts` + `src/routes/advisories.tsx` serve `/:owner/:repo/security/advisories` (open) + `/all`, owner-only `POST /scan`, and per-alert dismiss/reopen. `drizzle/0029_security_advisories.sql` adds `security_advisories` + `repo_advisory_alerts`. |
| Commit signature verification (Verified badge) | ✅ | J3 — GPG + SSH pubkey registration at `/settings/signing-keys`, `gpgsig` extraction from raw commit objects, OpenPGP packet walker for Issuer Fingerprint, SHA-256 fingerprints for SSHSIG pubkeys, memoised in `commit_verifications`. Green "Verified" badge rendered on commit list + detail when a registered key matches. `src/lib/signatures.ts` + `src/routes/signing-keys.tsx` + `drizzle/0030_signing_keys.sql`. |
| Repository rulesets (push policy engine) | ✅ | J6 — named policies group N rules at active/evaluate/disabled enforcement. Pure evaluator `evaluatePush(rulesets, ctx)``{allowed, violations}`. Six rule types: commit_message_pattern, branch_name_pattern, tag_name_pattern, blocked_file_paths, max_file_size, forbid_force_push. Glob-lite matcher (`*` = non-slash, `**` = anything). Owner-only CRUD at `/:owner/:repo/settings/rulesets`. `src/lib/rulesets.ts` + `src/routes/rulesets.tsx` + `drizzle/0032_repo_rulesets.sql`. **Pre-receive enforcement (2026-04-30):** `src/lib/push-policy.ts` evaluates active rulesets at the HTTP layer for ref-name patterns; violations return 403 with a human-readable `remote: ` body. Pack-content rules (`commit_message_pattern`, `blocked_file_paths`, `max_file_size`) still need pack inspection — tracked in §7. |
| Commit status API (external CI signals) | ✅ | J8 — external systems POST per-commit (sha, context) statuses with state pending/success/failure/error. Combined rollup reduces to worst state. Public list + combined endpoints; write requires owner auth. Rendered on commit detail view as a pill row. `src/lib/commit-statuses.ts` + `src/routes/commit-statuses.ts` + `drizzle/0033_commit_statuses.sql`. |

### 2.3 Collaboration
| Feature | Status | Notes |
|---|---|---|
| Issues (CRUD / comments / labels / close) | ✅ | |
| Milestones | ✅ | `src/routes/insights.tsx` |
| Pull requests (CRUD / review / merge) | ✅ | |
| PR inline comments | ✅ | file+line anchored |
| Draft PRs | ✅ | create as draft, ready-for-review toggle, dedicated tab, merge blocked until ready |
| Reactions (emoji) | ✅ | 8 reactions, toggle via `POST /api/reactions/:t/:id/:emoji/toggle` on issues + PRs + comments |
| Mentions + notifications | ✅ | `src/routes/notifications.tsx` |
| Code owners | ✅ | `src/lib/codeowners.ts` |
| Issue templates | ✅ | `.github/ISSUE_TEMPLATE.md` auto-prefills new issues; frontmatter stripped; `src/lib/templates.ts` |
| PR templates | ✅ | `.github/PULL_REQUEST_TEMPLATE.md` auto-prefills new PRs; `src/lib/templates.ts` |
| Saved replies | ✅ | per-user canned comments, unique-shortcut, `/settings/replies`, `/api/user/replies` |
| Discussions / forums | ✅ | E2 — categorised threads, pinned/locked, q-and-a answers. `src/routes/discussions.tsx` + `drizzle/0013_discussions.sql` |
| Wikis | ✅ | E3 — markdown pages per repo with revision history + revert. DB-backed v1. `src/routes/wikis.tsx` + `drizzle/0016_wikis.sql` |
| Projects / kanban | ✅ | E1 — per-repo boards with auto-seeded To Do/In Progress/Done columns. Notes or linked issues/PRs. `src/routes/projects.tsx` + `drizzle/0015_projects.sql` |
| AI incident responder | ✅ | D4 — auto-issues on deploy fail, `src/lib/ai-incident.ts` |
| AI-generated test stubs | ✅ | D8 — `src/lib/ai-tests.ts`, `/:owner/:repo/ai/tests` |

### 2.4 Automation + AI
| Feature | Status | Notes |
|---|---|---|
| Webhooks (outbound, HMAC signed) | ✅ | `src/routes/webhooks.tsx` |
| GateTest inbound callback | ✅ | `POST /api/hooks/gatetest`, bearer or HMAC |
| Backup PAT-auth gate ingest | ✅ | `POST /api/v1/gate-runs` |
| Gate runs (test / secret / AI review) | ✅ | `gate_runs` table, `src/routes/gates.tsx` |
| Branch protection | ✅ | `branch_protection` table + UI |
| Auto-repair engine | ✅ | `src/lib/auto-repair.ts` |
| Secret scanner | ✅ | 15 patterns, `src/lib/security-scan.ts` |
| AI security review | ✅ | Sonnet 4, `src/lib/security-scan.ts` |
| AI commit messages | ✅ | `src/lib/ai-generators.ts` |
| AI PR summaries | ✅ | |
| AI changelogs | ✅ | auto on release create; arbitrary-range viewer at `/:owner/:repo/ai/changelog?from=&to=` (D7) |
| AI code review | ✅ | `src/lib/ai-review.ts``triggerAiReview` now runs real Claude review on PR open: `git diff base...head`, calls `reviewDiff()`, posts a summary comment + N inline file/line comments. Idempotent via `AI_REVIEW_MARKER`. Was a stub until 2026-04-30; now fully wired with a 100KB diff cap and graceful fallback when the Anthropic API fails. |
| AI merge conflict resolver | ✅ | `src/lib/merge-resolver.ts` |
| AI chat (global + repo) | ✅ | `src/routes/ask.tsx` |
| AI explain-this-codebase | ✅ | D6 — per-commit cached markdown, `GET /:owner/:repo/explain`, `src/lib/ai-explain.ts` + `src/routes/ai-explain.tsx` |
| AI PR triage | ✅ | D3 — Claude Haiku suggests labels/reviewers/priority as an AI comment on PR create. `triagePullRequest` in `src/lib/ai-generators.ts` is the AI helper; `src/lib/pr-triage.ts` `triggerPrTriage` is the fire-and-forget caller wired in `src/routes/pulls.tsx`. Builds a numstat-only diff summary, loads available labels + candidate reviewers (owner + recent PR authors, capped at 12), inserts a "## AI Triage" comment. Idempotent via `PR_TRIAGE_MARKER`. Was a stub until 2026-04-30; now fully wired. |
| GitHub Actions equivalent (workflow runner) | ✅ | `src/lib/workflow-parser.ts`, `src/lib/workflow-runner.ts`, `src/routes/workflows.tsx`; `.gluecron/workflows/*.yml` auto-discovered on push; Bun subprocess executor, per-step timeouts, size-capped logs |
| Scheduled workflows (cron triggers) | ✅ | 2026-04-30 — `on: schedule: [{cron: "0 * * * *"}]` triggers fire from the autopilot tick. `src/lib/cron.ts` (5-field UNIX parser/matcher, POSIX dom/dow OR, no @aliases or L/W/#/?). `src/lib/scheduled-workflows.ts` walks non-disabled workflows, computes `since` from latest schedule run (fallback now-6min), enqueues at most one schedule run per workflow per tick. `MAX_RUNS_PER_TICK=50` safety cap. Wired as the `scheduled-workflows` autopilot task. |
| Live comment updates (SSE) | ✅ | 2026-04-30 — `src/routes/issues.tsx` + `src/routes/pulls.tsx` publish on comment create via `src/lib/sse.ts`; the same detail pages render a hidden banner + `liveCommentBannerScript` from `src/lib/sse-client.ts` that reveals "X new comment(s) — reload" when remote tabs post. Multi-segment topic grammar (`repo:<uuid>:issue:<n>`) accepted by `src/routes/live-events.ts`. In-process broadcaster only — cross-node fanout still in §7. |
| AI Suggest PR description | ✅ | 2026-04-30 — "Suggest description with AI" button on `/:owner/:repo/pulls/new` calls `POST /:owner/:repo/ai/pr-description` (write-access gated). Endpoint computes `git diff base...head`, calls `generatePrSummary` (Sonnet 4), returns JSON `{ok, body}`. Inline JS replaces the description textarea on success (with a confirm-overwrite prompt if non-empty). Helper was on disk but unused until 2026-04-30. |
| AI Issue triage | ✅ | 2026-04-30 — On every issue create the system fires `triggerIssueTriage` (`src/lib/issue-triage.ts`) which calls `triageIssue()` from `src/lib/ai-generators.ts`. Posts a "## AI Triage" issue comment with one-line summary, priority, suggested labels filtered against the repo's existing labels, and a "Possible duplicate of #N" callout when confidence is high. Idempotent via `ISSUE_TRIAGE_MARKER`. Suggestions only. The `triageIssue` helper was on disk but unused until 2026-04-30. |
| AI commit message suggestion | ✅ | 2026-04-30 — "Suggest with AI" button on the file-edit form. `POST /:owner/:repo/ai/commit-message` (write-access gated) reads the on-disk blob via `getBlob`, builds a minimal Old/New diff representation, calls `generateCommitMessage()` from `src/lib/ai-generators.ts`, caps to one line + 100 chars. The helper was on disk but unused until 2026-04-30. |
| Workflow secret substitution | ✅ | 2026-04-30 — `${{ secrets.NAME }}` references inside step `run:` are now substituted at execution time. Pure helper `substituteSecrets(template, secrets)` in `src/lib/workflow-secrets.ts` (strict `[A-Z_][A-Z0-9_]*` grammar; missing names left intact as a loud failure signal; uses `hasOwnProperty` to block prototype-pollution). The v1 runner (`src/lib/workflow-runner.ts`) loads the per-run secrets map once via `loadSecretsContext(repositoryId)` and passes it into every `runStep` call. Was a stub until 2026-04-30. |
| Model Context Protocol server | ✅ | 2026-04-30 — `GET /mcp` discovery + `POST /mcp` JSON-RPC 2.0. Lets Claude Desktop / Code / Cursor drive Gluecron natively. v1 tools (read-only, public-only): `gluecron_repo_search`, `gluecron_repo_read_file`, `gluecron_repo_list_issues`, `gluecron_repo_explain_codebase`. Single + batched + notification envelopes supported. Move #9 from `docs/STRATEGY.md`. Implementation: `src/lib/mcp.ts` + `src/lib/mcp-tools.ts` + `src/routes/mcp.ts`. **2026-05-13 (Block K1):** added 10 write tools so Claude can drive PRs/issues end-to-end without GitHub round-trip: `gluecron_create_issue`, `gluecron_comment_issue`, `gluecron_close_issue`, `gluecron_reopen_issue`, `gluecron_create_pr`, `gluecron_get_pr`, `gluecron_list_prs`, `gluecron_comment_pr`, `gluecron_merge_pr`, `gluecron_close_pr`. All gate on `ctx.userId !== null` (rejected via `ERR_INVALID_PARAMS`) + repo write-access via `src/middleware/repo-access.ts` (rejected via `ERR_METHOD_NOT_FOUND` — same shape as read tools so private repos don't leak). `gluecron_merge_pr` re-uses the manual-merge `matchProtection`/`evaluateProtection`/`listRequiredChecks`/`passingCheckNames`/`runAllGateChecks`/`mergeWithAutoResolve` + J7 close-keywords pipeline. HTTP route still uses `softAuth` so original 5 read tools keep working anonymously. |
| AI auto-merge (gated) | ✅ | K2 — `src/lib/auto-merge.ts` + `drizzle/0040_branch_protection_auto_merge.sql` + settings checkbox on `src/routes/gates.tsx`. Pure `decideAutoMerge` + DB orchestrator `evaluateAutoMerge`. Default-deny: requires `enable_auto_merge=true` on a matching branch_protection rule AND every gate the manual merge path enforces. AI-approval heuristic over `AI_REVIEW_MARKER` comments. Optional size cap. Side-effect-free; the K3 autopilot is the only caller. |
| AI-driven autopilot | ✅ | K3 — extends `src/lib/autopilot.ts` with two new tick tasks: `auto-merge-sweep` (calls K2, merges eligible PRs, audits `auto_merge.merged` / `auto_merge.merge_failed`, posts `<!-- gluecron:auto-merge:v1 -->` marker) and `ai-build-from-issues` (finds open issues labelled `ai:build`, dispatches via spec-to-pr, posts `<!-- gluecron:ai-build:v1 -->` marker for idempotency). `src/lib/pr-merge.ts` factored shared `performMerge(prId)` helper. `src/lib/ai-build-tasks.ts` orchestrates the AI-build sweep with 5 DI'd collaborators. Both tasks respect `AUTOPILOT_DISABLED=1` and skip archived repos. |
| App-bot push auth (`ghi_` install tokens) | ✅ | 2026-04-30 — `git-receive-pack` Authorization header now resolves `ghi_*` install tokens to the synthetic `users` row that `marketplace.ts createApp` creates alongside the bot. Bots get identity for the audit log + protected-tag checks; authorisation (collaborator grants) is still separate. Legacy bots created before the back-fill landed remain anonymous. Move #4 from `docs/STRATEGY.md`. |
| Dependabot equivalent (AI dep bumper) | ✅ | D2 — `dep_update_runs` table, npm registry fetch, plan + apply bumps, creates `gluecron/dep-update-*` branch + PR row via git plumbing. `src/lib/dep-updater.ts`, `src/routes/dep-updater.tsx`, settings UI at `/:owner/:repo/settings/dep-updater`. |
| Code scanning UI | ✅ | I5 — `src/routes/code-scanning.tsx`, `GET /:owner/:repo/security`. Aggregates last-100 `gate_runs` matching `%scan%`/`%security%`, rolls up latest status per gate, shows failed/repaired/total cards + scanner status list + recent runs. |
| Copilot code completion | ✅ | D9 — `POST /api/copilot/completions` (PAT/OAuth/session), `GET /api/copilot/ping`. `src/lib/ai-completion.ts`, `src/routes/copilot.ts`. LRU-cached, rate-limited 60/min. |
| Semantic code search | ✅ | D1 — see 2.2 |
| Spec-to-PR (NL feature spec → draft PR) | ✅ | `src/routes/specs.tsx` UI, `src/lib/spec-to-pr.ts` orchestrator, `src/lib/spec-ai.ts` Claude call + parser, `src/lib/spec-context.ts` repo context reader, `src/lib/spec-git.ts` plumbing-only branch writer. Graceful fallback when `ANTHROPIC_API_KEY` is unset. |
| Repository intelligence engine | ✅ | `src/lib/intelligence.ts` — repo-level summarisation + signals. |
| Time-travel code explorer | ✅ | `src/lib/timetravel.ts` — historical snapshot navigation. |
| Dependency impact analyzer | ✅ | `src/lib/depimpact.ts` — "what breaks if I bump X" cross-reference. |
| One-click rollback | ✅ | `src/lib/rollback.ts` — fast revert helper for failed deploys. |
| Auto-repair (intelligence path) | ✅ | `src/lib/autorepair.ts` `autoRepair(...)` — invoked from `src/hooks/post-receive.ts`. Distinct from `src/lib/auto-repair.ts` `repairSecrets`/`repairSecurityIssues` (gate-time). Both are load-bearing — do not dedupe without owner approval. |

### 2.5 Platform
| Feature | Status | Notes |
|---|---|---|
| Dashboard | ✅ | `src/routes/dashboard.tsx` |
| Explore / discover | ✅ | |
| Global search | ✅ | repos / users / issues / PRs |
| Insights (graph, contributors, green rate) | ✅ | `src/routes/insights.tsx` |
| Releases + tags | ✅ | AI changelog |
| Personal access tokens | ✅ | SHA-256 hashed |
| OAuth app provider | ✅ | `src/routes/oauth.tsx`, `src/routes/developer-apps.tsx`, `src/lib/oauth.ts`; `oauth_apps` + `oauth_authorizations` + `oauth_access_tokens` tables |
| GitHub Apps equivalent | ✅ | H2 — `src/lib/marketplace.ts` `generateBearerToken`/`verifyInstallToken` (1h TTL, `ghi_` prefix, sha256 hashed). Each app gets a `<slug>[bot]` identity (`app_bots`). Permissions enforced via `hasPermission` (write implies read). |
| GraphQL API | ✅ | G2 — see 2.6 |
| Organizations + teams | ✅ | B1+B2+B3 shipped: `src/routes/orgs.tsx`, `src/lib/orgs.ts`; org-owned repos (`repositories.orgId`); team-based CODEOWNERS (`@org/team` resolution) |
| Enterprise SAML / SSO | ✅ | I10 — OIDC (Okta / Azure AD / Auth0 / Google Workspace). `src/lib/sso.ts` + `src/routes/sso.tsx`, `drizzle/0027_sso_oidc.sql` (tables `sso_config` singleton + `sso_user_links`). Admin config at `/admin/sso`; `/login/sso` starts auth-code flow with state+nonce cookies; `/login/sso/callback` exchanges code, fetches userinfo, links by `sub` (or by email, or auto-creates). Optional email-domain allow-list + auto-create toggle. |
| 2FA / TOTP | ✅ | `src/routes/settings-2fa.tsx`, `src/lib/totp.ts`; `user_totp` + `user_recovery_codes` tables |
| Passkeys / WebAuthn | ✅ | `src/routes/passkeys.tsx`, `src/lib/webauthn.ts`; `user_passkeys` + `webauthn_challenges` tables |
| Packages registry (npm / docker / etc) | ✅ | `src/lib/packages.ts`, `src/routes/packages-api.ts`, `src/routes/packages.tsx`; npm protocol (packument, tarball, publish, yank); PAT (`glc_`) auth via Authorization header; container registry deferred |
| Pages / static hosting | ✅ | `src/lib/pages.ts`, `src/routes/pages.tsx`; serves blobs from bare git at latest `gh-pages` commit; per-repo settings (source branch/dir, custom domain); short-cache headers |
| Gists | ✅ | E4 — multi-file tiny repos with per-revision JSON snapshots + stars. `src/routes/gists.tsx` + `drizzle/0014_gists.sql` |
| Sponsors | ✅ | I6 — `src/routes/sponsors.tsx`, `drizzle/0023_sponsors.sql` (tables `sponsorship_tiers`, `sponsorships`). Public `/sponsors/:user` page with tier cards + recent public sponsors; maintainer view at `/settings/sponsors` with add/retire tiers. Payment rails deferred — captures intent + thank-you notes. |
| Marketplace | ✅ | H1 — `src/routes/marketplace.tsx` + `src/lib/marketplace.ts`, `drizzle/0021_marketplace_and_apps.sql` (5 tables: `apps`, `app_installations`, `app_bots`, `app_install_tokens`, `app_events`). Public `/marketplace` directory, `/marketplace/:slug` detail + install, `/settings/apps` personal installs, `/developer/apps-new` registration, `/developer/apps/:slug/manage` event log + token issuance. |
| Environments / deployment tracking | ✅ | `src/routes/deployments.tsx` — grouped by env, success-rate rollup, per-deploy detail. Protected environments (`src/routes/environments.tsx`, `src/lib/environments.ts`) with reviewer-gated approval, branch-glob restrictions, approve/reject decisions recorded in `deployment_approvals` |
| Merge queues | ✅ | E5 — serialised merge with re-test. `src/lib/merge-queue.ts`, `src/routes/merge-queue.tsx`, `drizzle/0017_merge_queue.sql`; per `(repo, base_branch)` queue, owner-only process-next re-runs gates against latest base before merging. |
| Required checks matrix | ✅ | E6 — per branch-protection named check list. `src/routes/required-checks.tsx`, `drizzle/0018_required_checks.sql`; `listRequiredChecks` + `passingCheckNames` helpers in `src/lib/branch-protection.ts`; merge handler verifies every required name has a passing gate_run or workflow_run. |
| Protected tags | ✅ | E7 — owners can mark tag patterns (`v*`, `release-*`) protected. `src/lib/protected-tags.ts`, `src/routes/protected-tags.tsx`, `drizzle/0019_protected_tags.sql`. **Pre-receive enforcement (2026-04-30):** `src/lib/push-policy.ts` blocks tag pushes that match a protected pattern unless the pusher is the repo owner; rejection returns 403 with the violation list. Audit log entries (`push.rejected`) record blocked attempts. Was advisory-only until 2026-04-30. |

### 2.6 Observability + safety
| Feature | Status | Notes |
|---|---|---|
| Rate limiting | ✅ | `src/middleware/rate-limit.ts` |
| Request-ID tracing | ✅ | `src/middleware/request-context.ts` |
| Health / readiness / metrics | ✅ | `/healthz` `/readyz` `/metrics`. Extended probe surface in `src/routes/health-probe.ts`; public dashboard `src/routes/status.tsx` (`GET /status` + `/status.svg` shields badge). |
| Public platform status JSON | ✅ | `src/routes/platform-status.ts` — machine-readable status JSON. |
| Error tracking | ✅ | `src/lib/observability.ts` — wired into `app.onError`. Sinks: `ERROR_WEBHOOK_URL` and/or `SENTRY_DSN`. Never throws. |
| Comprehensive REST API v2 | ✅ | `src/routes/api-v2.ts` — full CRUD across resources. Authoritative integration surface alongside the v1 `/api/*` endpoints in §4.6. |
| API documentation page | ✅ | `src/routes/api-docs.tsx` — interactive in-app docs. |
| SSE in-process pub/sub | ✅ | `src/lib/sse.ts` + `src/lib/sse-client.ts` — topic-based broadcaster. `src/routes/live-events.ts` exposes `GET /live-events/:topic`. Used by the live UI updates layer. |
| Inbound deploy event receiver (Signal Bus P1) | ✅ | `src/routes/events.ts` — Crontech → Gluecron event ingest. Idempotent via `drizzle/0034_processed_events.sql` (sha256 of payload deduped at write-site). |
| Autopilot ticker | ✅ | `src/lib/autopilot.ts` — 5-min cycle: mirror sync, merge-queue peek, weekly digests, advisory rescans, **wait-timer-release** (env approvals), **scheduled-workflows** (cron triggers). `AUTOPILOT_DISABLED=1` opt-out. Admin page `/admin/autopilot`. |
| Demo seed | ✅ | `src/lib/demo-seed.ts` — idempotent `ensureDemoContent()`. `DEMO_SEED_ON_BOOT=1` boot flag. Site-admin reseed at `/admin` (`POST /admin/demo/reseed`). `/demo` redirect to a sample repo. |
| SEO (robots + sitemap) | ✅ | `src/routes/seo.ts``/robots.txt` + `/sitemap.xml`. |
| Audit log (table) | ✅ | `audit_log` table |
| Audit log UI | ✅ | `/settings/audit` (personal) + `/:owner/:repo/settings/audit` (per-repo, owner-only) |
| Traffic analytics per repo | ✅ | F1 — `src/lib/traffic.ts` + `src/routes/traffic.tsx`, `drizzle/0020_analytics_and_admin.sql`; owner-only 7/14/30/90d windows, ascii-bar daily chart. SHA-256-truncated IP for unique visitors. Fire-and-forget wiring in `web.tsx` + `git.ts`. |
| Org insights dashboard | ✅ | F2 — `src/routes/org-insights.tsx`; `computeOrgInsights(orgId)` rollup of gate green-rate + PR/issue counts + per-repo breakdown. `GET /orgs/:slug/insights`. |
| Site admin panel | ✅ | F3 — `src/lib/admin.ts` + `src/routes/admin.tsx`, tables `site_admins` + `system_flags`. Bootstrap rule (oldest user wins until `site_admins` populated). Flags: registration_locked, site_banner_*, read_only_mode. |
| Billing + quotas | ✅ | F4 — `src/lib/billing.ts` + `src/routes/billing.tsx`, tables `billing_plans` + `user_quotas` seeded free/pro/team/enterprise. `/settings/billing` personal view + `/admin/billing` site-admin override. |
| Email notifications | ✅ | opt-in per kind (mention/assign/gate-fail) via `/settings`; provider-pluggable `src/lib/email.ts` (log default, resend in prod) |
| Email digest | ✅ | I7 — `src/lib/email-digest.ts` + `drizzle/0024_email_digest.sql` (`users.notify_email_digest_weekly` + `last_digest_sent_at`). `composeDigest` pulls notifications + failed/repaired gates + merged PRs over last 7d, renders text + escaped HTML. `/settings/digest/preview` for self-preview; `/admin/digests` dashboard + `POST /admin/digests/run` fires `sendDigestsToAll`; `POST /admin/digests/preview` sends to one user. Never throws. |
| Mobile PWA | ✅ | G1 — `src/routes/pwa.ts` serves `/manifest.webmanifest` + `/sw.js` + `/icon.svg`; Layout injects manifest link + SW registration. Offline-capable (network-first for HTML). |
| GraphQL API | ✅ | G2 — `src/lib/graphql.ts` parser + executor, `src/routes/graphql.ts` endpoint at `POST /api/graphql`, GraphiQL-lite explorer at `GET /api/graphql`. Queries only (viewer/user/repository/search/rateLimit). |
| Official CLI | ✅ | G3 — `cli/gluecron.ts` Bun-compilable single binary. REST + GraphQL client, `~/.gluecron/config.json` 0600. |
| VS Code extension | ✅ | G4 — `vscode-extension/` with commands for explain / open-on-web / semantic search / generate tests. |
| Native mobile apps | ❌ | |
| Repository collaborators (per-repo roles) | ✅ | `drizzle/0035_repo_collaborators.sql` (read/write/admin, hierarchical) + `drizzle/0036_invite_token_hash.sql` (sha256 invite tokens). `src/routes/collaborators.tsx`, `src/routes/team-collaborators.tsx`, `src/routes/invites.tsx`, `src/lib/invite-tokens.ts`. Wired through `src/middleware/repo-access.ts` permission middleware. |
| GitHub import (single repo) | ✅ | `src/routes/import.tsx` + `src/lib/import-helper.ts`. PAT-based clone-and-rehost. |
| Bulk GitHub import (org migration) | ✅ | `src/routes/import-bulk.tsx` — paste a token, mirror an entire org in one pass. |
| Migration history + verifier | ✅ | `src/routes/migrations.tsx` per-user history dashboard, `src/lib/import-verify.ts` post-migration smoke check (object count, branches, default branch). |
| Onboarding flow | ✅ | `src/routes/onboarding.tsx` — guided first-five-minutes for new accounts. |
| Public help / quickstart | ✅ | `src/routes/help.tsx` — owner-facing migration cheatsheet at `/help`. |
| Dark mode | ✅ | default |
| Light-mode toggle | ✅ | `/theme/toggle` + `theme` cookie, pre-paint script avoids FOUC, nav sun/moon icon |
| Keyboard shortcuts | ✅ | `/shortcuts` page |
| Command palette | ✅ | I4 — `src/views/layout.tsx` injects a Cmd+K palette with ~20 canonical destinations, arrow-key navigation + fuzzy match. Backdrop click or Esc closes. |

---

## 3. BUILD PLAN (BLOCKS)

Each block is a self-contained unit. Order matters for dependencies. Each block ends with tests + commit + push.

### BLOCK A — Hardening the current surface
Polish what's shipped before adding more. **Priority: do this first if parity gaps are minor.**
- **A1** — Dark/light theme toggle (cookie, CSS variable swap) ✅
- **A2** — Audit log UI page (`/settings/audit` + `/:owner/:repo/settings/audit`) ✅
- **A3** — Reactions UI on issues / PRs / comments (data exists) ✅
- **A4** — Draft PR toggle + filter ✅
- **A5** — Issue + PR templates (`.github/*_TEMPLATE.md` auto-prefill) ✅
- **A6** — Saved replies per user ✅
- **A7** — Environments + deployment history UI (`deployments` table) ✅
- **A8** — Email notifications (opt-in, provider-pluggable) ✅

**BLOCK A COMPLETE.** Next: BLOCK B (Identity + orgs).

### BLOCK B — Identity + orgs
- **B1** — Organizations (schema: `organizations`, `org_members`, `teams`, `team_members`) → ✅ shipped (`6563f0a`)
  - Helpers in `src/lib/orgs.ts`: slug validation, role rank, reserved-slug set, loaders
  - Routes in `src/routes/orgs.tsx`: list / create / profile / people / teams / team detail
  - Role-based guards: admin adds members, owner grants owner, last-owner demote/remove blocked
  - All sensitive actions `audit()`'d (org.create, member.add/role/remove, team.create, team.member.add/remove)
- **B2** — Repos owned by orgs (nullable `repositories.orgId`) → ✅ shipped (`7437605`)
- **B3** — Team-based CODEOWNERS (`@org/team` resolution) → ✅ shipped (`40d3e3f`)
- **B4** — 2FA / TOTP (enroll, recovery codes) → ✅ shipped (`7298a17`)
- **B5** — WebAuthn / passkeys → ✅ shipped (`2df1f8c`)
- **B6** — OAuth 2.0 provider (third-party apps can request access) → ✅ shipped (pending final commit)

### BLOCK C — Runtime + hosting
- **C1** — Actions-equivalent workflow runner → ✅ shipped (`eafe8c6`)
  - Workflow YAML parser (`src/lib/workflow-parser.ts`) — hand-rolled subset
  - Background worker (`src/lib/workflow-runner.ts`) — Bun.spawn, size-capped logs, SIGTERM→SIGKILL timeouts
  - Auto-discovery from `.gluecron/workflows/*.yml` on default-branch push
  - UI at `/:owner/:repo/actions` with manual trigger + cancel
- **C2** — Package registry (npm protocol) → ✅ shipped
  - Packument + tarball + publish + yank via `PUT /npm/<name>` + `GET /npm/<name>`
  - PAT (`glc_`) bearer auth for CLI clients; add `//host/npm/:_authToken=<PAT>` to .npmrc
  - Container registry deferred (schema ready for it)
- **C3** — Pages / static hosting → ✅ shipped
  - Serves `/:owner/:repo/pages/*` from the latest successful `pages_deployments` row
  - Auto-records on push to the repo's configured source branch (default `gh-pages`)
  - Settings UI at `/:owner/:repo/settings/pages` + manual redeploy
- **C4** — Environments with protected approvals → ✅ shipped
  - Per-repo `environments` with reviewer list + branch-glob allowlist
  - Auto-deploy on main is gated by `requiresApprovalFor()`; pending rows show status `pending_approval`
  - Approve/reject at `POST /:owner/:repo/deployments/:id/approve|reject`

### BLOCK D — AI-native differentiation
This is where GlueCron beats GitHub outright. **Priority: ship these loud.**
- **D1** — Semantic code search → ✅ shipped. `src/lib/semantic-search.ts` + `src/routes/semantic-search.tsx`. `code_chunks` table stores chunk embeddings as JSON (upgrade path to `pgvector`). Embedding provider: Voyage AI `voyage-code-3` when `VOYAGE_API_KEY` is set, otherwise deterministic 512-dim hashing fallback. Index via `POST /:owner/:repo/search/semantic/reindex` (owner-only).
- **D2** — AI dependency updater → ✅ shipped. `src/lib/dep-updater.ts` + `src/routes/dep-updater.tsx`. `dep_update_runs` table tracks run history. Parses `package.json`, queries `registry.npmjs.org`, plans bumps (skips workspace/github specs + downgrades), writes an `gluecron/dep-update-<ts>` branch via git plumbing (`hash-object` + `mktree` + `commit-tree` + `update-ref`), inserts a pull_requests row with a markdown bump table. Settings UI at `/:owner/:repo/settings/dep-updater` with "Run now".
- **D3** — AI PR triage → ✅ shipped. `triagePullRequest` in `src/lib/ai-generators.ts`; hooked into PR create in `src/routes/pulls.tsx` (fire-and-forget). Posts a non-applied "## AI Triage" comment with suggested labels, reviewers, priority, and risk area. Suggestions only — PR author stays in control.
- **D4** — AI incident responder → ✅ shipped. `src/lib/ai-incident.ts` exports `onDeployFailure(args)` — on deploy-fail hooks, samples ~10 recent commits, calls Sonnet 4 for a structured root-cause JSON, opens an issue (number via `serial`), best-effort attaches `incident` label, sets `deployments.blockedReason="auto-issue #N"`. Wired from `src/hooks/post-receive.ts triggerCrontechDeploy` (fire-and-forget) and from `POST /:owner/:repo/deployments/:id/retry-incident` (owner-only re-run button on the deployment detail page). Never throws; degrades to deterministic body when no `ANTHROPIC_API_KEY`.
- **D5** — AI code reviewer blocks merges → ✅ shipped. `src/lib/branch-protection.ts` exports `matchProtection(repoId, branch)` (exact > glob, reuses `matchGlob` from environments.ts), `evaluateProtection(rule, ctx)` pure decision helper (checks `requireAiApproval` / `requireGreenGates` / `requireHumanReview` / `requiredApprovals`), and `countHumanApprovals(prId)` (LGTM/`+1`/approved heuristic on non-AI PR comments). Wired into `src/routes/pulls.tsx` merge handler after the existing hard-gate filter — blocks merge with readable reasons when rule fails. 8 unit tests in `src/__tests__/branch-protection.test.ts`.
- **D6** — AI "explain this codebase" → ✅ shipped. `src/lib/ai-explain.ts` + `src/routes/ai-explain.tsx`. Samples up to ~25 representative files (~60KB cap), generates a Markdown explanation via Sonnet 4, caches per (repo, commit sha) in `codebase_explanations`. `GET /:owner/:repo/explain` + owner-only `POST /:owner/:repo/explain/regenerate`. Explain link added to `RepoNav`.
- **D7** — AI changelog for every commit range → ✅ shipped. `src/routes/ai-changelog.tsx`. `GET /:owner/:repo/ai/changelog?from=&to=(&format=markdown)` — runs `git log` on the range, calls existing `generateChangelog`, renders form + rendered Markdown + copy-box; `format=markdown` returns `text/markdown` for CLI/CI consumers. Caps at 500 commits.
- **D8** — AI-generated test suite → ✅ shipped. `src/lib/ai-tests.ts` exports `detectLanguage(path)`, `detectTestFramework(repo tree)`, `buildTestsPrompt(...)`, `suggestedTestPath(...)`, `generateTestStub({path, content, framework, language})` (returns `{code:"", framework:"fallback"}` when AI unavailable), `contentTypeFor(path)`. Route `src/routes/ai-tests.tsx` adds `GET /:owner/:repo/ai/tests` (form + file picker), `GET /:owner/:repo/ai/tests?format=raw` (raw text with correct MIME), `POST /:owner/:repo/ai/tests/generate` (requireAuth, renders highlighted source + generated failing test, copy button). Stubs are intentionally failing so the author fills them in.
- **D9** — Copilot-style completion endpoint → ✅ shipped. `src/lib/ai-completion.ts` + `src/routes/copilot.ts`. `POST /api/copilot/completions` (requireAuth accepts PAT/OAuth/session), `GET /api/copilot/ping`. Claude Haiku; in-memory LRU (size 200, 5-min TTL); code-fence stripping; 60/min rate limit per caller.

### BLOCK E — Collaboration parity
- **E1** — Projects / kanban boards → ✅ shipped. `src/routes/projects.tsx`, tables `projects`/`project_columns`/`project_items` (migration 0015). Create creates three default columns (To Do/In Progress/Done); cards carry note or issue/pr link; one-click move between columns; owner-only close.
- **E2** — Discussions (forum threads per repo) → ✅ shipped. `src/routes/discussions.tsx`, tables `discussions`/`discussion_comments` (migration 0013). Categorised (general/q-and-a/ideas/announcements/show-and-tell), pinnable, lockable, q-and-a answers.
- **E3** — Wikis → ✅ shipped as DB-backed v1. `src/routes/wikis.tsx`, tables `wiki_pages`/`wiki_revisions` (migration 0016). Slug auto-derived; every edit bumps revision + appends a revision row; owner can revert. Git-backed mirror deferred.
- **E4** — Gists → ✅ shipped. `src/routes/gists.tsx`, tables `gists`/`gist_files`/`gist_revisions`/`gist_stars` (migration 0014). Multi-file; each edit takes a JSON snapshot into `gist_revisions` keyed on revision number; stars toggle; secret gists hidden from non-owners.
- **E5** — Merge queues → ✅ shipped. `src/lib/merge-queue.ts`, `src/routes/merge-queue.tsx`, table `merge_queue_entries` (migration 0017). Per `(repo, base_branch)` FIFO queue; `POST /:owner/:repo/pulls/:n/enqueue` adds from the PR page; owner-only `POST /queue/process-next` re-runs gates against latest base before merging the head. Entries have queued | running | merged | failed | dequeued states.
- **E6** — Required status checks matrix → ✅ shipped. `src/routes/required-checks.tsx`, table `branch_required_checks` (migration 0018); helpers `listRequiredChecks` + `passingCheckNames` in `src/lib/branch-protection.ts`. Settings UI at `/:owner/:repo/gates/protection/:id/checks`; merge handler (`src/routes/pulls.tsx`) loads required names + computes passing set from `gate_runs` (passed/repaired) + `workflow_runs` (success) and blocks if any required name is missing.
- **E7** — Protected tags → ✅ shipped. `src/lib/protected-tags.ts`, `src/routes/protected-tags.tsx`, table `protected_tags` (migration 0019). Settings CRUD at `/:owner/:repo/settings/protected-tags`; patterns use same glob syntax as branch protection. v1 enforcement is advisory: post-receive logs audit entries (`protected_tags.{create|update|delete}_violation_candidate`) so owners can see violations; pre-receive blocking is future work.

### BLOCK F — Observability + admin
- **F1** — Traffic analytics per repo → ✅ shipped. `src/lib/traffic.ts` + `src/routes/traffic.tsx`, table `repo_traffic_events` (migration 0020). `track`/`trackView`/`trackClone`/`trackByName` are fire-and-forget; SHA-256 of IP truncated to 16 chars for unique-visitor approximation. Owner-only `GET /:owner/:repo/traffic` renders 7/14/30/90 day windows with an ascii-bar daily chart. Wired into `src/routes/web.tsx` repo overview + `src/routes/git.ts` git-upload-pack handler.
- **F2** — Org-wide insights → ✅ shipped. `src/routes/org-insights.tsx` exports `computeOrgInsights(orgId)`. `GET /orgs/:slug/insights` requires org membership; aggregates gate green-rate, open/merged PR counts, open issue count, and per-repo rows sorted by activity. No new tables — live rollup across existing `repositories`, `gate_runs`, `pull_requests`, `issues`.
- **F3** — Admin / superuser panel → ✅ shipped. `src/lib/admin.ts` + `src/routes/admin.tsx`, tables `site_admins` + `system_flags` (migration 0020). `isSiteAdmin(userId)` with bootstrap rule (empty `site_admins` table → oldest user wins); `KNOWN_FLAGS` = { registration_locked, site_banner_text, site_banner_level, read_only_mode }. Routes: `GET /admin` (dashboard), `GET /admin/users` + toggle grant/revoke, `GET /admin/repos` + nuclear delete, `GET /admin/flags` + save. All mutations audit-logged.
- **F4** — Billing + quotas → ✅ shipped. `src/lib/billing.ts` + `src/routes/billing.tsx`, tables `billing_plans` + `user_quotas` (migration 0020, seeded with free/pro/team/enterprise). `FALLBACK_PLANS` mirror the seeds so billing works pre-migration. Helpers: `getUserQuota` (auto-initialises free row on first read), `bumpUsage`, `checkQuota` (fail-open), `wouldExceedRepoLimit`, `resetIfCycleExpired`. Routes: `GET /settings/billing` (personal view with usage bars + plan cards), `GET /admin/billing` (site-admin plan override), `POST /admin/billing/:userId/plan`.

### BLOCK G — Mobile + client
- **G1** — PWA manifest + service worker → ✅ shipped. `src/routes/pwa.ts` serves `/manifest.webmanifest`, `/sw.js`, `/icon.svg`; `Layout` injects `<link rel="manifest">` + a tiny SW registration script. Service worker is network-first for HTML + skips `.git/`/`/api/`/`/login*` routes.
- **G2** — GraphQL API mirror of REST → ✅ shipped. `src/lib/graphql.ts` is a dependency-free recursive-descent parser + executor over a fixed schema (viewer, user, repository, search, rateLimit). `src/routes/graphql.ts` serves `POST /api/graphql` + a GraphiQL-lite explorer at `GET /api/graphql`. Queries only; writes stay on REST.
- **G3** — Official CLI (`gluecron`) → ✅ shipped. `cli/gluecron.ts` is a Bun-compilable single-file CLI. Commands: `login`, `whoami`, `repo ls/show/create`, `issues ls`, `gql`, `host`, `version`. Config in `~/.gluecron/config.json` (0600). Talks to the server via REST + GraphQL.
- **G4** — VS Code extension → ✅ shipped. `vscode-extension/` contains package.json + `src/extension.ts`. Commands: `gluecron.explainFile`, `gluecron.openOnWeb`, `gluecron.searchSemantic`, `gluecron.generateTests`. Detects Gluecron remotes via `git config remote.origin.url`. Settings: `gluecron.host` + `gluecron.token`.

### BLOCK I — Filling parity gaps
- **I1** — Archive / unarchive repository → ✅ shipped. `src/routes/repo-settings.tsx` archive/unarchive toggle (existing `repositories.is_archived` column). `RepoHeader` surfaces an "Archived" badge.
- **I2** — Template repositories → ✅ shipped. `drizzle/0022_repo_templates.sql` adds `is_template` column + partial index. `src/routes/templates.ts` serves `POST /:owner/:repo/use-template` (git clone --bare into caller's namespace, fresh `activity_feed` entry). Settings UI gains a "Mark as template" toggle. Public repo page renders a prominent "Use this template" CTA for non-owners.
- **I3** — Repository transfer → ✅ shipped. `drizzle/0022_repo_templates.sql` adds `repo_transfers` audit table. `src/routes/repo-settings.tsx` `POST /:owner/:repo/settings/transfer` (validate target user exists, reject name conflicts, update `owner_id`, log to `repo_transfers`).
- **I4** — Generic command palette → ✅ shipped. `src/views/layout.tsx` injects a Cmd+K palette with ~20 canonical destinations (Dashboard, Explore, Notifications, Ask AI, Create repo, Marketplace, Installed apps, Register app, Shortcuts, Settings, 2FA, Passkeys, PATs, Billing, Audit, Gists, GraphQL, Admin, Theme). Fuzzy-match, arrow-key navigation, Esc/backdrop to close.
- **I5** — Code scanning UI → ✅ shipped. `src/routes/code-scanning.tsx` `GET /:owner/:repo/security` aggregates `gate_runs` matching `%scan%`/`%security%` (last 100), computes latest-per-gate status, renders failed/repaired/total summary cards + per-scanner status list + recent-runs table. Private-repo visibility enforced. Zero new tables — pure surfacing layer.
- **I6** — Sponsors → ✅ shipped. `drizzle/0023_sponsors.sql` adds `sponsorship_tiers` (maintainer_id, name, monthly_cents, one_time_allowed, is_active) + `sponsorships` (sponsor_id, maintainer_id, tier_id, amount_cents, kind, note, is_public, cancelled_at). `src/routes/sponsors.tsx` serves public `/sponsors/:username` (tier cards + recent public sponsors join) + maintainer `/settings/sponsors` (tier CRUD, soft-retire via is_active=false, activity list). Payment rails deferred — v1 captures intent + thank-you notes.
- **I7** — Weekly email digest → ✅ shipped. `drizzle/0024_email_digest.sql` adds `users.notify_email_digest_weekly` + `last_digest_sent_at`. `src/lib/email-digest.ts` exposes `composeDigest`/`sendDigestForUser`/`sendDigestsToAll` (never-throws). Pulls notifications + failed/repaired gate_runs + merged PRs from the last 7d, composes escaped HTML + plaintext, and sends via the shared email provider. `/settings/digest/preview` renders the digest inline for self-preview; `/admin/digests` gives site admins a "Send now" trigger + single-user preview, audit-logged as `admin.digests.run`/`admin.digests.preview`.
- **I8** — Symbol / xref navigation → ✅ shipped. `drizzle/0025_code_symbols.sql` adds a `code_symbols` table. `src/lib/symbols.ts` provides a regex-based top-level extractor for ts/js/py/rs/go/rb/java/kt/swift. On-demand indexing via `POST /:owner/:repo/symbols/reindex` walks the default-branch tree, caps at 2000 files/1MB each, replaces the prior set. Browse at `/:owner/:repo/symbols` (A–Z + per-kind counts), search via `/symbols/search?q=`, inspect at `/symbols/:name`. 14 new tests.
- **I9** — Repository mirroring → ✅ shipped. `drizzle/0026_repo_mirrors.sql` adds `repo_mirrors` (one-per-repo config) + `repo_mirror_runs` (audit log). `src/lib/mirrors.ts` provides URL validation (https/http/git only, no ssh/file/paths/shell metas), credentials-redaction for logs, and `runMirrorSync` that shells out to `git fetch --prune --tags` with a 5-min timeout and `GIT_TERMINAL_PROMPT=0`. `src/routes/mirrors.tsx` serves owner-only `/:owner/:repo/settings/mirror` + site-admin `/admin/mirrors/sync-all`. 17 new tests.
- **I10** — Enterprise SSO via OIDC → ✅ shipped. `drizzle/0027_sso_oidc.sql` adds `sso_config` (singleton `id='default'` row with issuer + authorize/token/userinfo endpoints + client credentials + scopes + optional email-domain allow-list + `auto_create_users` toggle) and `sso_user_links` (maps local user to IdP `sub`, unique per-subject). `src/lib/sso.ts` exposes `buildAuthorizeUrl`/`exchangeCode`/`fetchUserinfo`/`findOrCreateUserFromSso` pure helpers — plain OIDC auth-code flow, no XML / no signature verification dep. `src/routes/sso.tsx` serves site-admin `/admin/sso` config page, `/login/sso` initiator (state + nonce cookies, 10-min TTL), `/login/sso/callback` exchanger + session issuer, plus `POST /settings/sso/unlink` for users. `/login` renders "Sign in with &lt;provider name&gt;" when enabled. 24 new tests (pure helpers + route-auth smokes).

### BLOCK J — Beyond-parity advanced features
- **J1** — Dependency graph → ✅ shipped. `drizzle/0028_repo_dependencies.sql` adds `repo_dependencies` (ecosystem + name + version_spec + manifest_path + is_dev + commit_sha) with indexes on `(repository_id, ecosystem)` + `(name)`. `src/lib/deps.ts` parses seven manifest formats (package.json, requirements.txt, pyproject.toml, go.mod, Cargo.toml, Gemfile, composer.json) without a TOML library — each parser is defensive and returns `[]` on malformed input. Walks default-branch tree (max 200 manifests, 1MB each), replaces the prior set on reindex. `src/routes/deps.tsx` serves `/:owner/:repo/dependencies` (grouped by ecosystem with per-ecosystem counts) + owner-only `POST /dependencies/reindex`. Reverse-dep lookup via `repositoriesDependingOn(ecosystem, name)` for future "who depends on me" network-graph UI. 21 new tests.
- **J2** — Security advisories (Dependabot-style) → ✅ shipped. `drizzle/0029_security_advisories.sql` adds `security_advisories` (GHSA + CVE IDs, severity, affected range, fixed version) + `repo_advisory_alerts` (per-repo match state with open/dismissed/fixed, unique on `(repo, advisory, manifest_path)`). `src/lib/advisories.ts` ships a 12-entry seed list (log4shell, lodash, minimist, vm2, urllib3, jwt-go, etc.), a minimal version-range matcher (`satisfiesRange` + `rangeMatches`) that handles `<`/`<=`/`>`/`>=`/`=` clauses including compound ranges, and `scanRepositoryForAlerts(repoId)` which cross-references J1 dep rows against the advisory list — inserts new alerts, reopens fixed-then-regressed ones, auto-closes alerts whose dep went away. `src/routes/advisories.tsx` serves `/:owner/:repo/security/advisories` (open), `/all` (everything), owner-only `POST /scan`, and per-alert `POST /:id/dismiss` + `POST /:id/reopen` with audit-log entries. 27 new tests (version parser, range matcher, seed shape, route auth).
- **J3** — Commit signature verification (GPG + SSH "Verified" badge) → ✅ shipped. `drizzle/0030_signing_keys.sql` adds `signing_keys` (per-user GPG/SSH pubkeys, unique on `(key_type, fingerprint)`) + `commit_verifications` (memoised per-commit result, unique on `(repo, sha)`). `src/lib/signatures.ts` extracts `gpgsig` / `gpgsig-sha256` from raw commit objects (`getRawCommitObject` added to `src/git/repository.ts`), unarmors PGP + SSH signature blobs, walks OpenPGP packet streams for Issuer Fingerprint (subpacket 33) / Issuer Key ID (subpacket 16), parses the SSHSIG inner publickey field, and SHA-256 fingerprints SSH wire-format keys. Identity matching via fingerprint → optional email check → cached. `src/routes/signing-keys.tsx` serves `GET/POST /settings/signing-keys` + `POST /settings/signing-keys/:id/delete`, audit-logged. `CommitList` + single commit view render a green "Verified" badge when cached `verified=true`. 29 new tests (extraction, unarmor, packet walker, SSH fp, end-to-end fast paths, route auth).
- **J4** — User following + personalised feed → ✅ shipped. `drizzle/0031_user_follows.sql` adds `user_follows` (composite PK on `(follower_id, following_id)`, CHECK constraint rejecting self-follows, reverse-lookup index on `following_id`). `src/lib/follows.ts` exposes `followUser/unfollowUser/isFollowing/listFollowers/listFollowing/followCounts` + `feedForUser(userId, limit)` which joins `activity_feed` against the follow set (bounded to 200 edges) and filters out private repos the viewer doesn't own. `src/routes/follows.tsx` serves `POST /:user/follow` + `/:user/unfollow` (auth-gated, audit-logged), public `GET /:user/followers` + `/:user/following`, and `GET /feed` (personalised timeline). Follow button + follower/following counts added to the user profile page via `src/routes/web.tsx`. Reserved-name set protects fixed paths (`login`, `settings`, `feed`, etc.). 8 new tests (verb table + route auth).
- **J5** — Profile READMEs → ✅ shipped. User profile page at `/:owner` now attempts to render `<user>/<user>/README.md` (GitHub convention) or `<user>/.github/README.md` (org-style fallback) as the hero panel above the repo list. No schema changes — reuses `getReadme` / `renderMarkdown` + `repoExists` from the git layer. Failures are silent; missing repo just hides the panel. 2 smoke tests.
- **J6** — Repository rulesets (push policy engine) → ✅ shipped. `drizzle/0032_repo_rulesets.sql` adds `repo_rulesets` (unique on `(repository_id, name)`, enforcement enum active/evaluate/disabled) + `ruleset_rules` (JSON params). `src/lib/rulesets.ts` exposes six rule types (`commit_message_pattern`, `branch_name_pattern`, `tag_name_pattern`, `blocked_file_paths`, `max_file_size`, `forbid_force_push`) + the pure evaluator `evaluatePush(rulesets, ctx) → {allowed, violations}`. Helpers: glob-lite matcher (`globToRegex`), defensive `parseParams`. CRUD: `listRulesetsForRepo`, `getRuleset`, `createRuleset`, `updateRulesetEnforcement`, `deleteRuleset`, `addRule`, `deleteRule`. `src/routes/rulesets.tsx` serves owner-only UI at `/:owner/:repo/settings/rulesets` (list + create), `/:id` (detail, enforcement toggle, add rule), `/:id/delete`, `/:id/rules/:ruleId/delete`. 23 new tests covering each rule type, enforcement modes, glob edge cases, and route-auth redirects.
- **J7** — Closing keywords auto-close issues on PR merge → ✅ shipped. `src/lib/close-keywords.ts` exports pure `extractClosingRefs(text)` and `extractClosingRefsMulti(sources[])` — scans for `(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)[:|-]? #N` with case-insensitive, punctuation-tolerant, word-boundary-respecting matching. Rejects cross-repo refs (`owner/repo#N`), embedded-in-word verbs (`disclose`, `unresolved`), and non-positive numbers. Wired into the PR merge handler in `src/routes/pulls.tsx` — after a successful merge, scans `pr.title + pr.body`, looks up each referenced open issue in the same repo, closes it, and posts a "Closed by pull request #N" comment. Wrapped in try/catch so close-keyword failures never block the merge redirect. 14 new tests covering verb forms, punctuation variants, de-dup+sort, cross-repo rejection, embedded-word rejection, case-insensitivity, and multi-source merging. Total suite 749/749.
- **J8** — Commit status API (external CI signals) → ✅ shipped. `drizzle/0033_commit_statuses.sql` adds `commit_statuses` (unique on `(repository_id, commit_sha, context)`, state vocabulary pending/success/failure/error). `src/lib/commit-statuses.ts` exposes pure helpers (`isValidSha`, `isValidState`, `sanitiseContext`, `reduceCombined`) and DB helpers (`setStatus` with delete-then-insert upsert, `listStatuses`, `combinedStatus`). `src/routes/commit-statuses.ts` serves `POST /api/v1/repos/:owner/:repo/statuses/:sha` (requireAuth + owner check), `GET /api/v1/repos/:owner/:repo/commits/:sha/statuses` (list, private-repo visibility), `GET /api/v1/repos/:owner/:repo/commits/:sha/status` (combined rollup). Commit detail view now renders a "Checks" pill row when statuses exist, colour-coded per state with clickable target URLs. 18 new tests covering the pure helpers + route auth + invalid-sha rejection. Total suite 767/767.

### BLOCK H — Marketplace
- **H1** — App marketplace → ✅ shipped. `src/routes/marketplace.tsx` + `src/lib/marketplace.ts` + `drizzle/0021_marketplace_and_apps.sql` (5 tables: `apps`, `app_installations`, `app_bots`, `app_install_tokens`, `app_events`). Routes: `GET /marketplace` (public directory with search), `GET /marketplace/:slug` (detail + install CTA), `POST /marketplace/:slug/install` (user-target install in v1), `POST /marketplace/installations/:id/uninstall`, `GET /settings/apps` (personal list), `GET+POST /developer/apps-new` (register), `GET /developer/apps/:slug/manage` (event log + install count), `POST /developer/apps/:slug/tokens/new` (show-once token). Install idempotent via soft-update on existing non-uninstalled row.
- **H2** — GitHub Apps equivalent (bot identities + installation tokens) → ✅ shipped. Same schema as H1: every app gets a `<slug>[bot]` row in `app_bots`. `generateBearerToken()` produces `ghi_`-prefixed bearers; `hashBearer` (sha256) is the only form persisted. `verifyInstallToken(token)` returns `{installation, app, botUsername, permissions}` or `null` (checks revoked/expired/uninstalled/suspended). Permission vocabulary: `contents:read/write`, `issues:read/write`, `pulls:read/write`, `checks:read/write`, `deployments:read/write`, `metadata:read``hasPermission` implements write→read implication.

### BLOCK K — Claude-native development loop
The "feature description → live site, no human merge step for the routine cases" loop. Three sub-blocks shipped together (PR #62) on 2026-05-13.
- **K1** — MCP write surface → ✅ shipped. Extends `src/lib/mcp-tools.ts` with 10 write tools (`gluecron_create_issue` / `_comment_issue` / `_close_issue` / `_reopen_issue` / `_create_pr` / `_get_pr` / `_list_prs` / `_comment_pr` / `_merge_pr` / `_close_pr`). Per-tool auth gate: `ctx.userId === null``ERR_INVALID_PARAMS`. Write-access gate via `src/middleware/repo-access.ts``ERR_METHOD_NOT_FOUND` on denial (same shape as read tools so private repos don't leak). `merge_pr` re-uses the entire manual-merge gating pipeline. HTTP route still on `softAuth` so the original 5 read tools keep working anonymously. `src/__tests__/mcp-write.test.ts` — 34 tests covering happy path + auth gate + write-access gate per tool. Mock-isolation hardened via spread-from-real, defensive `users` defaults, and `afterAll` row resets so the file no longer poisons downstream tests in the same `bun test` run.
- **K2** — AI-gated auto-merge → ✅ shipped. `src/lib/auto-merge.ts` (399 lines), `drizzle/0040_branch_protection_auto_merge.sql`, settings checkbox on `src/routes/gates.tsx`. Pure `decideAutoMerge(facts)` + DB orchestrator `evaluateAutoMerge(ctx, opts?)`. Decision rules (all must hold): matching branch_protection rule with `enable_auto_merge=true`, PR not draft, manual-merge `evaluateProtection` passes, AI approval per `aiCommentLooksApproved` when `requireAiApproval=true`, optional size cap. 19 tests covering each block reason + the `blocking-non-empty iff merge=false` invariant. Side-effect-free; only K3 calls it.
- **K3** — AI-driven autopilot → ✅ shipped. Two new tick tasks on the locked autopilot ticker: `auto-merge-sweep` (calls K2 every tick, performs merges via `performMerge`, audits `auto_merge.evaluated`/`auto_merge.merged`/`auto_merge.merge_failed`, posts `<!-- gluecron:auto-merge:v1 -->` marker) and `ai-build-from-issues` (open issues labelled `ai:build` → spec-to-PR dispatch, posts `<!-- gluecron:ai-build:v1 -->` marker). Shared `src/lib/pr-merge.ts performMerge(prId)` factored from `routes/pulls.tsx`. `src/lib/ai-build-tasks.ts runAiBuildTaskOnce(deps)` orchestrator with 5 DI'd collaborators. 14 tests covering draft skip, archived skip, marker dedup, AUTOPILOT_DISABLED short-circuit, AI-key absence short-circuit, and "merge fn called exactly once per merge=true PR".

### BLOCK L — Hook / mouth / taste — packaging the Claude-native pitch
The marketing + acquisition + activation + retention package. Ten sub-blocks shipped together (PR #62) on 2026-05-13. The whole funnel:
  - **Hook** (acquisition surface): L4 social-proof counters · L5 /vs-github · L10 hero · L8 /pricing
  - **Mouth** (activation): L2 one-command install · L6 GitHub OIDC · L7 Claude Code skills · L3 live /demo
  - **Taste** (retention): L1 Sleep Mode (marquee) · L9 AI hours saved counter

- **L1** — Sleep Mode → ✅ shipped. Marquee feature. User toggle + UTC hour preference (`drizzle/0041_sleep_mode.sql` adds `users.sleep_mode_enabled` + `users.sleep_mode_digest_hour_utc`). `src/lib/sleep-mode.ts` (536 lines) composes a "while-you-slept" daily digest of K3 autopilot work (auto-merges, ai:build dispatches, AI reviews, secret-scan fixes). New autopilot task `sleep-mode-digest` fires daily per user's preferred UTC hour with 23h cooldown. Public `/sleep-mode` marketing page. Settings UI in `src/routes/settings.tsx` + "Preview digest now" button at `/settings/sleep-mode/preview`. Re-uses `email-digest.ts` escape helpers + the `sendEmail()` provider. 17 tests.
- **L2** — One-command install → ✅ shipped. `curl -sSL gluecron.com/install | bash` does the whole onboarding in one shot: signs in, mints a fresh `glc_` PAT (admin scope), writes Claude Desktop's `claude_desktop_config.json` with a `gluecron` MCP server entry, optionally imports the user's current GitHub repo, mkdirs `~/.claude/skills/` and drops L7's skill files. `scripts/install.sh` (303 lines). New endpoint `POST /api/v2/auth/install-token` (session-cookie-only auth; explicit 401 on Bearer to prevent token escalation; audits `auth.install_token.created`). Public `GET /install` serves the script (`Cache-Control: public, max-age=300`). 11 tests (9 + 2 DB-gated skip).
- **L3** — Live /demo page → ✅ shipped. Anonymous visitors land on `/demo` and watch K3 autopilot work in real time. Three tiles refreshing every 30s: queued ai:build issues, recent auto-merges, AI reviews today. Live activity feed pinned at the bottom. `src/routes/demo.tsx` (409 lines) SSR + plain-JS poller. `src/lib/demo-activity.ts` (510 lines) — pure helpers (`listQueuedAiBuildIssues`, `listRecentAutoMerges`, `listRecentAiReviews`, `listDemoActivityFeed`), all scoped to demo-owned repos, never throw, 30s LRU cache. `src/lib/demo-activity-seed.ts` (additive sibling to the locked `demo-seed.ts`, called from `src/index.ts`) adds 1-2 `ai:build`-labelled issues per demo repo + an open and a merged PR + an `AI_REVIEW_MARKER` comment + an `auto_merge.merged` audit row so the tiles are populated. Four JSON endpoints `GET /api/v2/demo/{activity,queued,merges,reviews}`. 15 tests.
- **L4** — Public stats counters → ✅ shipped. `src/lib/public-stats.ts` (363 lines) computes 9 site-wide metrics over PUBLIC data only (every counter JOINs on `is_private=false` so private repos never leak). 5-min LRU cache. `emptyPublicStats()` zero-fallback on DB error. Re-uses `computeHoursSaved` from L9. New endpoint `GET /api/v2/stats` (`Cache-Control: public, max-age=300`). Landing page renders six animated count-up tiles after the hero (IntersectionObserver + `prefers-reduced-motion` fallback; static numbers stay in HTML for no-JS clients). 13 tests.
- **L5** — /vs-github comparison page → ✅ shipped. `src/routes/vs-github.tsx` (660 lines) — public marketing page at `GET /vs-github`. Hero + 26-row honest comparison table grouped into 4 categories (AI-native workflow, developer integration, hosting+workflow, pricing) + "killer move" Sleep Mode banner + 4-question objection FAQ + dual-CTA footer (Migrate from GitHub / Try the demo). Removed a stale shadow handler in `marketing.tsx` that was overlapping the URL. The legacy branch-diff `/:owner/:repo/compare/:spec?` route is untouched. 6 tests covering anon 200, content, all 10 AI-native rows, CTA hrefs, and the legacy route guard.
- **L6** — Sign in with GitHub → ✅ shipped. Reuses the I10 SSO machinery. `drizzle/0042_github_oauth.sql` seeds a new row id='github' in the existing `sso_config` table — schema unchanged. `src/lib/github-oauth.ts` (187 lines) — pure network-only helpers (`buildGithubAuthorizeUrl`, `exchangeGithubCode`, `fetchGithubUserinfo`, `fetchGithubPrimaryEmail`) handling GitHub's non-OIDC quirks: `Accept: application/json` on token endpoint, null email fallback via `/user/emails` primary+verified pick, no id_token. Subject keyed as `github:${id}` to coexist with enterprise SSO. New routes `/login/github`, `/login/github/callback`, `/admin/github-oauth`. "Sign in with GitHub" button on `/login` above the enterprise SSO option. 22 tests.
- **L7** — Claude Code skill bundle → ✅ shipped. Three skills shipped at the project root (`.claude/skills/`) and copied to `~/.claude/skills/` by the L2 install script: `gluecron-pr` (open / list / fetch / comment / merge / close PRs), `gluecron-issue` (create / list / comment / close / reopen issues), `gluecron-review` (act as a secondary AI reviewer on top of Gluecron's built-in Sonnet 4 review). Each SKILL.md has YAML frontmatter that names "Gluecron" so the Claude Code harness auto-invokes when the active repo's origin URL matches a Gluecron host. CLAUDE.md updated with a "Skills available for this project" section. 25 tests.
- **L8** — Free-tier UI polish → ✅ shipped. Pure UX over the existing F4 billing infrastructure; zero new billing logic. `src/routes/pricing.tsx` (641 lines) — public `GET /pricing`. Hero ("Free for the AI-curious. Pay only when you're ready to scale.") + four plan cards driven by `FALLBACK_PLANS` (Free/Pro/Team/Enterprise) with CTA to `/settings/billing?plan=` (or `/register?next=...` for anon). "What you get on the free tier" 13-feature 2-col block. Self-host vs Gluecron Cloud comparison. 5-question FAQ. `src/routes/billing.tsx` extended with a "this month" usage panel + "Upgrade to Pro" CTA for Free users + a "Detailed plan comparison" link to `/pricing`. Small "Free forever for the AI-curious" link added near the landing hero. Plan card text references K1/K2/K3/L1/L7/L9 features explicitly so visitors see what they get on Free.
- **L9** — AI hours saved counter → ✅ shipped. Retention feature. `src/lib/ai-hours-saved.ts` (413 lines) exports `computeHoursSaved(breakdown)` (pure, deterministic, used by L1 and L4 too) + `computeAiSavingsForUser(userId, opts?)` + `computeLifetimeAiSavingsForUser(userId)`. Formula: `prsAutoMerged*0.30 + issuesBuiltByAi*1.50 + aiReviewsPosted*0.25 + aiTriagesPosted*0.10 + aiCommitMsgs*0.05 + secretsAutoRepaired*0.50 + gateAutoRepairs*0.40` — heuristic constants documented inline. Dashboard renders a large gradient hero widget with this-week / all-time tabs + breakdown pills + "How is this calculated?" disclosure. New endpoint `GET /api/v2/me/ai-savings` so VS Code + CLI consume the same metric. 17 tests.
- **L10** — Marketing landing hero rewrite → ✅ shipped. `src/views/landing.tsx` — new gradient headline ("The git host built around Claude."), one-sentence subhead ("Label an issue. Walk away. Wake up to a merged PR."), one-line install snippet + copy button, three CTAs (`/register` / `/demo` / `/vs-github`), "what just happened" rail driven by L4's `publicStats`, "Three reasons to switch" 3-column section linking to Sleep Mode / `/import` / `/demo`, and a "How is this different from GitHub?" pull-quote linking to `/vs-github`. Preserves L4 counter tiles + L5 vs-github CTA + L7 git-remote caption + L8 free-tier link. `src/views/layout.tsx` extended additively with optional `description`/`ogTitle`/`ogDescription`/`ogType`/`twitterCard`/`fullTitle` props (render byte-identical when omitted, so the §4.7 locked contract holds). `src/routes/web.tsx` landing handler now passes SEO + OG props. 9 tests.

### BLOCK M — 2030 vision: developer painkiller features (2026-05-28+)

The "lightning-fast push → live site + zero friction" package. Owner directive: be the biggest painkiller for developers; lightning-fast push to bare metal to live site; never any caching issues; most advanced git host until 2030.

- **M0** — DORA metrics dashboard → ✅ shipped (`8d1483c`). `src/routes/dora.tsx``GET /:owner/:repo/insights/dora`. Six parallel DB queries (deployment freq, lead time, change failure rate, MTTR, gate pass rate, workflow success rate). DORA level badges (Elite/High/Medium/Low) with colour coding. Last-10-deployments table. All queries wrapped in try/catch for graceful degradation.
- **M1** — Push Watch page → ✅ shipped (`05ab9b1`). `src/routes/push-watch.tsx``GET /:owner/:repo/push/:sha`. Per-commit live status: gate results, deploy status, push-to-live latency computation. JSON API at `/api/repos/:owner/:repo/push-status/:sha`. 5-second JS poller stops at terminal state. Middleware scoped to specific paths (not `"*"`).
- **M2** — Org-level Secrets Manager → ✅ shipped (`05ab9b1`). `drizzle/0075_org_secrets.sql` + `src/lib/org-secrets.ts` + `src/routes/org-secrets.tsx`. AES-256-GCM encrypted, inline Drizzle table (schema.ts locked). Settings UI at `/orgs/:slug/settings/secrets`. Extends secrets resolution order: repo secrets > org secrets.
- **M7** — Cross-repo Code Search → ✅ shipped (`a2b3e99`). `src/routes/cross-repo-search.tsx``GET /search/code` + `GET /api/search/code`. Paginated keyword search across all accessible repos. Strategy: `code_chunks` ILIKE first (fast, uses semantic index), then `git grep` fallback (capped at 10 repos/20 files/5 lines per repo). Auth-aware: anonymous sees public only; authenticated sees own + collaborator repos. CSS under `.crs-*`.
- **M8** — Browser Push Notifications → ✅ shipped (`a2b3e99`). `src/lib/push-notify.ts` typed fan-out helpers (notifyDeploySuccess, notifyGateFailed, notifyPrMerged, notifyAiReview). `src/routes/push-notifications.tsx``GET /settings/notifications/push` opt-in UI + `/api/push/subscribe|unsubscribe|test`. VAPID implemented natively via Bun `crypto.subtle` (no npm dep). `drizzle/0076_push_subscriptions.sql` adds user notification prefs columns.
- **M9** — Developer Velocity Dashboard → ✅ shipped (`534afdd`+). `src/routes/velocity.tsx``GET /:owner/:repo/insights/velocity?window=7|30|90`. Per-developer metrics: PRs opened/merged, avg time-to-merge, code review activity. 4 team summary cards + PR speed buckets (Fast/Normal/Slow). 3 parallel DB queries. Insights sub-nav links DORA ↔ Velocity. CSS under `.vel-*`. Zero new tables.
- **M10** — Stale Branch Cleanup UI → ✅ shipped. `src/routes/stale-branches.tsx``GET /:owner/:repo/branches/stale` + `POST /:owner/:repo/branches/stale/delete`. Lists merged branches with PR links + age; checkbox select + one-click delete; never suggests deleting main/master/develop/staging/production. Owner-only delete, read-only view for others.
- **M11** — Create Branch from Issue → ✅ shipped (`c922868`). POST `/:owner/:repo/issues/:number/branch` (write-access gated). Creates a branch from the default branch SHA using existing `updateRef` + `resolveRef` git plumbing. Zero new DB tables. "Create branch" details-dropdown appears on open issues for authenticated write-access users, pre-fills branch name as `issue-<N>-<title-slug>`.
- **M12** — Repository Pulse → ✅ shipped. `src/routes/pulse.tsx``GET /:owner/:repo/pulse?window=1|7|30`. GitHub Pulse equivalent: issues opened/closed, PRs opened/merged/closed, commit count, active contributors, gate pass rate, code review count, top contributors grid, recent activity feed. All queries parallel + best-effort commit walk from git log. Zero new DB tables.
- **M3** — No-cache middleware → ✅ shipped (`f5b9ef5`). `src/middleware/no-cache.ts` stamps `Cache-Control: no-store, no-cache, must-revalidate` + `Pragma: no-cache` + `Vary: Cookie` on all `text/html` responses. Assets (CSS/JS/images) unaffected. Eliminates stale-page issues after login, deploy, or push.
- **M4** — Wider platform layout → ✅ shipped (`f5b9ef5`). All `max-width: 1240px``1440px` in `src/views/layout.tsx` (nav, main content, footer). Modern wide-screen utilisation for developer dashboards.
- **M5** — Clean user nav dropdown → ✅ shipped (`f5b9ef5`). Replaced 8+ top-level nav links with a polished user dropdown (avatar initials + caret trigger, Dashboard, PRs, Issues, Activity, Import, Profile, Settings, Tokens, Theme toggle, Sign out) + bell inbox icon with unread badge. Reduces cognitive load; keeps the nav scannable.
- **M6** — Zero-friction Claude Code integration → ✅ shipped (`f5b9ef5` + M6b). `src/routes/claude-integration.ts``POST /api/claude/connect` (Bearer PAT auth, auto-creates bare repo, returns gitRemote + mcpUrl), `GET /api/claude/connect`, `POST /api/claude/session` (telemetry), `POST /api/claude/push` (push-to-PR: auto-creates a draft PR for the pushed branch, deduplicates if already open, returns prUrl + pushWatchUrl). `src/routes/connect.tsx``/connect/claude-guide` public onboarding page.
- **M13** — PR Review Assignment Automation → ✅ shipped (`ace34ef`). `src/lib/reviewer-suggest.ts``suggestReviewers(owner, repo, headBranch, baseBranch, authorId, repoId)` runs `git log --format=%ae base..head` over changed files, resolves emails→users, intersects with repo owner + accepted collaborators, excludes PR author, caps at 5. `requestReview(prId, repoId, reviewerId, requesterId)` sends notify() + activityFeed entry (does NOT insert into prReviews to avoid corrupting countHumanApprovals). PR detail view gains "Suggested reviewers" section with avatar initials + "Request" form buttons (write-access only). POST `/:owner/:repo/pulls/:number/request-review` validates reviewer is repo owner OR accepted collaborator. CSS under `.prs-reviewer-avatar`.
- **M14** — Repository Health Score → ✅ shipped. `src/lib/health-score.ts``computeHealthScore(repoId)` runs 4 parallel DB queries (open advisories, gate pass rate 30d, avg PR TTM 90d, avg open issue age) and returns a 0-100 composite score with grade (Elite/Strong/Improving/Needs Attention). `src/routes/health-score.tsx``GET /:owner/:repo/insights/health`. CSS-only SVG circle gauge, grade badge, 4 component progress bars. Insights sub-nav: Insights / DORA / Velocity / Pulse / Health / Hot Files. Zero new DB tables. 9 tests.
- **M15** — PR Size Auto-Labels → ✅ shipped. `src/lib/pr-size.ts``computePrSize(owner, repo, base, head)` runs `git diff --numstat base...head`, sums lines changed, maps to XS/S/M/L/XL label with colour. Badge rendered in PR detail meta row next to state pill, with tooltip showing +added/−deleted breakdown. Zero new DB tables. 10 tests.
- **M16** — Hot Files Heatmap → ✅ shipped. `src/lib/hot-files.ts``getHotFiles(owner, repo, windowDays)` spawns `git log --numstat --since=N.days.ago --format=` and aggregates per-file churn (added+deleted). Risk tiers: high (auth/security/schema/db/middleware/crypto), medium (routes/api/lib/.sql), low (everything else). Top 50 files returned. `src/routes/hot-files.tsx``GET /:owner/:repo/insights/hotfiles?window=7|30|90`. Heat bar + risk pill per row. Insights sub-nav matches M14/M9. Zero new DB tables. 14 tests.
- **M17** — Site-wide width pass ("stop the thin") → ✅ shipped. Follow-up to M4: M4 widened the `src/views/layout.tsx` shell to 1440px, but ~90 per-page content containers (`*-wrap` / `*-container` / `*-page`) were still capped at 880–1180px, leaving a narrow centered column inside a wide shell — the "everything's thin" complaint. Shipped in two passes via the one-shot, conservative `scripts/widen-layout.ts` (only rewrites lines that set both a `*-wrap/container/page` selector AND `max-width: Npx; margin: 0 auto`, so prose measure / hero-inner / media-query widths are untouched). **Final state (near-full-bleed, per owner "do it all"):** shell (nav / `main` / footer) 1440 → **1920px**; per-page tiers — wide app/dashboard/list/admin/explore/insights → **1680**, medium pages → **1320**, settings/forms → **1200**, small confirm/detail cards → **900**, tiny claim/dialog views (< 740px) left compact. Inline-styled branches/tags containers in `src/routes/web.tsx` → 1680. 87 files, pure CSS-value edits — no logic, routes, or `Layout` props changed; full suite unchanged at 2712 pass / 28 pre-existing fail. NOTE: the script's pass-2 tier map is **not idempotent** (1320 is both a medium-tier output and a wide-tier input) — re-run only from a clean tree, never twice in a row.

---

## 4. LOCKED BLOCKS (DO NOT UNDO)

Everything below is committed, tested, and load-bearing. **Do not delete, rename, or semantically change without owner permission.**

### 4.1 Infrastructure (locked)
- `src/app.tsx` — route composition, middleware order, error handlers
- `src/index.ts` — Bun server entry
- `src/lib/config.ts` — env getters (late-binding)
- `src/db/schema.ts` — 95 tables. New tables only via new migration.
- `src/db/index.ts` — lazy proxy DB connection
- `src/db/migrate.ts` — migration runner
- `drizzle/0000_initial.sql`, `drizzle/0001_green_ecosystem.sql` — migrations
- `drizzle/0004_org_owned_repos.sql` (Block B2) — migration, never edited in place
- `drizzle/0005_totp_2fa.sql` (Block B4) — migration, never edited in place
- `drizzle/0006_webauthn_passkeys.sql` (Block B5) — migration, never edited in place
- `drizzle/0007_oauth_provider.sql` (Block B6) — migration, never edited in place
- `drizzle/0008_workflows.sql` (Block C1) — migration, never edited in place
- `drizzle/0009_packages.sql` (Block C2) — migration, never edited in place
- `drizzle/0010_pages.sql` (Block C3) — migration, never edited in place
- `drizzle/0011_environments.sql` (Block C4) — migration, never edited in place
- `drizzle/0012_ai_native.sql` (Block D) — migration, never edited in place. Adds `codebase_explanations`, `dep_update_runs`, `code_chunks`.
- `drizzle/0013_discussions.sql` (Block E2) — migration, never edited in place. Adds `discussions`, `discussion_comments`.
- `drizzle/0014_gists.sql` (Block E4) — migration, never edited in place. Adds `gists`, `gist_files`, `gist_revisions`, `gist_stars`.
- `drizzle/0015_projects.sql` (Block E1) — migration, never edited in place. Adds `projects`, `project_columns`, `project_items`.
- `drizzle/0016_wikis.sql` (Block E3) — migration, never edited in place. Adds `wiki_pages`, `wiki_revisions`.
- `drizzle/0017_merge_queue.sql` (Block E5) — migration, never edited in place. Adds `merge_queue_entries` (with partial unique index on `pull_request_id WHERE state IN ('queued','running')`).
- `drizzle/0018_required_checks.sql` (Block E6) — migration, never edited in place. Adds `branch_required_checks`.
- `drizzle/0019_protected_tags.sql` (Block E7) — migration, never edited in place. Adds `protected_tags`.
- `drizzle/0020_analytics_and_admin.sql` (Block F) — migration, never edited in place. Adds `repo_traffic_events`, `system_flags`, `site_admins`, `billing_plans` (seeded free/pro/team/enterprise), `user_quotas`.
- `drizzle/0021_marketplace_and_apps.sql` (Block H) — migration, never edited in place. Adds `apps`, `app_installations` (partial unique index on `(app_id, target_type, target_id) WHERE uninstalled_at IS NULL`), `app_bots` (one-per-app, `<slug>[bot]` username), `app_install_tokens` (sha256 hash, expires_at, revoked_at), `app_events` (audit trail).
- `drizzle/0022_repo_templates.sql` (Block I2+I3) — migration, never edited in place. Adds `repositories.is_template` (partial index where true) + `repo_transfers` audit table.
- `drizzle/0023_sponsors.sql` (Block I6) — migration, never edited in place. Adds `sponsorship_tiers` + `sponsorships` tables.
- `drizzle/0024_email_digest.sql` (Block I7) — migration, never edited in place. Adds `users.notify_email_digest_weekly` + `users.last_digest_sent_at`.
- `drizzle/0025_code_symbols.sql` (Block I8) — migration, never edited in place. Adds `code_symbols` table with indexes on `(repository_id, name)` + `(repository_id, path)`.
- `drizzle/0026_repo_mirrors.sql` (Block I9) — migration, never edited in place. Adds `repo_mirrors` (unique on `repository_id`) + `repo_mirror_runs`.
- `drizzle/0027_sso_oidc.sql` (Block I10) — migration, never edited in place. Adds `sso_config` singleton (`id='default'`) + `sso_user_links` (`subject` unique, FK to `users` with ON DELETE CASCADE).
- `drizzle/0028_repo_dependencies.sql` (Block J1) — migration, never edited in place. Adds `repo_dependencies` with indexes on `(repository_id, ecosystem)` + `(name)`.
- `drizzle/0029_security_advisories.sql` (Block J2) — migration, never edited in place. Adds `security_advisories` (`ghsa_id` unique) + `repo_advisory_alerts` (unique on `(repository_id, advisory_id, manifest_path)`, status index).
- `drizzle/0030_signing_keys.sql` (Block J3) — migration, never edited in place. Adds `signing_keys` (unique on `(key_type, fingerprint)`) + `commit_verifications` (unique on `(repository_id, commit_sha)`).
- `drizzle/0031_user_follows.sql` (Block J4) — migration, never edited in place. Adds `user_follows` (composite PK on `(follower_id, following_id)`, CHECK no-self-follow, reverse index on `following_id`).
- `drizzle/0032_repo_rulesets.sql` (Block J6) — migration, never edited in place. Adds `repo_rulesets` (unique on `(repository_id, name)`, enforcement enum) + `ruleset_rules` (JSON params).
- `drizzle/0033_commit_statuses.sql` (Block J8) — migration, never edited in place. Adds `commit_statuses` (unique on `(repository_id, commit_sha, context)`, state vocabulary pending/success/failure/error).
- `drizzle/0034_processed_events.sql` (Signal Bus P1) — migration, never edited in place. Idempotency table for inbound deploy events; sha256-keyed dedupe of at-least-once delivery from Crontech.
- `drizzle/0035_repo_collaborators.sql` — migration, never edited in place. Adds `repo_collaborators` (unique on `(repository_id, user_id)`, role enum read/write/admin, `invited_by` nullable, `accepted_at` nullable).
- `drizzle/0036_invite_token_hash.sql` — migration, never edited in place. Adds `invite_token_hash` column (sha256 of plaintext) to `repo_collaborators` for email-flow invites; NULL on legacy auto-accepted rows.
- `drizzle/0037_workflow_engine_v2.sql` — migration, never edited in place. Strictly additive to Block C1 (`drizzle/0008_workflows.sql` stays locked). Adds `workflow_secrets` (AES-256-GCM, `(repo, name)` unique), `workflow_dispatch_inputs` (per-workflow input schema), `workflow_run_cache` (content-addressable cache, repo/branch/tag scoped), `workflow_runner_pool` (warm-runner registry).
- `drizzle/0038_deployment_ready_after.sql` — migration, never edited in place. Adds `deployments.ready_after` (timestamptz, NULL = no wait) + partial index. Backs the wait-timer enforcement for environment approvals (was previously a stub per Block C4 v1).
- `drizzle/0040_branch_protection_auto_merge.sql` (Block K2) — migration, never edited in place. Adds `branch_protection.enable_auto_merge` (boolean, default false) — strict opt-in so the K3 autopilot sweep is default-deny per branch. Slot 0040 (not 0039) because `drizzle/0039_repair_flywheel.sql` was already taken; additive only, never replaces an earlier migration.
- `drizzle/0041_sleep_mode.sql` (Block L1) — migration, never edited in place. Adds `users.sleep_mode_enabled` (boolean, default false) + `users.sleep_mode_digest_hour_utc` (integer 0-23, default 9). Strictly additive.
- `drizzle/0042_github_oauth.sql` (Block L6) — migration, never edited in place. INSERTs a seed row keyed `id='github'` into the existing `sso_config` table so the L6 GitHub-OIDC flow can coexist with the I10 enterprise-OIDC singleton. Schema unchanged; pure seed.

### 4.2 Git layer (locked)
- `src/git/repository.ts` — tree / blob / commits / diff / branches / blame / search / raw / tags / commitsBetween
- `src/git/protocol.ts` — Smart HTTP pkt-line
- `src/hooks/post-receive.ts` — CODEOWNERS sync, gates, auto-deploy, webhook fan-out

### 4.3 Auth + security (locked)
- `src/lib/auth.ts` — bcrypt, session tokens
- `src/middleware/auth.ts` — softAuth + requireAuth. Accepts three auth inputs: session cookie (web), OAuth access token (`glct_` prefix, Block B6), and personal access token (`glc_` prefix, Block C2). Invalid bearer → 401 JSON. Cookie flow → /login redirect.
- `src/middleware/rate-limit.ts` — fixed-window limiter
- `src/middleware/request-context.ts` — request-ID
- `src/lib/security-scan.ts``SECRET_PATTERNS` (exported) + `scanForSecrets` + `aiSecurityScan`
- `src/lib/codeowners.ts` — parser + `ownersForPath` (last-match-wins); team expansion helpers for `@org/team` (Block B3)
- `src/lib/totp.ts` (Block B4) — TOTP enroll / verify / recovery codes
- `src/lib/webauthn.ts` (Block B5) — WebAuthn registration + assertion helpers
- `src/lib/oauth.ts` (Block B6) — OAuth 2.0 provider: authorization code grant, token issuance, scope enforcement
- `src/lib/workflow-parser.ts` (Block C1) — YAML subset parser for `.gluecron/workflows/*.yml`. Exports `parseWorkflow(src)` returning `{ ok, workflow | error }`. Never throws.
- `src/lib/workflow-runner.ts` (Block C1) — shell executor. Exports `executeRun`, `drainOneRun`, `enqueueRun`, `startWorker`. Clones repo to tmpdir, runs each job via `Bun.spawn(["bash","-c",step.run])` with SIGTERM→SIGKILL timeouts, size-capped stdout/stderr, cleans up in `finally`.
- `src/lib/packages.ts` (Block C2) — npm protocol helpers: `parsePackageName`, `computeShasum` (sha1), `computeIntegrity` (sha512 base64), `buildPackument`, `resolveRepoFromPackageJson`, `parseRepoUrl`, `tarballFilename`. Pure functions.
- `src/lib/pages.ts` (Block C3) — `onPagesPush` (never throws), `resolvePagesPath` (probe list including pretty URLs + traversal strip), `contentTypeFor` (MIME).
- `src/lib/environments.ts` (Block C4) — `matchGlob`, `listEnvironments`, `getOrCreateEnvironment`, `getEnvironmentByName`, `isReviewer`, `reviewerIdsOf`, `allowedBranchesOf`, `computeApprovalState` (now also returns `readyAfter`), `reduceApprovalState`, `recordApproval`, `requiresApprovalFor`. Wait-timer enforcement: `latestApprovalAt(decided)`, `computeReadyAfter(env, decided)`, `releaseExpiredWaitTimers(now)` flip `status="waiting_timer"``"pending"` once `ready_after` elapses. Wired into autopilot as the `wait-timer-release` task. Empty reviewers list → repo owner approves. Any rejection hard-stops.

### 4.3.1 Permissions + collaborators (locked)
- `src/middleware/repo-access.ts` — centralised permission check applied to all repo-write routes. Resolves owner / collaborator-role / org-team-role and rejects unauthorised mutations.
- `src/lib/invite-tokens.ts` — opaque single-use invite secret generator + sha256 hasher. Hash stored in `repo_collaborators.invite_token_hash`; plaintext is delivered once via the invite email link.
- `src/routes/collaborators.tsx` — per-repo collaborator add/list/remove. Owner-or-admin only. Audit-logged.
- `src/routes/team-collaborators.tsx` — bulk-grant: invite every accepted member of a given team to a repo as a collaborator in one action.
- `src/routes/invites.tsx` — invite acceptance landing page. Verifies token hash, sets `accepted_at`, redirects to repo.

### 4.4 AI layer (locked)
- `src/lib/ai-client.ts` — Anthropic client + model constants
- `src/lib/ai-generators.ts` — commit / PR / changelog / issue-triage / **pull-request-triage (D3)**. `IssueTriage` interface is exported (was module-local until 2026-04-30 when issue-triage.ts started consuming it). `generatePrSummary` + `generateCommitMessage` + `triageIssue` are now wired into routes (previously orphan exports).
- `src/lib/ai-chat.ts` — conversational chat
- `src/lib/ai-review.ts` — PR code review
- `src/lib/auto-repair.ts` — worktree-backed repair commits
- `src/lib/merge-resolver.ts` — AI merge conflict resolution
- `src/lib/ai-explain.ts` (Block D6) — `explainCodebase(...)` + `getCachedExplanation(...)`. Samples up to ~25 representative files (~60KB cap), Sonnet 4, upserts into `codebase_explanations`. Fallback to README-ish synthesis when no key. Never throws.
- `src/lib/ai-completion.ts` (Block D9) — `completeCode({prefix, suffix?, language?, maxTokens?, repoHint?})` via Haiku. Inline LRU (size 200, 5-min TTL) keyed on sha256 of prefix+suffix+language. Code-fence stripping. Never throws. `__test` bundle exposed.
- `src/lib/dep-updater.ts` (Block D2) — `parseManifest`, `queryNpmLatest`, `planUpdates` (injectable `fetchLatest`), `applyBumps`, `runDepUpdateRun`. Creates `gluecron/dep-update-<ts>` branch via git plumbing + opens a PR row. Never throws.
- `src/lib/semantic-search.ts` (Block D1) — `tokenize`, `hashEmbed` (512-dim L2-normalised FNV-1a + sign trick), `embedBatch` (Voyage `voyage-code-3` when `VOYAGE_API_KEY` set, else fallback), `chunkFile`, `isCodeFile`, `indexRepository`, `searchRepository`, `cosine`, `isEmbeddingsProviderAvailable`, `__test` bundle.
- `src/lib/ai-incident.ts` (Block D4) — `onDeployFailure({deploymentId, reason, logs?})` and pure helper `summariseCommitsForIncident(commits)`. Sonnet 4 structured JSON RCA → opens `issues` row, attaches `incident` label if present, sets `deployments.blockedReason`. Never throws; deterministic fallback body when no API key. Wired from `post-receive.ts triggerCrontechDeploy` + `deployments.tsx retry-incident`.
- `src/lib/ai-tests.ts` (Block D8) — pure helpers `detectLanguage`, `detectTestFramework`, `buildTestsPrompt`, `suggestedTestPath`, `generateTestStub`, `contentTypeFor`. Returns `{code:"", framework:"fallback"}` on no API key. Never throws.
- `src/lib/branch-protection.ts` (Block D5) — `matchProtection(repoId, branch)` (exact wins; deterministic glob sort), `evaluateProtection(rule, ctx)` (pure — checks `requireAiApproval | requireGreenGates | requireHumanReview | requiredApprovals`), `countHumanApprovals(prId)` (LGTM/+1/approved heuristic). Never throws. Enforcement is in `src/routes/pulls.tsx` merge handler, after existing hard-gate filter.
- `src/lib/intelligence.ts` — repository intelligence engine (repo-level summarisation + signals).
- `src/lib/timetravel.ts` — time-travel code explorer (historical snapshot navigation).
- `src/lib/depimpact.ts` — dependency impact analyzer (cross-reference of who breaks if X bumps).
- `src/lib/rollback.ts` — one-click rollback helper for failed deploys.
- `src/lib/spec-to-pr.ts` + `src/lib/spec-ai.ts` + `src/lib/spec-context.ts` + `src/lib/spec-git.ts` — Spec-to-PR v2 pipeline (NL feature spec → Claude → branch via git plumbing → PR row). Graceful fallback when no `ANTHROPIC_API_KEY`. Issue-driven entry: the issue detail page renders a "Build with AI" button for owners on open issues, linking to `/spec?fromIssue=N`; spec is pre-filled with `Implement: <title>\n\n<body>\n\nCloses #N` so J7 close-keywords auto-close the issue on merge. Pure helper `buildSpecFromIssue` exported from `src/routes/specs.tsx`.
- `src/lib/autorepair.ts``autoRepair(...)` invoked from `src/hooks/post-receive.ts` and exercised by `src/__tests__/intelligence.test.ts`. **Distinct from `src/lib/auto-repair.ts`** (gate-time `repairSecrets`/`repairSecurityIssues` used by `src/lib/gate.ts`). Both are load-bearing — do not dedupe.
- `src/lib/pr-triage.ts``triggerPrTriage(input)` post-PR-create hook. Builds a tiny diff summary (numstat only), loads available labels + candidate reviewers (owner + recent PR authors, capped at 12), calls `triagePullRequest()` from `ai-generators.ts`, then inserts a "## AI Triage" comment. Idempotent via `PR_TRIAGE_MARKER` (HTML comment in body). Pure helper `renderTriageComment(triage)` exported. Suggestions only — never applies.
- `src/lib/issue-triage.ts``triggerIssueTriage(input)` post-issue-create hook. Mirrors the PR-triage pattern: idempotency via `ISSUE_TRIAGE_MARKER`, loads existing labels + recent issues (last 30) for context, calls `triageIssue()` from `ai-generators.ts`, inserts "## AI Triage" issue comment with summary / priority / labels / possible-duplicate callout. Pure helper `renderIssueTriageComment(triage)` exported. Suggestions only.
- `src/lib/ai-review.ts``triggerAiReview(...)` PR-create hook now actually runs Claude review. Computes `git diff base...head`, caps at 100KB, calls `reviewDiff()`, posts a summary comment + N inline file/line comments. Idempotent via `AI_REVIEW_MARKER`; degrades to a single "AI review unavailable" comment on Anthropic API failure. Never throws.
- `src/lib/auto-merge.ts` (Block K2) — pure decision helper `decideAutoMerge(facts)` plus the DB orchestrator `evaluateAutoMerge(ctx, opts?)`. Default-deny: requires a matching `branch_protection` rule with `enable_auto_merge=true` AND every gate the manual merge path enforces (reuses `matchProtection` / `evaluateProtection` / `listRequiredChecks` / `passingCheckNames` from §4.4's branch-protection module). When `requireAiApproval=true`, demands an AI-review comment carrying `AI_REVIEW_MARKER` that survives `aiCommentLooksApproved` (rejects "AI review unavailable" / "severity: blocking" / "flagged N item(s)"). Optional size cap (`maxChangedFiles` / `maxChangedLines`). Side-effect-free; K3's autopilot is the only intended caller. Also exports `recordAutoMergeAttempt(repoId, prId, decision)` for the audit trail.
- `src/lib/pr-merge.ts` (Block K3) — shared `performMerge(prId)` factored from `src/routes/pulls.tsx`. Pure `{ok, error}` return; never throws. Handles git-update-ref / `mergeWithAutoResolve` switch + PR state flip + J7 close-keyword auto-close. The HTTP route still has its own inline merge mechanics — refactoring it to call `performMerge()` is tracked as a follow-up. Both code paths are kept in lock-step until that refactor.
- `src/lib/ai-build-tasks.ts` (Block K3) — `runAiBuildTaskOnce(deps)` orchestrator + 5 collaborator interfaces (`findIssues`, `listOpenPrs`, `postIssueComment`, `buildSpec`, `dispatchSpec`). Finds open issues labelled `ai:build` (case-insensitive), skips those already linked to a PR via J7 close-keywords or already dispatched (marker `<!-- gluecron:ai-build:v1 -->`), composes a spec via `buildSpecFromIssue` (from `src/routes/specs.tsx`), dispatches to `src/lib/spec-to-pr.ts`. Posts the marker on the issue so re-runs never re-dispatch. Cap 20 issues per tick. Fire-and-forget; spec-to-pr failures caught + logged.
- `src/lib/sleep-mode.ts` (Block L1) — `composeSleepModeReport(userId, opts?)` + `renderSleepModeDigest(report, opts)` + `sendSleepModeDigestForUser(userId)`. Reads K3 audit-trail rows in the last N hours and produces the "while-you-slept" daily digest. Re-uses `email-digest.ts __internal.escapeHtml` + the shared `sendEmail()` provider. Never throws — DB error returns a zero-activity report. Used by the `sleep-mode-digest` autopilot task and the `/settings/sleep-mode/preview` endpoint.
- `src/lib/ai-hours-saved.ts` (Block L9) — pure `computeHoursSaved(breakdown)` (the canonical formula — L1 and L4 both import it) + `computeAiSavingsForUser(userId, opts?)` + `computeLifetimeAiSavingsForUser(userId)`. Formula constants documented inline. Counts AI-driven events (auto_merge.merged, ai_build.dispatched, pr_comments.is_ai_review, gate_runs.status='repaired') over a window; conservative weighted sum; rounds to 1 decimal. Never throws — DB error returns all-zero report.
- `src/lib/public-stats.ts` (Block L4) — `computePublicStats(opts?)` orchestrator + 9 default DB-backed counters. EVERY counter JOINs on `repositories.is_private=false` — private repos must never leak into landing-page social proof. 5-minute LRU cache via `src/lib/cache.ts`. `emptyPublicStats()` zero-fallback. Re-uses `computeHoursSaved` from L9 for the `weeklyHoursSaved` field. Never throws.

### 4.5 Platform (locked)
- `src/lib/notify.ts` — notification creation + audit log (swallow-failures pattern). Also fans out email to opted-in recipients for `mention|review_requested|assigned|gate_failed`. Exports `__internal` for tests.
- `src/lib/email.ts` — provider-pluggable email sender (`log`|`resend`). `sendEmail()` never throws. `absoluteUrl()` joins paths against `APP_BASE_URL`.
- `src/lib/templates.ts``loadIssueTemplate` / `loadPrTemplate`. Checks standard paths (`.github/`, `.gluecron/`, root, `docs/`) on the default branch, strips YAML frontmatter, 16KB cap, returns null on any failure.
- `src/lib/unread.ts` — unread count helper (never throws)
- `src/lib/repo-bootstrap.ts` — green defaults on repo creation
- `src/lib/gate.ts` — gate orchestration + persistence
- `src/lib/cache.ts` — LRU cache, git-cache invalidation
- `src/lib/reactions.ts``summariseReactions`, `toggleReaction`, `ALLOWED_EMOJIS`, `EMOJI_GLYPH`, `isAllowedEmoji`, `isAllowedTarget`
- `src/lib/observability.ts` — minimal dependency-free observability layer. Wired into `app.onError`. Sinks: `ERROR_WEBHOOK_URL` and/or `SENTRY_DSN`. Never throws.
- `src/lib/sse.ts` — in-process topic-based pub/sub broadcaster for Server-Sent Events. Backs the live UI updates layer.
- `src/lib/sse-client.ts` — emits a `<script>`-droppable JS snippet for SSR'd views to subscribe to a topic. Plain JS only — no client framework.
- `src/lib/autopilot.ts` — 5-minute self-sufficiency ticker. Default tasks (in order): `mirror-sync`, `merge-queue`, `weekly-digest`, `advisory-rescan`, `wait-timer-release`, `scheduled-workflows`, `auto-merge-sweep` (Block K3), `ai-build-from-issues` (Block K3), `sleep-mode-digest` (Block L1). `AUTOPILOT_DISABLED=1` opt-out. Observability via `getLastTick()` + `getTickCount()`. K3 tasks: `auto-merge-sweep` calls `evaluateAutoMerge` (§4.4) on every open non-draft PR updated in the last 24h (cap 50), performs the merge via `pr-merge.ts performMerge` on `merge:true` decisions, and emits `auto_merge.evaluated`/`auto_merge.merged`/`auto_merge.merge_failed` audit rows plus a stable `<!-- gluecron:auto-merge:v1 -->` PR comment. `ai-build-from-issues` runs the orchestrator in `ai-build-tasks.ts`. L1 task: `sleep-mode-digest` finds users with `sleep_mode_enabled=true` whose preferred UTC hour matches the current hour AND who haven't received a digest in 23h, sends each one a `sleep-mode.ts`-composed daily report. Cap 100 users per tick.
- `src/lib/demo-activity.ts` (Block L3) — pure helpers (`listQueuedAiBuildIssues`, `listRecentAutoMerges`, `listRecentAiReviews`, `countAiReviewsSince`, `listDemoActivityFeed`). All scoped to `demo`-owned repos. Never throw — DB error returns `[]`. 30-second LRU cache per query.
- `src/lib/demo-activity-seed.ts` (Block L3) — `ensureDemoActivity()` additive sibling to the locked `demo-seed.ts`. Idempotent. Adds an `ai:build`-labelled issue per demo repo + an open and a merged PR on `todo-api` + an `AI_REVIEW_MARKER`-bearing pr_comment + an `auto_merge.merged` audit row, so the live `/demo` tiles render with content on a fresh DB. Boot path wired in `src/index.ts` (only the lib file is locked; index.ts is the legitimate caller surface).
- `src/lib/demo-seed.ts` — idempotent `ensureDemoContent()` (creates `demo` user + `hello-python` / `todo-api` / `design-docs`). `DEMO_SEED_ON_BOOT=1` boot flag in `src/index.ts`.
- `src/lib/import-helper.ts` — small helpers for the GitHub import flow (`src/routes/import.tsx`).
- `src/lib/import-verify.ts` — post-migration smoke verifier (object count, branches, default-branch HEAD). Re-runnable from `src/routes/migrations.tsx`.
- `src/lib/namespace.ts` — namespace resolution for Block B2 (user vs org slug → owner record).
- `src/lib/action-registry.ts` — built-in action registry for workflow engine v2 (Sprint 1). Backs `src/lib/actions/{cache,checkout,download-artifact,gatetest,upload-artifact}-action.ts`.
- `src/lib/workflow-conditionals.ts``if:` expression evaluator for workflow steps.
- `src/lib/workflow-matrix.ts` — matrix expansion for workflow jobs.
- `src/lib/workflow-artifacts.ts` — artifact persistence helpers backing `workflow_run_cache`-style storage.
- `src/lib/workflow-secrets.ts` + `src/lib/workflow-secrets-crypto.ts` — AES-256-GCM secret storage. Crypto lib stays separate from DB lib so the DB layer holds opaque bytes only. `substituteSecrets(template, secrets)` is the pure regex-replace helper consumed by the v1 runner; `loadSecretsContext(repoId)` is the per-run loader (fail-soft on master-key absence / decrypt failure / DB error).
- `src/lib/cron.ts` — pure 5-field UNIX cron parser + matcher. Supports literals, ranges, steps (`a-b/n`, `*/n`), comma-lists, full POSIX OR semantics for dom + dow. Rejects @aliases and L/W/#/? with clear errors. Exports `parseCron`, `cronMatches`, `cronFiredBetween` (caps lookback at 1 day). Zero side effects.
- `src/lib/scheduled-workflows.ts``runScheduledWorkflowsTick(now)` scans non-disabled workflows whose parsed JSON includes `schedules`, computes `since` from the latest schedule-triggered run (fallback now-6min), enqueues at most one schedule run per workflow per tick. `MAX_RUNS_PER_TICK=50` safety cap. Pure helpers `schedulesFromParsedJson` + `firstCronToFire` exported. Wired into `src/lib/autopilot.ts` as the `scheduled-workflows` task.
- `src/lib/push-policy.ts` — pre-receive enforcement at the HTTP layer. Exports `evaluatePushPolicy({repositoryId, refs, pusherUserId})` returning `{allowed, violations}`; combines `matchProtectedTag` + `canBypassProtectedTag` (E7) and `evaluatePush` (J6) over the ref list. Only "active" enforcement blocks; "evaluate" enforcement remains dry-run. `formatPolicyError(violations)` builds the 403 body. Fail-open on any DB hiccup so a Postgres blip cannot wedge legitimate pushes. `ZERO_SHA` exported.
- `src/lib/git-push-auth.ts` — Authorization-header → User resolver for `git-receive-pack`. Accepts Basic (user:secret) and Bearer schemes; secrets matching `glc_` (PAT), `glct_` (OAuth), or `ghi_` (Block H2 app-bot installation token) prefixes are looked up. Decoders `decodeBasicAuth` / `decodeBearerAuth` exported. Returns `null` on any failure → caller treats as anonymous. App-bot path (2026-04-30): joins `app_install_tokens` + `app_installations` + `app_bots` and resolves to the synthetic `users` row that `marketplace.ts createApp` creates alongside the bot.
- `src/lib/mcp.ts` (move #9 from STRATEGY) — Model Context Protocol JSON-RPC 2.0 router. `routeMcpRequest({jsonrpc, id?, method, params?}, {ctx, tools})`. Supported methods: `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`. Standard JSON-RPC error codes exported (`ERR_PARSE`, `ERR_INVALID_REQUEST`, `ERR_METHOD_NOT_FOUND`, `ERR_INVALID_PARAMS`, `ERR_INTERNAL`). `McpError` class for typed throws from tool handlers. Notifications return null. Never throws.
- `src/lib/mcp-tools.ts` (move #9) — read-only v1 tool surface: `gluecron_repo_search`, `gluecron_repo_read_file`, `gluecron_repo_list_issues`, `gluecron_repo_explain_codebase`. Public-only visibility check; private repos return `-32601 not found`. `argString` / `argNumber` helpers throw `McpError(-32602)` on missing or wrong-typed args. Default registry exposed via `defaultTools()`.

### 4.6 Routes (locked endpoints — behaviour must be preserved)
- `src/routes/git.ts` — Smart HTTP (clone/push)
- `src/routes/api.ts` — REST (`POST /api/repos`, `GET /api/users/:u/repos`, `GET /api/repos/:o/:n`, `POST /api/setup`)
- `src/routes/hooks.ts``POST /api/hooks/gatetest` (bearer/HMAC), `GET /api/hooks/ping`, `POST /api/v1/gate-runs` (PAT backup), `GET /api/v1/gate-runs`. See `GATETEST_HOOK.md`.
- `src/routes/theme.ts``GET /theme/toggle`, `GET /theme/set?mode=`. Writes `theme` cookie (`dark`|`light`, 1-year). Layout reads via pre-paint inline script.
- `src/routes/audit.tsx``GET /settings/audit` (personal) + `GET /:owner/:repo/settings/audit` (owner-only).
- `src/routes/saved-replies.tsx``GET/POST /settings/replies`, `POST /settings/replies/:id`, `POST /settings/replies/:id/delete`, `GET /api/user/replies`. Unique constraint `saved_replies_user_shortcut`.
- `src/routes/deployments.tsx``GET /:owner/:repo/deployments` (grouped by env, success-rate rollup), `GET /:owner/:repo/deployments/:id` (detail).
- `src/routes/reactions.ts``POST /api/reactions/:targetType/:targetId/:emoji/toggle` (authed, form- or fetch-compatible), `GET /api/reactions/:targetType/:targetId`. Targets: `issue|pr|issue_comment|pr_comment`. Emojis: 8 canonical.
- `src/routes/auth.tsx` — register / login / logout
- `src/routes/web.tsx` — home / new / browse / blob / commits / raw / blame / star / search / profile
- `src/routes/issues.tsx` — issue CRUD + comments + labels + lock
- `src/routes/pulls.tsx` — PR CRUD + review + merge + close
- `src/routes/editor.tsx` — web file editor
- `src/routes/compare.tsx` — base...head diff
- `src/routes/settings.tsx` — profile + password + email notification preferences (`POST /settings/notifications`)
- `src/routes/repo-settings.tsx` — repo settings + delete
- `src/routes/webhooks.tsx` — webhook CRUD + test + `fireWebhooks`
- `src/routes/fork.ts` — fork
- `src/routes/explore.tsx` — discover
- `src/routes/tokens.tsx` — personal access tokens
- `src/routes/contributors.tsx` — contributor list
- `src/routes/notifications.tsx` — inbox + unread API
- `src/routes/dashboard.tsx` — authed home (`renderDashboard` exported)
- `src/routes/ask.tsx` — global + repo AI chat + explain
- `src/routes/releases.tsx` — tags + AI changelog
- `src/routes/gates.tsx` — history + settings + branch protection UI
- `src/routes/insights.tsx` — insights + milestones
- `src/routes/search.tsx` — global search + `/shortcuts`
- `src/routes/health.ts``/healthz` `/readyz` `/metrics`
- `src/routes/orgs.tsx``/orgs` list, `/orgs/new` create, `/orgs/:slug` profile, `/orgs/:slug/people` + add/role/remove, `/orgs/:slug/teams` + create, `/orgs/:slug/teams/:teamSlug` + member add/remove. All require auth. Role guards via `orgRoleAtLeast`; last-owner cannot be demoted or removed; every write path `audit()`'d.
- `src/lib/orgs.ts` (Block B1) — `isValidSlug` (rejects reserved + too-short/long + consecutive/leading/trailing hyphens), `normalizeSlug`, `orgRoleAtLeast` (owner>admin>member), `isValidOrgRole`, `isValidTeamRole`, `loadOrgForUser`, `listOrgsForUser`, `listOrgMembers`, `listTeamsForOrg`, `listTeamMembers`, `__test` export for unit tests.
- `src/routes/settings-2fa.tsx` (Block B4) — TOTP enroll / verify / disable + recovery codes UI. All require auth.
- `src/routes/passkeys.tsx` (Block B5) — WebAuthn passkey registration / assertion / management. All require auth.
- `src/routes/oauth.tsx` (Block B6) — OAuth 2.0 authorize + token + userinfo endpoints.
- `src/routes/developer-apps.tsx` (Block B6) — developer-facing OAuth app CRUD (`/settings/developer/apps`), client secret rotation, audit-logged.
- `src/routes/workflows.tsx` (Block C1) — Actions UI. `GET /:owner/:repo/actions`, `GET /:owner/:repo/actions/runs/:runId`, `POST /:owner/:repo/actions/:workflowId/run` (auth+owner), `POST /:owner/:repo/actions/runs/:runId/cancel` (auth+owner). Manual runs are `event=manual`, ref=default branch.
- `src/routes/packages-api.ts` (Block C2) — npm protocol: `GET/PUT/DELETE /npm/*` (packument, tarball, publish, yank); JSON helpers at `/api/packages/:owner/:repo/...`. PAT (`glc_`) bearer auth.
- `src/routes/packages.tsx` (Block C2) — UI: `/:owner/:repo/packages` list + `/:owner/:repo/packages/:pkgName` detail.
- `src/routes/pages.tsx` (Block C3) — `GET /:owner/:repo/pages/*` serves static files from latest gh-pages commit (binary via `getRawBlob`, text via `getBlob`). `GET/POST /:owner/:repo/settings/pages` settings + redeploy.
- `src/routes/environments.tsx` (Block C4) — settings CRUD at `/:owner/:repo/settings/environments`; approval endpoints at `/:owner/:repo/deployments/:id/{approve,reject}`.
- `src/routes/ai-explain.tsx` (Block D6) — `GET /:owner/:repo/explain` (softAuth), `POST /:owner/:repo/explain/regenerate` (requireAuth, owner-only).
- `src/routes/ai-changelog.tsx` (Block D7) — `GET /:owner/:repo/ai/changelog` (softAuth). Form + rendered output; `?format=markdown` returns `text/markdown`.
- `src/routes/copilot.ts` (Block D9) — `POST /api/copilot/completions` (requireAuth, 60/min rate limit), `GET /api/copilot/ping` (public).
- `src/routes/dep-updater.tsx` (Block D2) — `GET /:owner/:repo/settings/dep-updater` + `POST /:owner/:repo/settings/dep-updater/run` (requireAuth, owner-only).
- `src/routes/semantic-search.tsx` (Block D1) — `GET /:owner/:repo/search/semantic?q=` (softAuth) + `POST /:owner/:repo/search/semantic/reindex` (requireAuth, owner-only).
- `src/routes/ai-tests.tsx` (Block D8) — `GET /:owner/:repo/ai/tests` (softAuth form + picker), `GET /:owner/:repo/ai/tests?format=raw` (raw text w/ MIME), `POST /:owner/:repo/ai/tests/generate` (requireAuth, renders highlighted source + AI-generated failing test with copy button).
- `src/routes/discussions.tsx` (Block E2) — full discussion CRUD + categories + q-and-a answers + lock/pin. Exports `isValidCategory(c)` helper. Owner-only lock/pin; owner-or-author can close/toggle.
- `src/routes/gists.tsx` (Block E4) — `GET /gists` discover, `/gists/new|:slug|:slug/edit|:slug/delete|:slug/star|:slug/revisions|:slug/revisions/:rev` + `/:username/gists`. Exports `generateSlug()` (8-hex) and `snapshotOf(files)` JSON serializer. Retries on slug collision up to 5x.
- `src/routes/projects.tsx` (Block E1) — kanban board CRUD. Auto-seeds three default columns on project create. `/:owner/:repo/projects/:number/items/:itemId/move` recomputes position via `max+1` of target column.
- `src/routes/wikis.tsx` (Block E3) — DB-backed wiki with revision history + revert. Exports `slugifyTitle(title)` (lowercase alphanumerics joined by single dashes, trimmed). Every edit appends a `wiki_revisions` row; revert creates a new revision.
- `src/routes/merge-queue.tsx` (Block E5) — `GET /:owner/:repo/queue` list, `POST /:owner/:repo/pulls/:n/enqueue` (requireAuth), `POST /:owner/:repo/queue/:id/dequeue` (owner-or-enqueuer), `POST /:owner/:repo/queue/process-next?base=X` (owner-only, re-runs gates against base then updates base ref). PR page has an extra "Add to merge queue" button.
- `src/lib/merge-queue.ts` (Block E5) — `enqueuePr`, `dequeueEntry`, `peekHead`, `markHeadRunning`, `completeEntry`, `isQueued`, `queueDepth`, `listQueue`, `listQueueWithPrs`. No side effects beyond the `merge_queue_entries` table; callers own gate execution + git updates.
- `src/routes/required-checks.tsx` (Block E6) — `/:owner/:repo/gates/protection/:id/checks` CRUD (owner-only, requireAuth). "Required checks" link added on gates settings UI next to each branch protection rule.
- `src/lib/branch-protection.ts` extends for E6 — `listRequiredChecks(branchProtectionId)`, `passingCheckNames(repositoryId, commitSha)` (scans `gate_runs` + `workflow_runs`), and `evaluateProtection(rule, ctx, requiredChecks[])` now takes a third param + reports `missingChecks`.
- `src/routes/protected-tags.tsx` (Block E7) — `/:owner/:repo/settings/protected-tags` CRUD (owner-only, requireAuth).
- `src/lib/protected-tags.ts` (Block E7) — `matchProtectedTag`, `isProtectedTag`, `canBypassProtectedTag`, `listProtectedTags`, `addProtectedTag`, `removeProtectedTag`, `userIdFromUsername`. Matching uses `matchGlob` from environments.ts with `refs/tags/` prefix stripped. Post-receive hook writes audit log entries (`protected_tags.{create|update|delete}_violation_candidate`) on matched pushes.
- `src/lib/traffic.ts` (Block F1) — `track`, `trackView`, `trackClone`, `trackByName(owner, repo, kind, meta)`, `summarise(repoId, windowDays=14)`, pure `bucketDaily(events)`. SHA-256-truncated IP hashing (16 hex) for unique-visitor approximation. All callers use `.catch(() => {})` fire-and-forget.
- `src/routes/traffic.tsx` (Block F1) — `GET /:owner/:repo/traffic` (owner-only) with 7/14/30/90d windows, ascii-bar daily chart, top referers, unique visitors.
- `src/routes/org-insights.tsx` (Block F2) — exports `computeOrgInsights(orgId)` returning `OrgInsightsSummary` (repoCount, gateRunsTotal, greenRate, openIssues, openPrs, mergedPrs30d, perRepo[]). `GET /orgs/:slug/insights` requires org membership. No new tables.
- `src/lib/admin.ts` (Block F3) — `isSiteAdmin(userId)` with bootstrap rule (empty `site_admins` → oldest user wins), `listSiteAdmins`, `grantSiteAdmin`, `revokeSiteAdmin`, `getFlag`, `setFlag`, `listFlags`, `KNOWN_FLAGS = { registration_locked, site_banner_text, site_banner_level, read_only_mode }`. All helpers swallow DB errors.
- `src/routes/admin.tsx` (Block F3) — `GET /admin` dashboard (user/repo/admin counts + recent signups), `/admin/users` + toggle grant/revoke, `/admin/repos` + nuclear delete, `/admin/flags` form. All mutations audit-logged via `audit()`. Gated through a `gate(c)` helper that returns `{user} | Response`.
- `src/lib/billing.ts` (Block F4) — plan + quota helpers. `FALLBACK_PLANS` (free/pro/team/enterprise) mirror the seed rows. `getUserQuota(userId)` auto-initialises free row. `bumpUsage`, `checkQuota` (fail-open), `wouldExceedRepoLimit`, `resetIfCycleExpired`, `formatPrice`. Never throws into request path.
- `src/routes/billing.tsx` (Block F4) — `GET /settings/billing` (personal view with usage bars + plan cards), `GET /admin/billing` (site-admin user/plan table), `POST /admin/billing/:userId/plan` (override plan, audit-logged).
- `src/routes/pwa.ts` (Block G1) — `/manifest.webmanifest`, `/sw.js`, `/icon.svg`. Exports `MANIFEST`, `SERVICE_WORKER_SRC`, `PWA_REGISTER_SNIPPET` for testing. SW deliberately skips `.git/`, `/api/`, `/login*`, `/register`, `/logout`.
- `src/lib/graphql.ts` (Block G2) — hand-rolled recursive-descent parser (`parseQuery`) + executor (`execute`) over a fixed schema. Zero dependencies. Root fields: viewer, user, repository, search, rateLimit. No mutations.
- `src/routes/graphql.ts` (Block G2) — `POST /api/graphql` JSON endpoint + `GET /api/graphql` GraphiQL-lite explorer (Cmd+Enter to run).
- `cli/gluecron.ts` (Block G3) — single-file Bun CLI. Exports `dispatch(argv, out)` for programmatic use, `HELP` constant, `loadConfig`/`saveConfig`. Config at `~/.gluecron/config.json` (0600). Compile: `bun build cli/gluecron.ts --compile --outfile gluecron`.
- `vscode-extension/` (Block G4) — VS Code extension with `package.json` declaring four commands (explainFile, openOnWeb, searchSemantic, generateTests) + `gluecron.host` / `gluecron.token` settings. Detects Gluecron remotes via `git config remote.origin.url`.
- `src/lib/marketplace.ts` (Block H1+H2) — marketplace + app identity surface. `KNOWN_PERMISSIONS` (10 scopes), `KNOWN_EVENTS` (8 kinds). Pure helpers: `slugify` (40-char cap), `botUsername` (`<slug>[bot]`), `normalisePermissions` (drops unknown, de-dupes), `parsePermissions` (JSON), `hasPermission` (write→read implication), `permissionsSubset`, `generateBearerToken` (`ghi_` prefix + 24-byte hex), `hashBearer` (sha256). DB helpers: `listPublicApps(query)`, `getAppBySlug`, `createApp` (retries slug collisions, creates matching bot row), `installApp` (idempotent soft-update), `uninstallApp` (revokes all tokens), `issueInstallToken` (1h TTL default), `verifyInstallToken` (checks revoked/expired/uninstalled/suspended), `listInstallationsForApp`, `listInstallationsForTarget`, `listEventsForApp`, `countInstalls`. Never throws into request path.
- `src/routes/marketplace.tsx` (Block H1+H2) — public marketplace + developer UX. `GET /marketplace` (directory + search), `GET /marketplace/:slug` (detail + install form), `POST /marketplace/:slug/install` (v1 user-target only), `POST /marketplace/installations/:id/uninstall` (installer-only), `GET /settings/apps` (personal list), `GET+POST /developer/apps-new` (register), `GET /developer/apps/:slug/manage` (event log + install count, owner-only), `POST /developer/apps/:slug/tokens/new` (show-once `ghi_` token). All mutations audit-logged.
- `src/routes/code-scanning.tsx` (Block I5) — `GET /:owner/:repo/security` (softAuth, private-repo visibility enforced). Aggregates last-100 scan-related `gate_runs`, builds `latestByName` map, renders summary cards + scanner status list + recent runs.
- `src/routes/sponsors.tsx` (Block I6) — public `/sponsors/:username` + maintainer `/settings/sponsors` (requireAuth). Tier CRUD (`POST /settings/sponsors/tiers/new`, soft-retire via `is_active=false` on delete). Exports `sponsorshipTotalForUser(userId)` helper and `__internal.formatCents` for tests.
- `src/lib/email-digest.ts` (Block I7) — `composeDigest(userId, since?)` (never throws, null on failure), `sendDigestForUser(userId)` (opt-out check + updates `last_digest_sent_at` on success), `sendDigestsToAll()` (iterates opted-in users). Pulls notifications + owned-repo gate_runs (failed/repaired) + merged PRs over last 7d. Builds text + escaped HTML body. Exports `__internal = { textToHtml, escapeHtml, fmtRange }` for tests.
- `src/routes/admin.tsx` (extends Block F3 for I7) — adds `GET /admin/digests` (opted-in count + recently sent list), `POST /admin/digests/run` (calls `sendDigestsToAll`, audit-logged with counts), `POST /admin/digests/preview` (sends to one user by username, audit-logged). New "Email digests" tile on the /admin dashboard grid.
- `src/routes/settings.tsx` (extends for I7) — adds `notify_email_digest_weekly` checkbox to email prefs + handler wiring in `POST /settings/notifications`, and `GET /settings/digest/preview` (renders `composeDigest` output inline via `raw(body.html)` with Hono's `hono/html`).
- `src/lib/symbols.ts` (Block I8) — regex-based top-level symbol extractor. Pure helpers: `detectLanguage(path)` (10 extensions mapped to 8 languages), `extractSymbols(content, lang)` (per-language rule list, 1-based line numbers, 240-char signature cap, skips lines >500 chars). `indexRepositorySymbols(repoId)` walks the default-branch tree, caps at 2000 files / 1MB each, replaces the prior set in batches of 500. `findDefinitions(repoId, name)` + `countSymbolsForRepo(repoId)`. `__internal` exposes `RULES` + `EXT_LANG` for tests.
- `src/routes/symbols.tsx` (Block I8) — `/:owner/:repo/symbols` overview (total + per-kind counts + A–Z list with blob deep-links), `/:owner/:repo/symbols/search?q=` prefix search (ilike `q%`), `/:owner/:repo/symbols/:name` detail (all definitions with signature preview + deep link). `POST /:owner/:repo/symbols/reindex` is requireAuth + owner-only.
- `src/lib/mirrors.ts` (Block I9) — upstream URL validator (accepts https/http/git schemes, rejects ssh/file/paths/shell metacharacters, 2048-char cap), `safeUrlForLog` (redacts embedded credentials), `upsertMirror` / `deleteMirror` / `getMirrorForRepo` / `listRecentRuns`, `runMirrorSync` (runs `git fetch --prune --tags --no-write-fetch-head` via `Bun.spawn` with 5-min timeout + `GIT_TERMINAL_PROMPT=0`; updates `last_synced_at` + `last_status` + `last_error`), `listDueMirrors` + `syncAllDue` for the admin trigger.
- `src/routes/mirrors.tsx` (Block I9) — owner-only config at `/:owner/:repo/settings/mirror` (GET form + recent-runs panel, POST save, POST delete, POST sync-now). Site-admin `POST /admin/mirrors/sync-all` iterates due mirrors. All mutations `audit()`-logged.
- `src/lib/rulesets.ts` (Block J6) — exports `RULE_TYPES` (6 types), pure `globToRegex`, defensive `parseParams`, pure evaluator `evaluatePush(rulesets, ctx) → {allowed, violations}`. CRUD: `listRulesetsForRepo`, `getRuleset`, `createRuleset`, `updateRulesetEnforcement`, `deleteRuleset`, `addRule`, `deleteRule`. `__internal = { evalRule, globToRegex, parseParams }` for tests. Every rule variant no-ops on malformed params or bad regex rather than throwing.
- `src/routes/rulesets.tsx` (Block J6) — owner-only UI at `/:owner/:repo/settings/rulesets` (list + create), `/:id` (detail + enforcement toggle + add rule), `/:id/delete`, `/:id/rules/:ruleId/delete`. All mutations `audit()`-logged via the existing `gate(c)` owner pattern.
- `src/lib/close-keywords.ts` (Block J7) — pure parser. Exports `extractClosingRefs(text)` + `extractClosingRefsMulti(sources[])`. Verbs: close/closes/closed/fix/fixes/fixed/resolve/resolves/resolved. Case-insensitive, word-boundary-respecting, ignores cross-repo `owner/repo#N` refs, rejects non-positive numbers, returns sorted de-duped list. Tolerates `:` / `-` / whitespace between verb and ref.
- `src/lib/commit-statuses.ts` (Block J8) — pure helpers: `isValidSha`, `isValidState`, `sanitiseContext`, `reduceCombined`. DB helpers: `setStatus` (delete-then-insert upsert on `(repo, sha, context)`, SHA lowercased, description/url clamped to 1000/2048 chars), `listStatuses` (newest first), `combinedStatus` (latest per context, reduces to worst state, returns counts + context pills). `STATUS_STATES` exported array. Never throws on clamping; null/empty description returns null.
- `src/routes/commit-statuses.ts` (Block J8) — `POST /api/v1/repos/:owner/:repo/statuses/:sha` (requireAuth — accepts session/OAuth/PAT; owner-only), `GET /api/v1/repos/:owner/:repo/commits/:sha/statuses` (softAuth, private-repo visibility enforced), `GET /api/v1/repos/:owner/:repo/commits/:sha/status` (combined rollup, same visibility rules).
- `src/routes/api-v2.ts` — Comprehensive REST API v2 (full CRUD across resources). Authoritative integration surface alongside the v1 endpoints in this section.
- `src/routes/mcp.ts` (move #9) — `GET /mcp` discovery (server info + tool count) + `POST /mcp` JSON-RPC 2.0 endpoint. Single requests + batched arrays both supported; notifications return 204. softAuth for the `userId` context; v1 tools are public-only so anonymous works. Mounted at root in `src/app.tsx` alongside graphql + live-events.
- `src/routes/api-docs.tsx` — interactive in-app API documentation page.
- `src/routes/collaborators.tsx` — repo collaborator add / list / remove. Owner-or-admin only. Audit-logged. (See §4.3.1.)
- `src/routes/team-collaborators.tsx` — bulk team-to-repo collaborator grant. (See §4.3.1.)
- `src/routes/invites.tsx` — invite acceptance flow. (See §4.3.1.)
- `src/routes/events.ts``POST /events/...` inbound deploy-event receiver for Crontech (Signal Bus P1). Idempotent via `processed_events`.
- `src/routes/live-events.ts``GET /live-events/:topic` SSE subscription endpoint backed by `src/lib/sse.ts`.
- `src/routes/health-probe.ts` — extended health + metrics probes for load-balancer + observability sinks.
- `src/routes/help.tsx` — public `/help` quickstart + API cheatsheet for owners migrating from GitHub.
- `src/routes/import.tsx` — single-repo GitHub → Gluecron import (PAT-based clone-and-rehost).
- `src/routes/import-bulk.tsx``/import/bulk` paste-a-token org-wide migration.
- `src/routes/migrations.tsx``/migrations` per-user import history dashboard with re-run-verifier button.
- `src/routes/onboarding.tsx` — guided first-five-minutes flow for new accounts.
- `src/routes/platform-status.ts` — machine-readable platform status JSON.
- `src/routes/seo.ts``/robots.txt` + `/sitemap.xml`.
- `src/routes/signing-keys.tsx` — Block J3 UI: `GET/POST /settings/signing-keys` + `POST /settings/signing-keys/:id/delete`. Audit-logged. (Library is `src/lib/signatures.ts`, see §4.6 for the underlying lib.)
- `src/routes/specs.tsx` — Spec-to-PR UI. Paste a plain-English feature spec, get back a draft PR. Calls `src/lib/spec-to-pr.ts`.
- `src/routes/status.tsx` — public `GET /status` HTML dashboard + `/status.svg` shields-style badge.
- `src/routes/workflow-artifacts.ts` — REST API for workflow run artifacts (workflow engine v2 / Sprint 1).
- `src/routes/workflow-secrets.tsx` — per-repo workflow secrets settings UI (list / create / delete encrypted secrets). Backed by `workflow_secrets` table from `drizzle/0037`.

### 4.7 Views (locked contracts)
- `src/views/layout.tsx``Layout` accepts `title`, `user`, `notificationCount`
- `src/views/components.tsx``RepoHeader`, `RepoNav` (active: `code|issues|pulls|commits|releases|actions|gates|insights|explain|changelog|semantic`), `RepoCard`, etc.
- `src/views/reactions.tsx``ReactionsBar` (no-JS compatible, form-per-emoji)
- Nav links: logo · search · theme-toggle · Explore · Ask · Notifications · New · Profile (or Sign in / Register)
- Keyboard chords: `/`, `Cmd+K`, `?`, `n`, `g d`, `g n`, `g e`, `g a`

### 4.7.1 Workflow runner extensions (locked)
- `src/lib/workflow-runner.ts` — v1 runner. Now loads workflow secrets once per run via `loadSecretsContext(run.repositoryId)` and passes them through to `executeJob``runStep`. `runStep` substitutes `${{ secrets.NAME }}` in `step.run` via `substituteSecrets` only when the template contains `${{` (cheap pre-check); otherwise the hot path is unchanged. The v2 executor stays gated off via `_v2NeededFor` returning false; secrets plumbing for v2 will reuse the same args.

### 4.8 Tests (locked)
- `src/__tests__/green-ecosystem.test.ts` — secret scanner, codeowners, AI fallback, health, rate-limit headers, `/shortcuts`, `/search`
- All other existing test files — do not delete without owner permission

### 4.9 Invariants (never break these)
- `isAiAvailable()` guard returns true fallback strings when no ANTHROPIC_API_KEY. AI features degrade gracefully.
- `getUnreadCount` never throws; returns 0 on any error.
- Rate-limit middleware adds `X-RateLimit-Limit` + `X-RateLimit-Remaining` to every response, including 500s.
- `c.header("X-Request-Id", ...)` set by request-context on every response.
- Secret scanner skips binary/lock paths (`shouldSkipPath`).
- `SECRET_PATTERNS` is an exported array. Its shape is `{ type, regex, severity }`.
- Theme routes live outside `/settings/*` (they must work for logged-out visitors). Cookie name: `theme`, values: `dark|light`.
- Draft PRs cannot be merged — `/pulls/:n/merge` returns a redirect with the draft error when `pr.isDraft=true`.
- Reactions API accepts only `ALLOWED_EMOJIS` and `ALLOWED_TARGETS`. Toggle is idempotent per (user, target, emoji).
- `sendEmail()` never throws — always resolves to `{ ok, provider, ... }`. Email failures never break notification delivery or the primary request path.
- Email fan-out in `notify()` is scoped to kinds in `EMAIL_ELIGIBLE` (mention / review_requested / assigned / gate_failed). Each eligible kind maps to exactly one user preference column.
- Issue + PR template loading must return `null` on any git-subprocess failure (templates are a convenience, not a requirement). Forms always render.

---

## 5. OPERATIONAL NOTES

> Day-to-day operator surface: [`/admin/ops`](https://gluecron.com/admin/ops) (auto-merge toggle, deploy, rollback) and [`/admin/deploys`](https://gluecron.com/admin/deploys) (live log stream). Prefer those over SSH wherever possible — see README.md and DEPLOY.md §6. The terminal commands below are for local dev.

### 5.1 Running locally
```bash
bun install
bun dev          # hot reload
bun test         # 1491 tests currently pass (0 failed, 2 skipped) as of 2026-05-13 after BLOCK K (3 sub-blocks) + BLOCK L (10 sub-blocks). +218 tests vs the 1273 pre-K baseline. Run `bun install` first.
bun run db:migrate
```

### 5.2 Environment
- `DATABASE_URL` — Neon Postgres
- `ANTHROPIC_API_KEY` — unlocks AI features
- `GIT_REPOS_PATH` — default `./repos`
- `PORT` — default 3000
- `EMAIL_PROVIDER``log` (default, stderr-only) or `resend`
- `EMAIL_FROM` — sender address for outbound mail
- `RESEND_API_KEY` — required when `EMAIL_PROVIDER=resend`
- `APP_BASE_URL` — canonical URL used to build absolute links in emails
- `VOYAGE_API_KEY` — optional; when set, D1 semantic search uses Voyage `voyage-code-3` embeddings. Otherwise falls back to a deterministic 512-dim hashing embedder.

### 5.3 Models
- `claude-sonnet-4-20250514` — code review, security, chat
- `claude-haiku-4-5-20251001` — commit messages, summaries, light tasks
- Swap via `MODEL_SONNET` / `MODEL_HAIKU` constants in `src/lib/ai-client.ts`

### 5.4 Deployment
- `railway.toml` / `fly.toml` present
- Crontech deploy on green push to default branch (can opt out via `autoDeployEnabled`)

---

## 6. SESSION WORKFLOW (WHAT THE NEXT AGENT DOES)

1. Read this file, `CLAUDE.md`, `README.md`, `git log -1 --stat`.
2. Check `git status` + current branch.
3. Pick the next unfinished block from §3 (lowest letter + number first, unless owner specifies).
4. Create a todo list that mirrors the sub-items of that block.
5. Build. Write tests. Run `bun test`.
6. Commit with `feat(<BLOCK-ID>): ...`.
7. Push.
8. Update this file:
   - Move the block's row in §2 to ✅ where applicable.
   - Add the block's files to §4 LOCKED BLOCKS.
   - Commit + push again.
9. Start the next block. **Do not stop to ask.**

If a block is too large for a single session, split it into a sub-plan at the top of the session, ship what you can, and document what's left at the end of this file under a `## 7. IN-FLIGHT` section.

---

## 7. IN-FLIGHT

**2026-05-28 — Platform redesign + Claude Code integration session (branch `claude/jolly-heisenberg-2sg1Q`):**

Changes shipped this session (all pushed, zero TS errors, GateTest green):

**UX / Design:**
- `src/views/layout.tsx` — widened all containers from 1240px → 1440px; replaced crowded top-nav with a clean user dropdown (Dashboard, Pulls, Issues, Activity, Import, Profile, Settings, Tokens, Theme, Sign out) + bell inbox icon with badge. Theme toggle moved into user dropdown for logged-in users.
- `src/routes/issues.tsx``?sort=newest|oldest|updated` param with sort control links; bulk close/reopen via `POST /:owner/:repo/issues/bulk` with floating action bar and checkboxes (in-flight).
- `src/routes/pulls.tsx``?sort=newest|oldest|updated` + `?author=` filter with input field.

**Claude Code integration:**
- `src/routes/claude-integration.ts``POST /api/claude/connect` (Bearer auth, auto-create repo), `GET /api/claude/connect` (connection info), `POST /api/claude/session` (fire-and-forget telemetry to activity_feed).
- `src/routes/connect.tsx` — `/connect/claude-guide` onboarding page: 4 steps (token → remote → MCP → push) + "Why Gluecron?" benefits grid.

**Infrastructure:**
- `src/middleware/no-cache.ts` — stamps `Cache-Control: no-store` on all text/html responses; assets unaffected.
- `src/routes/keyboard-ux.ts` — Ctrl+Enter form submit + copy buttons on code blocks in rendered markdown.
- `src/lib/mention-autocomplete.ts` — @mention dropdown on all comment textareas.
- `src/lib/markdown-preview.ts` — Write/Preview tabs on all comment textareas.
- `.husky/pre-push` — added `fakeFixDetector`, `prSize` to false-positive skip list.

**Bug fixes (all pre-existing):**
- `src/views/diff-view.tsx:560` — `method="post"` (was `"POST"`, TS2820).
- `src/lib/ssh-server.ts` — explicit types on ssh2 callback params; `@ts-expect-error` on untyped import.
- `src/__tests__/editor.test.ts` — `as unknown as Hono` double-cast for Hono type mismatch.
- `src/routes/workflow-secrets.tsx` — moved `wsecScript` const before route handlers (was forward-referenced past GET handler).

**Additional shipped this session (2026-05-28 continued):**
- ~~DORA metrics dashboard `src/routes/dora.tsx`~~ — shipped (`8d1483c`). `GET /:owner/:repo/insights/dora` with 6 parallel DB queries, DORA level badges, last-10-deployments table.
- ~~Bulk issue operations~~ — shipped (`8d1483c`). POST /:owner/:repo/issues/bulk endpoint + floating sticky action bar.
- `src/__tests__/layout-user-prop.test.ts` — updated nav-user assertion to match redesigned dropdown (`da16579`). Test suite: 2696/0/120.
- ~~`diff-view.tsx` TS2820~~ — confirmed fixed.
- ~~L1/K3 follow-ups~~ — confirmed already done.

**In-flight (agents running):**
- **M1 Push Watch page** — `src/routes/push-watch.tsx` at `GET /:owner/:repo/push/:sha`. Per-push live status: commit info, gate results, deploy status, push-to-live latency. JSON API at `/api/repos/:owner/:repo/push-status/:sha`.
- **M2 Org-level Secrets** — `drizzle/0075_org_secrets.sql` + `src/lib/org-secrets.ts` + `src/routes/org-secrets.tsx`. Org secrets auto-apply to all repos; settings UI at `/orgs/:slug/settings/secrets`.

**Repo health score** — already exists at `src/routes/health.tsx` (uses `computeHealthScore` from `src/lib/intelligence.ts`). No new work needed.

**2026-05-13 — BLOCK L (PR #62) follow-ups (all non-blocking; suite is 1491/0/2 green):**

1. L1 and L9 ship near-identical hours-saved formulae. L1's `sleep-mode.ts` has its own copy; L9's `ai-hours-saved.ts` exports the canonical `computeHoursSaved`. L4 already imports L9's; L1 should too. One-line cleanup.
2. L1's 23h cooldown uses `users.last_digest_sent_at`, which is shared with the existing weekly digest (I7). A user with both opted in could starve one feed of the other. Worth splitting columns once an interaction is reported.
3. L10 added 5 optional props (`description`, `ogTitle`, `ogDescription`, `ogType`, `twitterCard`, `fullTitle`) to `Layout`. Strictly additive (render byte-identical when omitted) so the §4.7 locked contract holds — but the §4.7 entry should be bumped to enumerate the new props on the next reconciliation pass.
4. L5 removed a stale pre-existing `/vs-github` handler from `src/routes/marketing.tsx` (lines 1164-1647 of the old file). Confirmed no test asserted on the removed block; sanity-check at next release if anything trips on the deleted CSS class names.
5. L4's secrets-fixed counter uses `gate_runs.status='repaired' AND gate_name ILIKE '%secret%'`; L9's counter uses the same convention. If either moves, both need migrating together.
6. L4 accepts both `success` and `succeeded` for `deployments.status` until the schema convention normalises.
7. L7's `scripts/install.sh` writes SKILL.md bodies via inline heredocs which can drift from the canonical `.claude/skills/` files. Worth a build step that materialises them at release time.
8. L7 bundles the skill files only into THIS repo + L2-installed users. Imported repos don't get them yet (skip flagged in the L7 spec — bundling-into-imports is a future block).
9. L9's `ai.commit_message.generated` audit action isn't currently emitted by any producer; counter returns 0 for that line item until `src/lib/ai-generators.ts generateCommitMessage` gets an `audit()` call.
10. L6's `/login/sso/unlink` deletes any sso_user_links row including the GitHub one. A dedicated `/settings/github/unlink` route would be clearer UX.

**2026-05-13 — BLOCK K (PR #62) follow-ups (all four are non-blocking; the suite is 1350/0 green):**

1. Refactor `src/routes/pulls.tsx` merge handler to call `src/lib/pr-merge.ts performMerge()`. K3 factored the helper but left the route's inline merge mechanics untouched because the route's `c.redirect(?error=…)` flow doesn't map cleanly to the helper's `{ok,error}` return — and cracking open the locked route risked altering user-facing redirect semantics. The two paths are currently lock-step duplicates. Single shared code path is the goal.
2. K1's `gluecron_merge_pr` MCP tool also duplicates merge logic. Once #1 lands, this tool should switch to `performMerge()` too. Both merge call sites must move together.
3. Add a synthetic `system`/`autopilot` user (or a nullable `authored_by_system` column on `pr_comments`/`issue_comments`) so K3's auto-merge / ai-build marker comments don't credit the PR/issue author as the comment author. Today the marker is posted with `authorId = pr.authorId` (or issue authorId) because no system user exists.
4. Surface the new K3 tasks (`auto-merge-sweep`, `ai-build-from-issues`) on `/admin/autopilot`. Today they run but the admin UI lists only the original six tasks.

**2026-04-29 reconciliation pass (branch `claude/platform-launch-assessment-8dWV8`):**

The Bible had drifted behind reality. This session reconciled §2 / §4 / §5 against the actual codebase. All edits below are strictly additive — no locked entry was deleted or renamed.

What was added:
- §2.4 — surfaced previously undocumented AI features now on disk: Spec-to-PR (real AI), repository intelligence engine, time-travel code explorer, dependency impact analyzer, one-click rollback. Clarified that `auto-repair.ts` (gate-time) and `autorepair.ts` (intelligence-time) are two distinct load-bearing modules — do not dedupe.
- §2.5 — surfaced repository collaborators (per-repo roles), single-repo GitHub import, bulk org import, migration history + verifier, onboarding flow, public `/help` quickstart.
- §2.6 — surfaced public status page (`/status` + `/status.svg`), platform-status JSON, error-tracking wiring (`src/lib/observability.ts`), comprehensive REST API v2 (`src/routes/api-v2.ts`), API docs page, SSE pub/sub (`src/lib/sse.ts` + `src/routes/live-events.ts`), inbound deploy event receiver (`src/routes/events.ts`), autopilot ticker, demo seed, SEO routes.
- §4.1 — added migrations 0034–0037 to LOCKED.
- §4.3.1 (new sub-section) — permissions + collaborators surface (`src/middleware/repo-access.ts`, `src/lib/invite-tokens.ts`, three new collaborator routes).
- §4.4 — added intelligence / time-travel / depimpact / rollback / spec-to-PR libs; clarified the `auto-repair` vs `autorepair` distinction; flagged `pr-triage.ts` as a placeholder distinct from the live D3 path.
- §4.5 — added observability, SSE, autopilot, demo-seed, import helpers, namespace, action-registry, and the workflow-engine-v2 family (conditionals / matrix / artifacts / secrets / secrets-crypto).
- §4.6 — added 21 new routes: api-v2, api-docs, collaborators, team-collaborators, invites, events, live-events, health-probe, help, import, import-bulk, migrations, onboarding, platform-status, seo, signing-keys, specs, status, workflow-artifacts, workflow-secrets. Plus a note pointing signing-keys.tsx at the existing `src/lib/signatures.ts`.
- §5.1 — bumped the test-count claim from `601` to the actual current number `1033 pass / 8 skip / 0 fail` (verified with deps installed on this branch).

What was verified, not changed:
- `bun install && bun test` → 1033 pass, 0 fail. Suite is genuinely green.
- `auto-repair.ts` (472 lines, used by `src/lib/gate.ts`) and `autorepair.ts` (328 lines, used by `src/hooks/post-receive.ts`) are not duplicates — they expose disjoint APIs (`repairSecrets` / `repairSecurityIssues` vs `autoRepair`) and are both in active use.
- Workflow engine v2 is strictly additive on top of the locked v1 (`src/lib/workflow-runner.ts`); no v1 file was replaced.

**Polish batch shipped earlier:**
- `src/lib/autopilot.ts` + tests — 5-min ticker (mirror sync, merge-queue peek, weekly digests, advisory rescans). `AUTOPILOT_DISABLED=1` opt-out. Observability via `getLastTick()` / `getTickCount()`. Admin page at `/admin/autopilot` with "Run tick now" button.
- `src/views/landing.tsx` — marketing landing for logged-out `/`. Accepts optional `stats` prop; `src/routes/web.tsx` queries public repo + user counts.
- `src/lib/demo-seed.ts` + tests — idempotent `ensureDemoContent()` seeds `demo` user + 3 public sample repos + seeded issues/PR. Boot flag `DEMO_SEED_ON_BOOT=1`. Site-admin reseed button on `/admin` (`POST /admin/demo/reseed`). Public `/demo` convenience redirect to `/demo/hello-python`.
- Doc sync: `README.md`, `DEPLOY.md`, `LAUNCH_TODAY.md` aligned with current reality.

**BLK-016 Crontech-Gluecron deploy wire — Gluecron sender rewritten (pending live verification):**
- `src/hooks/post-receive.ts triggerCrontechDeploy` now matches the Crontech receiver at `apps/api/src/webhooks/gluecron-push.ts`:
  - Endpoint default `POST https://crontech.ai/api/webhooks/gluecron-push` (was `/api/hooks/gluecron/push`).
  - Auth model: HMAC-SHA256 of the JSON body in `X-Gluecron-Signature: sha256=<hex>`, signed with `GLUECRON_WEBHOOK_SECRET` (no longer Bearer).
  - Payload shape is GitHub-style: `{event, repository:{full_name}, ref, after, before, pusher:{name,email}, commits:[{id,message,timestamp}]}` plus ancillary `sent_at`/`source`. Receiver dedupes on `after`.
  - At-least-once delivery: 6 attempts (initial + 5 backoffs at 1s/4s/16s/64s/256s). Stops on first 2xx; bails immediately on unrecoverable 4xx (except 408/429); retries on 5xx + network errors.
  - `commits[]` + pusher derived via `commitsBetween(owner, repo, before, after)` from the bare repo (capped at 50, like GitHub). Empty when the git layer can't read the repo.
- `onPostReceive` no longer hardcodes `refs/heads/main` — gated by `${owner}/${repo} === config.crontechRepo` (env `CRONTECH_REPO`, default `ccantynz-alt/crontech`) and the repo's actual default branch via `getDefaultBranch`. Handles `Main` (capital M) without a code change.
- Config additions: `config.crontechRepo` (env `CRONTECH_REPO`); `config.crontechDeployUrl` default flipped; both reflected in `.env.example`.
- Tests: `src/__tests__/crontech-deploy.test.ts` rewritten — 19 tests covering endpoint, HMAC signature, payload shape, branch-case carry-through (`Main`), retry-on-5xx, give-up-after-N, no-retry-on-4xx, retry-on-408/429, retry-on-network-error, default backoff schedule. `triggerCrontechDeploy` now takes a single `args` object and an optional `opts: {fetchImpl, sleep, retryDelaysMs, now}` for injectability.
- Removed dead `fanoutWebhooks` helper (defined-but-uncalled, per audit).
- **Live verification (the BLK-016 done-criterion) is out of scope for this session** — it requires SSH to the Vultr box, a real test push, and confirming the deploy-agent log + GitHub Actions silence. Do not flip `BLK-016 → ✅ SHIPPED` in the Crontech BUILD_BIBLE without owner authorisation per Crontech CLAUDE.md §0.7.