CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
BUILD_BIBLE.md
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 9ab6971 | 1 | # GLUECRON BUILD BIBLE |
| 2 | ||
| 74d8c4d | 3 | **Last updated: 2026-05-29T03:00:00Z** |
| 69edb75 | 4 | |
| 9ab6971 | 5 | **This file is the single source of truth for the GlueCron build.** |
| 6 | ||
| 7 | **Every Claude agent MUST read this file in full before touching code. No exceptions.** | |
| 8 | ||
| 9 | 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. | |
| 10 | ||
| 69edb75 | 11 | > **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. |
| 12 | ||
| 9ab6971 | 13 | --- |
| 14 | ||
| 15 | ## 1. AGENT POLICY (READ FIRST, FOLLOW ALWAYS) | |
| 16 | ||
| 17 | ### 1.1 Required reads at session start | |
| 18 | 1. `BUILD_BIBLE.md` (this file) — complete | |
| 19 | 2. `CLAUDE.md` — stack + architecture | |
| 20 | 3. `README.md` — user-facing overview | |
| 21 | 4. Most recent commit on the current branch (`git log -1 --stat`) | |
| 22 | ||
| 23 | ### 1.2 Do-not-undo rule | |
| 24 | - 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. | |
| 25 | - "Refactor" is not permission. "Clean up" is not permission. "Simplify" is not permission. | |
| 26 | - If a locked file seems wrong, open an issue in the plan and keep going on a new block. | |
| 27 | ||
| 28 | ### 1.3 Continuous-build rule | |
| 29 | - The owner runs many parallel projects. Do not stop work to ask for clarification that can be inferred from this file. | |
| 30 | - Default behaviour when a block is partially complete: **finish it, run tests, commit, push, start the next block**. | |
| 31 | - Only stop for genuinely blocking decisions: destructive operations, architectural reversals, requests outside this plan, or repeated test failures you can't diagnose. | |
| 32 | - Never stop because "the session might run out." Commit what works and keep building. | |
| 33 | ||
| 34 | ### 1.4 Branch + commit rules | |
| 35 | - Development branch: whatever the current session was told (check session opening message). Fall back to `main` if none given. | |
| 36 | - One commit per completed block. Message format: `feat(BLOCK-ID): <summary>`. | |
| 37 | - Push after every commit with `git push -u origin <branch>`. | |
| 38 | - Never force-push. Never `--no-verify`. Never amend published commits. | |
| 39 | ||
| 40 | ### 1.5 Quality bars (non-negotiable) | |
| 41 | - `bun test` must pass before every commit. | |
| 42 | - New features ship with tests in `src/__tests__/`. | |
| 43 | - New routes use `softAuth` or `requireAuth` middleware. | |
| 44 | - New DB tables have a corresponding migration in `drizzle/`. | |
| 45 | - AI features use `isAiAvailable()` guards and degrade gracefully without `ANTHROPIC_API_KEY`. | |
| 46 | - Every user-facing failure mode has a fallback — no 500s reach the UI. | |
| 47 | ||
| 48 | ### 1.6 Green-ecosystem-by-default | |
| 49 | - Every new repo auto-configures: gates on, branch protection on, labels seeded, CODEOWNERS synced, welcome issue posted. | |
| 50 | - Users can opt out per feature but defaults are maximum-green. | |
| 51 | - Nothing broken ever reaches production, the website, or the customer. | |
| 52 | ||
| 15e65d2 | 53 | ### 1.7 Parallelism rule (added per owner request) |
| 54 | - **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. | |
| 55 | - 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. | |
| 56 | - 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. | |
| 57 | - When launching multiple agents, send them in a single message with multiple Agent tool calls so they actually run concurrently. | |
| 58 | - 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. | |
| 59 | ||
| 9ab6971 | 60 | --- |
| 61 | ||
| 62 | ## 2. GITHUB PARITY SCORECARD | |
| 63 | ||
| 64 | Legend: ✅ shipped · 🟡 partial · ❌ not built | |
| 65 | ||
| 66 | ### 2.1 Repository hosting | |
| 67 | | Feature | Status | Notes | | |
| 68 | |---|---|---| | |
| 69 | | Git Smart HTTP (clone / push / fetch) | ✅ | `src/routes/git.ts`, `src/git/protocol.ts` | | |
| 70 | | SSH keys | ✅ | `ssh_keys` table, `src/routes/settings.tsx` | | |
| 71 | | Public / private visibility | ✅ | `repositories.isPrivate` | | |
| 72 | | Forking | ✅ | `src/routes/fork.ts` | | |
| 73 | | Stars | ✅ | `stars` table, `/:owner/:repo/star` | | |
| 74 | | Topics | ✅ | `repo_topics` table | | |
| 71cd5ec | 75 | | Archive / disable repo | ✅ | I1 — `src/routes/repo-settings.tsx` archive toggle; `RepoHeader` renders an "Archived" badge when `is_archived=true`. | |
| 76 | | 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. | | |
| 77 | | 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. | | |
| 4a0dea1 | 78 | | 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`. | |
| 9ab6971 | 79 | |
| 80 | ### 2.2 Code browsing | |
| 81 | | Feature | Status | Notes | | |
| 82 | |---|---|---| | |
| 83 | | File tree browser | ✅ | `src/routes/web.tsx` | | |
| 84 | | Syntax highlighting | ✅ | 40+ languages, `src/lib/highlight.ts` | | |
| 85 | | Commit history | ✅ | | | |
| 86 | | Diffs | ✅ | | | |
| 87 | | Blame | ✅ | | | |
| 88 | | Raw file download | ✅ | | | |
| 89 | | Branch switcher | ✅ | | | |
| 90 | | Tag listing | ✅ | new this build | | |
| 91 | | Code search (ILIKE) | ✅ | per-repo + global | | |
| 3cbe3d6 | 92 | | Semantic / embedding search | ✅ | D1 — `code_chunks` table + lexical fallback, optional Voyage `voyage-code-3`; `src/lib/semantic-search.ts`, `src/routes/semantic-search.tsx` | |
| 4c8f666 | 93 | | 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. | |
| 8098672 | 94 | | 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`. | |
| f60ccde | 95 | | 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`. | |
| 3951454 | 96 | | 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`. | |
| d299572 | 97 | | 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. | |
| 0cdfd89 | 98 | | 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`. | |
| 9ab6971 | 99 | |
| 100 | ### 2.3 Collaboration | |
| 101 | | Feature | Status | Notes | | |
| 102 | |---|---|---| | |
| 103 | | Issues (CRUD / comments / labels / close) | ✅ | | | |
| 104 | | Milestones | ✅ | `src/routes/insights.tsx` | | |
| 105 | | Pull requests (CRUD / review / merge) | ✅ | | | |
| 106 | | PR inline comments | ✅ | file+line anchored | | |
| 6fc53bd | 107 | | Draft PRs | ✅ | create as draft, ready-for-review toggle, dedicated tab, merge blocked until ready | |
| 108 | | Reactions (emoji) | ✅ | 8 reactions, toggle via `POST /api/reactions/:t/:id/:emoji/toggle` on issues + PRs + comments | | |
| 9ab6971 | 109 | | Mentions + notifications | ✅ | `src/routes/notifications.tsx` | |
| 110 | | Code owners | ✅ | `src/lib/codeowners.ts` | | |
| 24cf2ca | 111 | | Issue templates | ✅ | `.github/ISSUE_TEMPLATE.md` auto-prefills new issues; frontmatter stripped; `src/lib/templates.ts` | |
| 112 | | PR templates | ✅ | `.github/PULL_REQUEST_TEMPLATE.md` auto-prefills new PRs; `src/lib/templates.ts` | | |
| 113 | | Saved replies | ✅ | per-user canned comments, unique-shortcut, `/settings/replies`, `/api/user/replies` | | |
| 1e162a8 | 114 | | Discussions / forums | ✅ | E2 — categorised threads, pinned/locked, q-and-a answers. `src/routes/discussions.tsx` + `drizzle/0013_discussions.sql` | |
| 115 | | Wikis | ✅ | E3 — markdown pages per repo with revision history + revert. DB-backed v1. `src/routes/wikis.tsx` + `drizzle/0016_wikis.sql` | | |
| 116 | | 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` | | |
| 117 | | AI incident responder | ✅ | D4 — auto-issues on deploy fail, `src/lib/ai-incident.ts` | | |
| 118 | | AI-generated test stubs | ✅ | D8 — `src/lib/ai-tests.ts`, `/:owner/:repo/ai/tests` | | |
| 9ab6971 | 119 | |
| 120 | ### 2.4 Automation + AI | |
| 121 | | Feature | Status | Notes | | |
| 122 | |---|---|---| | |
| ad6d4ad | 123 | | Webhooks (outbound, HMAC signed) | ✅ | `src/routes/webhooks.tsx` | |
| 124 | | GateTest inbound callback | ✅ | `POST /api/hooks/gatetest`, bearer or HMAC | | |
| 125 | | Backup PAT-auth gate ingest | ✅ | `POST /api/v1/gate-runs` | | |
| 9ab6971 | 126 | | Gate runs (test / secret / AI review) | ✅ | `gate_runs` table, `src/routes/gates.tsx` | |
| 127 | | Branch protection | ✅ | `branch_protection` table + UI | | |
| 128 | | Auto-repair engine | ✅ | `src/lib/auto-repair.ts` | | |
| 129 | | Secret scanner | ✅ | 15 patterns, `src/lib/security-scan.ts` | | |
| 130 | | AI security review | ✅ | Sonnet 4, `src/lib/security-scan.ts` | | |
| 131 | | AI commit messages | ✅ | `src/lib/ai-generators.ts` | | |
| 132 | | AI PR summaries | ✅ | | | |
| 3cbe3d6 | 133 | | AI changelogs | ✅ | auto on release create; arbitrary-range viewer at `/:owner/:repo/ai/changelog?from=&to=` (D7) | |
| d299572 | 134 | | 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. | |
| 9ab6971 | 135 | | AI merge conflict resolver | ✅ | `src/lib/merge-resolver.ts` | |
| 136 | | AI chat (global + repo) | ✅ | `src/routes/ask.tsx` | | |
| 3cbe3d6 | 137 | | AI explain-this-codebase | ✅ | D6 — per-commit cached markdown, `GET /:owner/:repo/explain`, `src/lib/ai-explain.ts` + `src/routes/ai-explain.tsx` | |
| d299572 | 138 | | 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. | |
| 5e888b7 | 139 | | 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 | |
| d299572 | 140 | | 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. | |
| 141 | | 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. | | |
| 142 | | 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. | | |
| 4e8f0e3 | 143 | | 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. | |
| 144 | | 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. | | |
| 145 | | 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. | | |
| 7c22521 | 146 | | 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. | |
| 147 | | 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. | | |
| 148 | | 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. | | |
| bcaa2b4 | 149 | | 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`. | |
| 3cbe3d6 | 150 | | 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`. | |
| 08420cd | 151 | | 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. | |
| 3cbe3d6 | 152 | | 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. | |
| 153 | | Semantic code search | ✅ | D1 — see 2.2 | | |
| afd651e | 154 | | 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. | |
| 155 | | Repository intelligence engine | ✅ | `src/lib/intelligence.ts` — repo-level summarisation + signals. | | |
| 156 | | Time-travel code explorer | ✅ | `src/lib/timetravel.ts` — historical snapshot navigation. | | |
| 157 | | Dependency impact analyzer | ✅ | `src/lib/depimpact.ts` — "what breaks if I bump X" cross-reference. | | |
| 158 | | One-click rollback | ✅ | `src/lib/rollback.ts` — fast revert helper for failed deploys. | | |
| 159 | | 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. | | |
| 9ab6971 | 160 | |
| 161 | ### 2.5 Platform | |
| 162 | | Feature | Status | Notes | | |
| 163 | |---|---|---| | |
| 164 | | Dashboard | ✅ | `src/routes/dashboard.tsx` | | |
| 165 | | Explore / discover | ✅ | | | |
| 166 | | Global search | ✅ | repos / users / issues / PRs | | |
| 167 | | Insights (graph, contributors, green rate) | ✅ | `src/routes/insights.tsx` | | |
| 168 | | Releases + tags | ✅ | AI changelog | | |
| 169 | | Personal access tokens | ✅ | SHA-256 hashed | | |
| 058d752 | 170 | | OAuth app provider | ✅ | `src/routes/oauth.tsx`, `src/routes/developer-apps.tsx`, `src/lib/oauth.ts`; `oauth_apps` + `oauth_authorizations` + `oauth_access_tokens` tables | |
| 06139e6 | 171 | | 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). | |
| 71cd5ec | 172 | | GraphQL API | ✅ | G2 — see 2.6 | |
| 058d752 | 173 | | 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) | |
| edf7c36 | 174 | | 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. | |
| 058d752 | 175 | | 2FA / TOTP | ✅ | `src/routes/settings-2fa.tsx`, `src/lib/totp.ts`; `user_totp` + `user_recovery_codes` tables | |
| 176 | | Passkeys / WebAuthn | ✅ | `src/routes/passkeys.tsx`, `src/lib/webauthn.ts`; `user_passkeys` + `webauthn_challenges` tables | | |
| 25a91a6 | 177 | | 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 | |
| 178 | | 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 | | |
| 1e162a8 | 179 | | Gists | ✅ | E4 — multi-file tiny repos with per-revision JSON snapshots + stars. `src/routes/gists.tsx` + `drizzle/0014_gists.sql` | |
| 08420cd | 180 | | 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. | |
| 06139e6 | 181 | | 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. | |
| 25a91a6 | 182 | | 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` | |
| a79a9ed | 183 | | 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. | |
| 184 | | 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. | | |
| d299572 | 185 | | 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. | |
| 9ab6971 | 186 | |
| 187 | ### 2.6 Observability + safety | |
| 188 | | Feature | Status | Notes | | |
| 189 | |---|---|---| | |
| 190 | | Rate limiting | ✅ | `src/middleware/rate-limit.ts` | | |
| 191 | | Request-ID tracing | ✅ | `src/middleware/request-context.ts` | | |
| afd651e | 192 | | 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). | |
| 193 | | Public platform status JSON | ✅ | `src/routes/platform-status.ts` — machine-readable status JSON. | | |
| 194 | | Error tracking | ✅ | `src/lib/observability.ts` — wired into `app.onError`. Sinks: `ERROR_WEBHOOK_URL` and/or `SENTRY_DSN`. Never throws. | | |
| 195 | | Comprehensive REST API v2 | ✅ | `src/routes/api-v2.ts` — full CRUD across resources. Authoritative integration surface alongside the v1 `/api/*` endpoints in §4.6. | | |
| 196 | | API documentation page | ✅ | `src/routes/api-docs.tsx` — interactive in-app docs. | | |
| 197 | | 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. | | |
| 198 | | 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). | | |
| b7b7652 | 199 | | 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`. | |
| afd651e | 200 | | 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. | |
| 201 | | SEO (robots + sitemap) | ✅ | `src/routes/seo.ts` — `/robots.txt` + `/sitemap.xml`. | | |
| 9ab6971 | 202 | | Audit log (table) | ✅ | `audit_log` table | |
| 6fc53bd | 203 | | Audit log UI | ✅ | `/settings/audit` (personal) + `/:owner/:repo/settings/audit` (per-repo, owner-only) | |
| 8f50ed0 | 204 | | 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`. | |
| 205 | | 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`. | | |
| 206 | | 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. | | |
| 207 | | 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. | | |
| 24cf2ca | 208 | | Email notifications | ✅ | opt-in per kind (mention/assign/gate-fail) via `/settings`; provider-pluggable `src/lib/email.ts` (log default, resend in prod) | |
| 08420cd | 209 | | 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. | |
| eae38d1 | 210 | | 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). | |
| 211 | | 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). | | |
| 212 | | Official CLI | ✅ | G3 — `cli/gluecron.ts` Bun-compilable single binary. REST + GraphQL client, `~/.gluecron/config.json` 0600. | | |
| 213 | | VS Code extension | ✅ | G4 — `vscode-extension/` with commands for explain / open-on-web / semantic search / generate tests. | | |
| 9ab6971 | 214 | | Native mobile apps | ❌ | | |
| afd651e | 215 | | 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. | |
| 216 | | GitHub import (single repo) | ✅ | `src/routes/import.tsx` + `src/lib/import-helper.ts`. PAT-based clone-and-rehost. | | |
| 217 | | Bulk GitHub import (org migration) | ✅ | `src/routes/import-bulk.tsx` — paste a token, mirror an entire org in one pass. | | |
| 218 | | 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). | | |
| 219 | | Onboarding flow | ✅ | `src/routes/onboarding.tsx` — guided first-five-minutes for new accounts. | | |
| 220 | | Public help / quickstart | ✅ | `src/routes/help.tsx` — owner-facing migration cheatsheet at `/help`. | | |
| 6fc53bd | 221 | | Dark mode | ✅ | default | |
| 222 | | Light-mode toggle | ✅ | `/theme/toggle` + `theme` cookie, pre-paint script avoids FOUC, nav sun/moon icon | | |
| 9ab6971 | 223 | | Keyboard shortcuts | ✅ | `/shortcuts` page | |
| 71cd5ec | 224 | | 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. | |
| 9ab6971 | 225 | |
| 226 | --- | |
| 227 | ||
| 228 | ## 3. BUILD PLAN (BLOCKS) | |
| 229 | ||
| 230 | Each block is a self-contained unit. Order matters for dependencies. Each block ends with tests + commit + push. | |
| 231 | ||
| 232 | ### BLOCK A — Hardening the current surface | |
| 233 | Polish what's shipped before adding more. **Priority: do this first if parity gaps are minor.** | |
| 6fc53bd | 234 | - **A1** — Dark/light theme toggle (cookie, CSS variable swap) ✅ |
| 235 | - **A2** — Audit log UI page (`/settings/audit` + `/:owner/:repo/settings/audit`) ✅ | |
| 236 | - **A3** — Reactions UI on issues / PRs / comments (data exists) ✅ | |
| 237 | - **A4** — Draft PR toggle + filter ✅ | |
| 24cf2ca | 238 | - **A5** — Issue + PR templates (`.github/*_TEMPLATE.md` auto-prefill) ✅ |
| 239 | - **A6** — Saved replies per user ✅ | |
| 240 | - **A7** — Environments + deployment history UI (`deployments` table) ✅ | |
| 241 | - **A8** — Email notifications (opt-in, provider-pluggable) ✅ | |
| 242 | ||
| 243 | **BLOCK A COMPLETE.** Next: BLOCK B (Identity + orgs). | |
| 9ab6971 | 244 | |
| 245 | ### BLOCK B — Identity + orgs | |
| 058d752 | 246 | - **B1** — Organizations (schema: `organizations`, `org_members`, `teams`, `team_members`) → ✅ shipped (`6563f0a`) |
| 6563f0a | 247 | - Helpers in `src/lib/orgs.ts`: slug validation, role rank, reserved-slug set, loaders |
| 248 | - Routes in `src/routes/orgs.tsx`: list / create / profile / people / teams / team detail | |
| 249 | - Role-based guards: admin adds members, owner grants owner, last-owner demote/remove blocked | |
| 250 | - All sensitive actions `audit()`'d (org.create, member.add/role/remove, team.create, team.member.add/remove) | |
| 058d752 | 251 | - **B2** — Repos owned by orgs (nullable `repositories.orgId`) → ✅ shipped (`7437605`) |
| 252 | - **B3** — Team-based CODEOWNERS (`@org/team` resolution) → ✅ shipped (`40d3e3f`) | |
| 253 | - **B4** — 2FA / TOTP (enroll, recovery codes) → ✅ shipped (`7298a17`) | |
| 254 | - **B5** — WebAuthn / passkeys → ✅ shipped (`2df1f8c`) | |
| 255 | - **B6** — OAuth 2.0 provider (third-party apps can request access) → ✅ shipped (pending final commit) | |
| 9ab6971 | 256 | |
| 257 | ### BLOCK C — Runtime + hosting | |
| 5e888b7 | 258 | - **C1** — Actions-equivalent workflow runner → ✅ shipped (`eafe8c6`) |
| 259 | - Workflow YAML parser (`src/lib/workflow-parser.ts`) — hand-rolled subset | |
| 260 | - Background worker (`src/lib/workflow-runner.ts`) — Bun.spawn, size-capped logs, SIGTERM→SIGKILL timeouts | |
| 261 | - Auto-discovery from `.gluecron/workflows/*.yml` on default-branch push | |
| 262 | - UI at `/:owner/:repo/actions` with manual trigger + cancel | |
| 25a91a6 | 263 | - **C2** — Package registry (npm protocol) → ✅ shipped |
| 264 | - Packument + tarball + publish + yank via `PUT /npm/<name>` + `GET /npm/<name>` | |
| 265 | - PAT (`glc_`) bearer auth for CLI clients; add `//host/npm/:_authToken=<PAT>` to .npmrc | |
| 266 | - Container registry deferred (schema ready for it) | |
| 267 | - **C3** — Pages / static hosting → ✅ shipped | |
| 268 | - Serves `/:owner/:repo/pages/*` from the latest successful `pages_deployments` row | |
| 269 | - Auto-records on push to the repo's configured source branch (default `gh-pages`) | |
| 270 | - Settings UI at `/:owner/:repo/settings/pages` + manual redeploy | |
| 271 | - **C4** — Environments with protected approvals → ✅ shipped | |
| 272 | - Per-repo `environments` with reviewer list + branch-glob allowlist | |
| 273 | - Auto-deploy on main is gated by `requiresApprovalFor()`; pending rows show status `pending_approval` | |
| 274 | - Approve/reject at `POST /:owner/:repo/deployments/:id/approve|reject` | |
| 9ab6971 | 275 | |
| 276 | ### BLOCK D — AI-native differentiation | |
| 277 | This is where GlueCron beats GitHub outright. **Priority: ship these loud.** | |
| 3cbe3d6 | 278 | - **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). |
| 279 | - **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". | |
| 280 | - **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. | |
| 1e162a8 | 281 | - **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`. |
| 282 | - **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`. | |
| 3cbe3d6 | 283 | - **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`. |
| 284 | - **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. | |
| 1e162a8 | 285 | - **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. |
| 3cbe3d6 | 286 | - **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. |
| 9ab6971 | 287 | |
| 288 | ### BLOCK E — Collaboration parity | |
| 1e162a8 | 289 | - **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. |
| 290 | - **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. | |
| 291 | - **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. | |
| 292 | - **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. | |
| a79a9ed | 293 | - **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. |
| 294 | - **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. | |
| 295 | - **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. | |
| 9ab6971 | 296 | |
| 297 | ### BLOCK F — Observability + admin | |
| 8f50ed0 | 298 | - **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. |
| 299 | - **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`. | |
| 300 | - **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. | |
| 301 | - **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`. | |
| 9ab6971 | 302 | |
| 303 | ### BLOCK G — Mobile + client | |
| eae38d1 | 304 | - **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. |
| 305 | - **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. | |
| 306 | - **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. | |
| 307 | - **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`. | |
| 9ab6971 | 308 | |
| 71cd5ec | 309 | ### BLOCK I — Filling parity gaps |
| 310 | - **I1** — Archive / unarchive repository → ✅ shipped. `src/routes/repo-settings.tsx` archive/unarchive toggle (existing `repositories.is_archived` column). `RepoHeader` surfaces an "Archived" badge. | |
| 311 | - **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. | |
| 312 | - **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`). | |
| 313 | - **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. | |
| 08420cd | 314 | - **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. |
| 315 | - **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. | |
| 316 | - **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`. | |
| 4c8f666 | 317 | - **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. |
| 4a0dea1 | 318 | - **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. |
| edf7c36 | 319 | - **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 <provider name>" when enabled. 24 new tests (pure helpers + route-auth smokes). |
| 71cd5ec | 320 | |
| 8098672 | 321 | ### BLOCK J — Beyond-parity advanced features |
| 322 | - **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. | |
| f60ccde | 323 | - **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). |
| 3951454 | 324 | - **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). |
| 7aa8b99 | 325 | - **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). |
| d412586 | 326 | - **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. |
| 9ff7128 | 327 | - **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. |
| d62fb36 | 328 | - **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. |
| 0cdfd89 | 329 | - **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. |
| 8098672 | 330 | |
| 9ab6971 | 331 | ### BLOCK H — Marketplace |
| 06139e6 | 332 | - **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. |
| 333 | - **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. | |
| 9ab6971 | 334 | |
| 7c22521 | 335 | ### BLOCK K — Claude-native development loop |
| 336 | 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. | |
| 337 | - **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. | |
| 338 | - **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. | |
| 339 | - **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". | |
| 340 | ||
| 53d32be | 341 | ### BLOCK L — Hook / mouth / taste — packaging the Claude-native pitch |
| 342 | The marketing + acquisition + activation + retention package. Ten sub-blocks shipped together (PR #62) on 2026-05-13. The whole funnel: | |
| 343 | - **Hook** (acquisition surface): L4 social-proof counters · L5 /vs-github · L10 hero · L8 /pricing | |
| 344 | - **Mouth** (activation): L2 one-command install · L6 GitHub OIDC · L7 Claude Code skills · L3 live /demo | |
| 345 | - **Taste** (retention): L1 Sleep Mode (marquee) · L9 AI hours saved counter | |
| 346 | ||
| 347 | - **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. | |
| 348 | - **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). | |
| 349 | - **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. | |
| 350 | - **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. | |
| 351 | - **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. | |
| 352 | - **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. | |
| 353 | - **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. | |
| 354 | - **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. | |
| 355 | - **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. | |
| 356 | - **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. | |
| 357 | ||
| 05ab9b1 | 358 | ### BLOCK M — 2030 vision: developer painkiller features (2026-05-28+) |
| 359 | ||
| 360 | 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. | |
| 361 | ||
| 362 | - **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. | |
| a991c92 | 363 | - **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 `"*"`). |
| 364 | - **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. | |
| 365 | - **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-*`. | |
| 366 | - **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. | |
| 9fbe6cd | 367 | - **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. |
| 368 | - **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. | |
| 369 | - **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>`. | |
| 22f43f3 | 370 | - **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. |
| 05ab9b1 | 371 | - **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. |
| 372 | - **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. | |
| 373 | - **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. | |
| da24dfe | 374 | - **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. |
| 74d8c4d | 375 | - **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`. |
| 376 | - **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. | |
| 377 | - **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. | |
| 378 | - **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. | |
| eed4684 | 379 | - **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. |
| 05ab9b1 | 380 | |
| 9ab6971 | 381 | --- |
| 382 | ||
| 383 | ## 4. LOCKED BLOCKS (DO NOT UNDO) | |
| 384 | ||
| 385 | Everything below is committed, tested, and load-bearing. **Do not delete, rename, or semantically change without owner permission.** | |
| 386 | ||
| 387 | ### 4.1 Infrastructure (locked) | |
| 388 | - `src/app.tsx` — route composition, middleware order, error handlers | |
| 389 | - `src/index.ts` — Bun server entry | |
| 390 | - `src/lib/config.ts` — env getters (late-binding) | |
| 0cdfd89 | 391 | - `src/db/schema.ts` — 95 tables. New tables only via new migration. |
| 9ab6971 | 392 | - `src/db/index.ts` — lazy proxy DB connection |
| 393 | - `src/db/migrate.ts` — migration runner | |
| 394 | - `drizzle/0000_initial.sql`, `drizzle/0001_green_ecosystem.sql` — migrations | |
| 058d752 | 395 | - `drizzle/0004_org_owned_repos.sql` (Block B2) — migration, never edited in place |
| 396 | - `drizzle/0005_totp_2fa.sql` (Block B4) — migration, never edited in place | |
| 397 | - `drizzle/0006_webauthn_passkeys.sql` (Block B5) — migration, never edited in place | |
| 398 | - `drizzle/0007_oauth_provider.sql` (Block B6) — migration, never edited in place | |
| 5e888b7 | 399 | - `drizzle/0008_workflows.sql` (Block C1) — migration, never edited in place |
| 25a91a6 | 400 | - `drizzle/0009_packages.sql` (Block C2) — migration, never edited in place |
| 401 | - `drizzle/0010_pages.sql` (Block C3) — migration, never edited in place | |
| 402 | - `drizzle/0011_environments.sql` (Block C4) — migration, never edited in place | |
| 3cbe3d6 | 403 | - `drizzle/0012_ai_native.sql` (Block D) — migration, never edited in place. Adds `codebase_explanations`, `dep_update_runs`, `code_chunks`. |
| 1e162a8 | 404 | - `drizzle/0013_discussions.sql` (Block E2) — migration, never edited in place. Adds `discussions`, `discussion_comments`. |
| 405 | - `drizzle/0014_gists.sql` (Block E4) — migration, never edited in place. Adds `gists`, `gist_files`, `gist_revisions`, `gist_stars`. | |
| 406 | - `drizzle/0015_projects.sql` (Block E1) — migration, never edited in place. Adds `projects`, `project_columns`, `project_items`. | |
| 407 | - `drizzle/0016_wikis.sql` (Block E3) — migration, never edited in place. Adds `wiki_pages`, `wiki_revisions`. | |
| a79a9ed | 408 | - `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')`). |
| 409 | - `drizzle/0018_required_checks.sql` (Block E6) — migration, never edited in place. Adds `branch_required_checks`. | |
| 410 | - `drizzle/0019_protected_tags.sql` (Block E7) — migration, never edited in place. Adds `protected_tags`. | |
| 8f50ed0 | 411 | - `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`. |
| 06139e6 | 412 | - `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). |
| 71cd5ec | 413 | - `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. |
| 08420cd | 414 | - `drizzle/0023_sponsors.sql` (Block I6) — migration, never edited in place. Adds `sponsorship_tiers` + `sponsorships` tables. |
| 415 | - `drizzle/0024_email_digest.sql` (Block I7) — migration, never edited in place. Adds `users.notify_email_digest_weekly` + `users.last_digest_sent_at`. | |
| 4c8f666 | 416 | - `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)`. |
| 4a0dea1 | 417 | - `drizzle/0026_repo_mirrors.sql` (Block I9) — migration, never edited in place. Adds `repo_mirrors` (unique on `repository_id`) + `repo_mirror_runs`. |
| edf7c36 | 418 | - `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). |
| 8098672 | 419 | - `drizzle/0028_repo_dependencies.sql` (Block J1) — migration, never edited in place. Adds `repo_dependencies` with indexes on `(repository_id, ecosystem)` + `(name)`. |
| f60ccde | 420 | - `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). |
| 3951454 | 421 | - `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)`). |
| 7aa8b99 | 422 | - `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`). |
| 9ff7128 | 423 | - `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). |
| 0cdfd89 | 424 | - `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). |
| afd651e | 425 | - `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. |
| 426 | - `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). | |
| 427 | - `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. | |
| 428 | - `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). | |
| 9e18483 | 429 | - `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). |
| 7c22521 | 430 | - `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. |
| 53d32be | 431 | - `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. |
| 432 | - `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. | |
| 9ab6971 | 433 | |
| 434 | ### 4.2 Git layer (locked) | |
| 435 | - `src/git/repository.ts` — tree / blob / commits / diff / branches / blame / search / raw / tags / commitsBetween | |
| 436 | - `src/git/protocol.ts` — Smart HTTP pkt-line | |
| 437 | - `src/hooks/post-receive.ts` — CODEOWNERS sync, gates, auto-deploy, webhook fan-out | |
| 438 | ||
| 439 | ### 4.3 Auth + security (locked) | |
| 440 | - `src/lib/auth.ts` — bcrypt, session tokens | |
| 25a91a6 | 441 | - `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. |
| 9ab6971 | 442 | - `src/middleware/rate-limit.ts` — fixed-window limiter |
| 443 | - `src/middleware/request-context.ts` — request-ID | |
| 444 | - `src/lib/security-scan.ts` — `SECRET_PATTERNS` (exported) + `scanForSecrets` + `aiSecurityScan` | |
| 058d752 | 445 | - `src/lib/codeowners.ts` — parser + `ownersForPath` (last-match-wins); team expansion helpers for `@org/team` (Block B3) |
| 446 | - `src/lib/totp.ts` (Block B4) — TOTP enroll / verify / recovery codes | |
| 447 | - `src/lib/webauthn.ts` (Block B5) — WebAuthn registration + assertion helpers | |
| 448 | - `src/lib/oauth.ts` (Block B6) — OAuth 2.0 provider: authorization code grant, token issuance, scope enforcement | |
| 5e888b7 | 449 | - `src/lib/workflow-parser.ts` (Block C1) — YAML subset parser for `.gluecron/workflows/*.yml`. Exports `parseWorkflow(src)` returning `{ ok, workflow | error }`. Never throws. |
| 450 | - `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`. | |
| 25a91a6 | 451 | - `src/lib/packages.ts` (Block C2) — npm protocol helpers: `parsePackageName`, `computeShasum` (sha1), `computeIntegrity` (sha512 base64), `buildPackument`, `resolveRepoFromPackageJson`, `parseRepoUrl`, `tarballFilename`. Pure functions. |
| 452 | - `src/lib/pages.ts` (Block C3) — `onPagesPush` (never throws), `resolvePagesPath` (probe list including pretty URLs + traversal strip), `contentTypeFor` (MIME). | |
| 9e18483 | 453 | - `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. |
| 9ab6971 | 454 | |
| afd651e | 455 | ### 4.3.1 Permissions + collaborators (locked) |
| 456 | - `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. | |
| 457 | - `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. | |
| 458 | - `src/routes/collaborators.tsx` — per-repo collaborator add/list/remove. Owner-or-admin only. Audit-logged. | |
| 459 | - `src/routes/team-collaborators.tsx` — bulk-grant: invite every accepted member of a given team to a repo as a collaborator in one action. | |
| 460 | - `src/routes/invites.tsx` — invite acceptance landing page. Verifies token hash, sets `accepted_at`, redirects to repo. | |
| 461 | ||
| 9ab6971 | 462 | ### 4.4 AI layer (locked) |
| 463 | - `src/lib/ai-client.ts` — Anthropic client + model constants | |
| 4e8f0e3 | 464 | - `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). |
| 9ab6971 | 465 | - `src/lib/ai-chat.ts` — conversational chat |
| 466 | - `src/lib/ai-review.ts` — PR code review | |
| 467 | - `src/lib/auto-repair.ts` — worktree-backed repair commits | |
| 468 | - `src/lib/merge-resolver.ts` — AI merge conflict resolution | |
| 3cbe3d6 | 469 | - `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. |
| 470 | - `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. | |
| 471 | - `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. | |
| 472 | - `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. | |
| 1e162a8 | 473 | - `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`. |
| 474 | - `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. | |
| 475 | - `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. | |
| afd651e | 476 | - `src/lib/intelligence.ts` — repository intelligence engine (repo-level summarisation + signals). |
| 477 | - `src/lib/timetravel.ts` — time-travel code explorer (historical snapshot navigation). | |
| 478 | - `src/lib/depimpact.ts` — dependency impact analyzer (cross-reference of who breaks if X bumps). | |
| 479 | - `src/lib/rollback.ts` — one-click rollback helper for failed deploys. | |
| 9e18483 | 480 | - `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`. |
| afd651e | 481 | - `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. |
| 9e18483 | 482 | - `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. |
| 4e8f0e3 | 483 | - `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. |
| 9e18483 | 484 | - `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. |
| 7c22521 | 485 | - `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. |
| 486 | - `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. | |
| 487 | - `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. | |
| 53d32be | 488 | - `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. |
| 489 | - `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. | |
| 490 | - `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. | |
| 9ab6971 | 491 | |
| 492 | ### 4.5 Platform (locked) | |
| 24cf2ca | 493 | - `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. |
| 494 | - `src/lib/email.ts` — provider-pluggable email sender (`log`|`resend`). `sendEmail()` never throws. `absoluteUrl()` joins paths against `APP_BASE_URL`. | |
| 495 | - `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. | |
| 9ab6971 | 496 | - `src/lib/unread.ts` — unread count helper (never throws) |
| 497 | - `src/lib/repo-bootstrap.ts` — green defaults on repo creation | |
| 498 | - `src/lib/gate.ts` — gate orchestration + persistence | |
| 499 | - `src/lib/cache.ts` — LRU cache, git-cache invalidation | |
| 6fc53bd | 500 | - `src/lib/reactions.ts` — `summariseReactions`, `toggleReaction`, `ALLOWED_EMOJIS`, `EMOJI_GLYPH`, `isAllowedEmoji`, `isAllowedTarget` |
| afd651e | 501 | - `src/lib/observability.ts` — minimal dependency-free observability layer. Wired into `app.onError`. Sinks: `ERROR_WEBHOOK_URL` and/or `SENTRY_DSN`. Never throws. |
| 502 | - `src/lib/sse.ts` — in-process topic-based pub/sub broadcaster for Server-Sent Events. Backs the live UI updates layer. | |
| 503 | - `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. | |
| 53d32be | 504 | - `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. |
| 505 | - `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. | |
| 506 | - `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). | |
| afd651e | 507 | - `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`. |
| 508 | - `src/lib/import-helper.ts` — small helpers for the GitHub import flow (`src/routes/import.tsx`). | |
| 509 | - `src/lib/import-verify.ts` — post-migration smoke verifier (object count, branches, default-branch HEAD). Re-runnable from `src/routes/migrations.tsx`. | |
| 510 | - `src/lib/namespace.ts` — namespace resolution for Block B2 (user vs org slug → owner record). | |
| 511 | - `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`. | |
| 512 | - `src/lib/workflow-conditionals.ts` — `if:` expression evaluator for workflow steps. | |
| 513 | - `src/lib/workflow-matrix.ts` — matrix expansion for workflow jobs. | |
| 514 | - `src/lib/workflow-artifacts.ts` — artifact persistence helpers backing `workflow_run_cache`-style storage. | |
| 4e8f0e3 | 515 | - `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). |
| 9e18483 | 516 | - `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. |
| 517 | - `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. | |
| 518 | - `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. | |
| bcaa2b4 | 519 | - `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. |
| 520 | - `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. | |
| 521 | - `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()`. | |
| 9ab6971 | 522 | |
| 523 | ### 4.6 Routes (locked endpoints — behaviour must be preserved) | |
| 524 | - `src/routes/git.ts` — Smart HTTP (clone/push) | |
| 525 | - `src/routes/api.ts` — REST (`POST /api/repos`, `GET /api/users/:u/repos`, `GET /api/repos/:o/:n`, `POST /api/setup`) | |
| ad6d4ad | 526 | - `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`. |
| 6fc53bd | 527 | - `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. |
| 528 | - `src/routes/audit.tsx` — `GET /settings/audit` (personal) + `GET /:owner/:repo/settings/audit` (owner-only). | |
| 24cf2ca | 529 | - `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`. |
| 530 | - `src/routes/deployments.tsx` — `GET /:owner/:repo/deployments` (grouped by env, success-rate rollup), `GET /:owner/:repo/deployments/:id` (detail). | |
| 6fc53bd | 531 | - `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. |
| 9ab6971 | 532 | - `src/routes/auth.tsx` — register / login / logout |
| 533 | - `src/routes/web.tsx` — home / new / browse / blob / commits / raw / blame / star / search / profile | |
| 534 | - `src/routes/issues.tsx` — issue CRUD + comments + labels + lock | |
| 535 | - `src/routes/pulls.tsx` — PR CRUD + review + merge + close | |
| 536 | - `src/routes/editor.tsx` — web file editor | |
| 537 | - `src/routes/compare.tsx` — base...head diff | |
| 24cf2ca | 538 | - `src/routes/settings.tsx` — profile + password + email notification preferences (`POST /settings/notifications`) |
| 9ab6971 | 539 | - `src/routes/repo-settings.tsx` — repo settings + delete |
| 540 | - `src/routes/webhooks.tsx` — webhook CRUD + test + `fireWebhooks` | |
| 541 | - `src/routes/fork.ts` — fork | |
| 542 | - `src/routes/explore.tsx` — discover | |
| 543 | - `src/routes/tokens.tsx` — personal access tokens | |
| 544 | - `src/routes/contributors.tsx` — contributor list | |
| 545 | - `src/routes/notifications.tsx` — inbox + unread API | |
| 546 | - `src/routes/dashboard.tsx` — authed home (`renderDashboard` exported) | |
| 547 | - `src/routes/ask.tsx` — global + repo AI chat + explain | |
| 548 | - `src/routes/releases.tsx` — tags + AI changelog | |
| 549 | - `src/routes/gates.tsx` — history + settings + branch protection UI | |
| 550 | - `src/routes/insights.tsx` — insights + milestones | |
| 551 | - `src/routes/search.tsx` — global search + `/shortcuts` | |
| 552 | - `src/routes/health.ts` — `/healthz` `/readyz` `/metrics` | |
| 6563f0a | 553 | - `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. |
| 058d752 | 554 | - `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. |
| 555 | - `src/routes/settings-2fa.tsx` (Block B4) — TOTP enroll / verify / disable + recovery codes UI. All require auth. | |
| 556 | - `src/routes/passkeys.tsx` (Block B5) — WebAuthn passkey registration / assertion / management. All require auth. | |
| 557 | - `src/routes/oauth.tsx` (Block B6) — OAuth 2.0 authorize + token + userinfo endpoints. | |
| 558 | - `src/routes/developer-apps.tsx` (Block B6) — developer-facing OAuth app CRUD (`/settings/developer/apps`), client secret rotation, audit-logged. | |
| 5e888b7 | 559 | - `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. |
| 25a91a6 | 560 | - `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. |
| 561 | - `src/routes/packages.tsx` (Block C2) — UI: `/:owner/:repo/packages` list + `/:owner/:repo/packages/:pkgName` detail. | |
| 562 | - `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. | |
| 563 | - `src/routes/environments.tsx` (Block C4) — settings CRUD at `/:owner/:repo/settings/environments`; approval endpoints at `/:owner/:repo/deployments/:id/{approve,reject}`. | |
| 3cbe3d6 | 564 | - `src/routes/ai-explain.tsx` (Block D6) — `GET /:owner/:repo/explain` (softAuth), `POST /:owner/:repo/explain/regenerate` (requireAuth, owner-only). |
| 565 | - `src/routes/ai-changelog.tsx` (Block D7) — `GET /:owner/:repo/ai/changelog` (softAuth). Form + rendered output; `?format=markdown` returns `text/markdown`. | |
| 566 | - `src/routes/copilot.ts` (Block D9) — `POST /api/copilot/completions` (requireAuth, 60/min rate limit), `GET /api/copilot/ping` (public). | |
| 567 | - `src/routes/dep-updater.tsx` (Block D2) — `GET /:owner/:repo/settings/dep-updater` + `POST /:owner/:repo/settings/dep-updater/run` (requireAuth, owner-only). | |
| 568 | - `src/routes/semantic-search.tsx` (Block D1) — `GET /:owner/:repo/search/semantic?q=` (softAuth) + `POST /:owner/:repo/search/semantic/reindex` (requireAuth, owner-only). | |
| 1e162a8 | 569 | - `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). |
| 570 | - `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. | |
| 571 | - `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. | |
| 572 | - `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. | |
| 573 | - `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. | |
| a79a9ed | 574 | - `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. |
| 575 | - `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. | |
| 576 | - `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. | |
| 577 | - `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`. | |
| 578 | - `src/routes/protected-tags.tsx` (Block E7) — `/:owner/:repo/settings/protected-tags` CRUD (owner-only, requireAuth). | |
| 579 | - `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. | |
| 8f50ed0 | 580 | - `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. |
| 581 | - `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. | |
| 582 | - `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. | |
| 583 | - `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. | |
| 584 | - `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`. | |
| 585 | - `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. | |
| 586 | - `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). | |
| eae38d1 | 587 | - `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`. |
| 588 | - `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. | |
| 589 | - `src/routes/graphql.ts` (Block G2) — `POST /api/graphql` JSON endpoint + `GET /api/graphql` GraphiQL-lite explorer (Cmd+Enter to run). | |
| 590 | - `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`. | |
| 591 | - `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`. | |
| 06139e6 | 592 | - `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. |
| 593 | - `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. | |
| 08420cd | 594 | - `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. |
| 595 | - `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. | |
| 596 | - `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. | |
| 597 | - `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. | |
| 598 | - `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`). | |
| 4c8f666 | 599 | - `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. |
| 600 | - `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. | |
| 4a0dea1 | 601 | - `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. |
| 602 | - `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. | |
| 9ff7128 | 603 | - `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. |
| 604 | - `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. | |
| d62fb36 | 605 | - `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. |
| 0cdfd89 | 606 | - `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. |
| 607 | - `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). | |
| afd651e | 608 | - `src/routes/api-v2.ts` — Comprehensive REST API v2 (full CRUD across resources). Authoritative integration surface alongside the v1 endpoints in this section. |
| bcaa2b4 | 609 | - `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. |
| afd651e | 610 | - `src/routes/api-docs.tsx` — interactive in-app API documentation page. |
| 611 | - `src/routes/collaborators.tsx` — repo collaborator add / list / remove. Owner-or-admin only. Audit-logged. (See §4.3.1.) | |
| 612 | - `src/routes/team-collaborators.tsx` — bulk team-to-repo collaborator grant. (See §4.3.1.) | |
| 613 | - `src/routes/invites.tsx` — invite acceptance flow. (See §4.3.1.) | |
| 614 | - `src/routes/events.ts` — `POST /events/...` inbound deploy-event receiver for Crontech (Signal Bus P1). Idempotent via `processed_events`. | |
| 615 | - `src/routes/live-events.ts` — `GET /live-events/:topic` SSE subscription endpoint backed by `src/lib/sse.ts`. | |
| 616 | - `src/routes/health-probe.ts` — extended health + metrics probes for load-balancer + observability sinks. | |
| 617 | - `src/routes/help.tsx` — public `/help` quickstart + API cheatsheet for owners migrating from GitHub. | |
| 618 | - `src/routes/import.tsx` — single-repo GitHub → Gluecron import (PAT-based clone-and-rehost). | |
| 619 | - `src/routes/import-bulk.tsx` — `/import/bulk` paste-a-token org-wide migration. | |
| 620 | - `src/routes/migrations.tsx` — `/migrations` per-user import history dashboard with re-run-verifier button. | |
| 621 | - `src/routes/onboarding.tsx` — guided first-five-minutes flow for new accounts. | |
| 622 | - `src/routes/platform-status.ts` — machine-readable platform status JSON. | |
| 623 | - `src/routes/seo.ts` — `/robots.txt` + `/sitemap.xml`. | |
| 624 | - `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.) | |
| 625 | - `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`. | |
| 626 | - `src/routes/status.tsx` — public `GET /status` HTML dashboard + `/status.svg` shields-style badge. | |
| 627 | - `src/routes/workflow-artifacts.ts` — REST API for workflow run artifacts (workflow engine v2 / Sprint 1). | |
| 628 | - `src/routes/workflow-secrets.tsx` — per-repo workflow secrets settings UI (list / create / delete encrypted secrets). Backed by `workflow_secrets` table from `drizzle/0037`. | |
| 9ab6971 | 629 | |
| 630 | ### 4.7 Views (locked contracts) | |
| 631 | - `src/views/layout.tsx` — `Layout` accepts `title`, `user`, `notificationCount` | |
| 3cbe3d6 | 632 | - `src/views/components.tsx` — `RepoHeader`, `RepoNav` (active: `code|issues|pulls|commits|releases|actions|gates|insights|explain|changelog|semantic`), `RepoCard`, etc. |
| 6fc53bd | 633 | - `src/views/reactions.tsx` — `ReactionsBar` (no-JS compatible, form-per-emoji) |
| 634 | - Nav links: logo · search · theme-toggle · Explore · Ask · Notifications · New · Profile (or Sign in / Register) | |
| 9ab6971 | 635 | - Keyboard chords: `/`, `Cmd+K`, `?`, `n`, `g d`, `g n`, `g e`, `g a` |
| 636 | ||
| 4e8f0e3 | 637 | ### 4.7.1 Workflow runner extensions (locked) |
| 638 | - `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. | |
| 639 | ||
| 9ab6971 | 640 | ### 4.8 Tests (locked) |
| 641 | - `src/__tests__/green-ecosystem.test.ts` — secret scanner, codeowners, AI fallback, health, rate-limit headers, `/shortcuts`, `/search` | |
| 642 | - All other existing test files — do not delete without owner permission | |
| 643 | ||
| 644 | ### 4.9 Invariants (never break these) | |
| 645 | - `isAiAvailable()` guard returns true fallback strings when no ANTHROPIC_API_KEY. AI features degrade gracefully. | |
| 646 | - `getUnreadCount` never throws; returns 0 on any error. | |
| 647 | - Rate-limit middleware adds `X-RateLimit-Limit` + `X-RateLimit-Remaining` to every response, including 500s. | |
| 648 | - `c.header("X-Request-Id", ...)` set by request-context on every response. | |
| 649 | - Secret scanner skips binary/lock paths (`shouldSkipPath`). | |
| 650 | - `SECRET_PATTERNS` is an exported array. Its shape is `{ type, regex, severity }`. | |
| 6fc53bd | 651 | - Theme routes live outside `/settings/*` (they must work for logged-out visitors). Cookie name: `theme`, values: `dark|light`. |
| 652 | - Draft PRs cannot be merged — `/pulls/:n/merge` returns a redirect with the draft error when `pr.isDraft=true`. | |
| 653 | - Reactions API accepts only `ALLOWED_EMOJIS` and `ALLOWED_TARGETS`. Toggle is idempotent per (user, target, emoji). | |
| 24cf2ca | 654 | - `sendEmail()` never throws — always resolves to `{ ok, provider, ... }`. Email failures never break notification delivery or the primary request path. |
| 655 | - 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. | |
| 656 | - Issue + PR template loading must return `null` on any git-subprocess failure (templates are a convenience, not a requirement). Forms always render. | |
| 9ab6971 | 657 | |
| 658 | --- | |
| 659 | ||
| 660 | ## 5. OPERATIONAL NOTES | |
| 661 | ||
| 9dd96b9 | 662 | > 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. |
| 663 | ||
| 9ab6971 | 664 | ### 5.1 Running locally |
| 665 | ```bash | |
| 666 | bun install | |
| 667 | bun dev # hot reload | |
| 53d32be | 668 | 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. |
| 9ab6971 | 669 | bun run db:migrate |
| 670 | ``` | |
| 671 | ||
| 672 | ### 5.2 Environment | |
| 673 | - `DATABASE_URL` — Neon Postgres | |
| 674 | - `ANTHROPIC_API_KEY` — unlocks AI features | |
| 675 | - `GIT_REPOS_PATH` — default `./repos` | |
| 676 | - `PORT` — default 3000 | |
| 24cf2ca | 677 | - `EMAIL_PROVIDER` — `log` (default, stderr-only) or `resend` |
| 678 | - `EMAIL_FROM` — sender address for outbound mail | |
| 679 | - `RESEND_API_KEY` — required when `EMAIL_PROVIDER=resend` | |
| 680 | - `APP_BASE_URL` — canonical URL used to build absolute links in emails | |
| 3cbe3d6 | 681 | - `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. |
| 9ab6971 | 682 | |
| 683 | ### 5.3 Models | |
| 684 | - `claude-sonnet-4-20250514` — code review, security, chat | |
| 685 | - `claude-haiku-4-5-20251001` — commit messages, summaries, light tasks | |
| 686 | - Swap via `MODEL_SONNET` / `MODEL_HAIKU` constants in `src/lib/ai-client.ts` | |
| 687 | ||
| 688 | ### 5.4 Deployment | |
| 689 | - `railway.toml` / `fly.toml` present | |
| 690 | - Crontech deploy on green push to default branch (can opt out via `autoDeployEnabled`) | |
| 691 | ||
| 692 | --- | |
| 693 | ||
| 694 | ## 6. SESSION WORKFLOW (WHAT THE NEXT AGENT DOES) | |
| 695 | ||
| 696 | 1. Read this file, `CLAUDE.md`, `README.md`, `git log -1 --stat`. | |
| 697 | 2. Check `git status` + current branch. | |
| 698 | 3. Pick the next unfinished block from §3 (lowest letter + number first, unless owner specifies). | |
| 699 | 4. Create a todo list that mirrors the sub-items of that block. | |
| 700 | 5. Build. Write tests. Run `bun test`. | |
| 701 | 6. Commit with `feat(<BLOCK-ID>): ...`. | |
| 702 | 7. Push. | |
| 703 | 8. Update this file: | |
| 704 | - Move the block's row in §2 to ✅ where applicable. | |
| 705 | - Add the block's files to §4 LOCKED BLOCKS. | |
| 706 | - Commit + push again. | |
| 707 | 9. Start the next block. **Do not stop to ask.** | |
| 708 | ||
| 709 | 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. | |
| 710 | ||
| 711 | --- | |
| 712 | ||
| 713 | ## 7. IN-FLIGHT | |
| 714 | ||
| 69edb75 | 715 | **2026-05-28 — Platform redesign + Claude Code integration session (branch `claude/jolly-heisenberg-2sg1Q`):** |
| 716 | ||
| 717 | Changes shipped this session (all pushed, zero TS errors, GateTest green): | |
| 718 | ||
| 719 | **UX / Design:** | |
| 720 | - `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. | |
| 721 | - `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). | |
| 722 | - `src/routes/pulls.tsx` — `?sort=newest|oldest|updated` + `?author=` filter with input field. | |
| 723 | ||
| 724 | **Claude Code integration:** | |
| 725 | - `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). | |
| 726 | - `src/routes/connect.tsx` — `/connect/claude-guide` onboarding page: 4 steps (token → remote → MCP → push) + "Why Gluecron?" benefits grid. | |
| 727 | ||
| 728 | **Infrastructure:** | |
| 729 | - `src/middleware/no-cache.ts` — stamps `Cache-Control: no-store` on all text/html responses; assets unaffected. | |
| 730 | - `src/routes/keyboard-ux.ts` — Ctrl+Enter form submit + copy buttons on code blocks in rendered markdown. | |
| 731 | - `src/lib/mention-autocomplete.ts` — @mention dropdown on all comment textareas. | |
| 732 | - `src/lib/markdown-preview.ts` — Write/Preview tabs on all comment textareas. | |
| 733 | - `.husky/pre-push` — added `fakeFixDetector`, `prSize` to false-positive skip list. | |
| 734 | ||
| 735 | **Bug fixes (all pre-existing):** | |
| 736 | - `src/views/diff-view.tsx:560` — `method="post"` (was `"POST"`, TS2820). | |
| 737 | - `src/lib/ssh-server.ts` — explicit types on ssh2 callback params; `@ts-expect-error` on untyped import. | |
| 738 | - `src/__tests__/editor.test.ts` — `as unknown as Hono` double-cast for Hono type mismatch. | |
| 739 | - `src/routes/workflow-secrets.tsx` — moved `wsecScript` const before route handlers (was forward-referenced past GET handler). | |
| 740 | ||
| 05ab9b1 | 741 | **Additional shipped this session (2026-05-28 continued):** |
| 742 | - ~~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. | |
| 743 | - ~~Bulk issue operations~~ — shipped (`8d1483c`). POST /:owner/:repo/issues/bulk endpoint + floating sticky action bar. | |
| 744 | - `src/__tests__/layout-user-prop.test.ts` — updated nav-user assertion to match redesigned dropdown (`da16579`). Test suite: 2696/0/120. | |
| 745 | - ~~`diff-view.tsx` TS2820~~ — confirmed fixed. | |
| 746 | - ~~L1/K3 follow-ups~~ — confirmed already done. | |
| 747 | ||
| 748 | **In-flight (agents running):** | |
| 749 | - **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`. | |
| 750 | - **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`. | |
| 69edb75 | 751 | |
| 05ab9b1 | 752 | **Repo health score** — already exists at `src/routes/health.tsx` (uses `computeHealthScore` from `src/lib/intelligence.ts`). No new work needed. |
| 988380a | 753 | |
| 53d32be | 754 | **2026-05-13 — BLOCK L (PR #62) follow-ups (all non-blocking; suite is 1491/0/2 green):** |
| 755 | ||
| 756 | 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. | |
| 757 | 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. | |
| 758 | 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. | |
| 759 | 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. | |
| 760 | 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. | |
| 761 | 6. L4 accepts both `success` and `succeeded` for `deployments.status` until the schema convention normalises. | |
| 762 | 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. | |
| 763 | 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). | |
| 764 | 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. | |
| 765 | 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. | |
| 766 | ||
| 7c22521 | 767 | **2026-05-13 — BLOCK K (PR #62) follow-ups (all four are non-blocking; the suite is 1350/0 green):** |
| 768 | ||
| 769 | 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. | |
| 770 | 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. | |
| 771 | 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. | |
| 772 | 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. | |
| 773 | ||
| afd651e | 774 | **2026-04-29 reconciliation pass (branch `claude/platform-launch-assessment-8dWV8`):** |
| 775 | ||
| 776 | 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. | |
| 777 | ||
| 778 | What was added: | |
| 779 | - §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. | |
| 780 | - §2.5 — surfaced repository collaborators (per-repo roles), single-repo GitHub import, bulk org import, migration history + verifier, onboarding flow, public `/help` quickstart. | |
| 781 | - §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. | |
| 782 | - §4.1 — added migrations 0034–0037 to LOCKED. | |
| 783 | - §4.3.1 (new sub-section) — permissions + collaborators surface (`src/middleware/repo-access.ts`, `src/lib/invite-tokens.ts`, three new collaborator routes). | |
| 784 | - §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. | |
| 785 | - §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). | |
| 786 | - §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`. | |
| 787 | - §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). | |
| 788 | ||
| 789 | What was verified, not changed: | |
| 790 | - `bun install && bun test` → 1033 pass, 0 fail. Suite is genuinely green. | |
| 791 | - `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. | |
| 792 | - Workflow engine v2 is strictly additive on top of the locked v1 (`src/lib/workflow-runner.ts`); no v1 file was replaced. | |
| 793 | ||
| 794 | **Polish batch shipped earlier:** | |
| 988380a | 795 | - `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. |
| 796 | - `src/views/landing.tsx` — marketing landing for logged-out `/`. Accepts optional `stats` prop; `src/routes/web.tsx` queries public repo + user counts. | |
| 797 | - `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`. | |
| 798 | - Doc sync: `README.md`, `DEPLOY.md`, `LAUNCH_TODAY.md` aligned with current reality. | |
| ba93444 | 799 | |
| 800 | **BLK-016 Crontech-Gluecron deploy wire — Gluecron sender rewritten (pending live verification):** | |
| 801 | - `src/hooks/post-receive.ts triggerCrontechDeploy` now matches the Crontech receiver at `apps/api/src/webhooks/gluecron-push.ts`: | |
| 802 | - Endpoint default `POST https://crontech.ai/api/webhooks/gluecron-push` (was `/api/hooks/gluecron/push`). | |
| 803 | - Auth model: HMAC-SHA256 of the JSON body in `X-Gluecron-Signature: sha256=<hex>`, signed with `GLUECRON_WEBHOOK_SECRET` (no longer Bearer). | |
| 804 | - 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`. | |
| 805 | - 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. | |
| 806 | - `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. | |
| 807 | - `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. | |
| 808 | - Config additions: `config.crontechRepo` (env `CRONTECH_REPO`); `config.crontechDeployUrl` default flipped; both reflected in `.env.example`. | |
| 809 | - 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. | |
| 810 | - Removed dead `fanoutWebhooks` helper (defined-but-uncalled, per audit). | |
| 811 | - **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. |