Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit7c22521unknown_key

docs(K): record BLOCK K (K1+K2+K3) in BUILD_BIBLE.md

docs(K): record BLOCK K (K1+K2+K3) in BUILD_BIBLE.md

Per §6 session workflow:
  • §2.4 parity scorecard — flipped "Model Context Protocol server" to
    note the K1 write tools, and added two new rows: "AI auto-merge
    (gated)" (K2) and "AI-driven autopilot" (K3).
  • §3 BUILD PLAN — added BLOCK K with K1/K2/K3 ✅ shipped entries.
  • §4.1 — added `drizzle/0040_branch_protection_auto_merge.sql` to
    LOCKED (K2). 0039 slot was already taken by `0039_repair_flywheel.sql`
    so K2 took 0040 — strictly additive.
  • §4.4 AI layer — locked `src/lib/auto-merge.ts` (K2),
    `src/lib/pr-merge.ts` (K3), `src/lib/ai-build-tasks.ts` (K3).
  • §4.5 — extended the autopilot row to list the two new K3 tasks
    (`auto-merge-sweep`, `ai-build-from-issues`) alongside the existing
    six.
  • §5.1 — bumped the test-count claim from 1214 / 0 fail (2026-04-30)
    to 1350 / 0 fail (2026-05-13), verified stable across 3 runs.
  • §7 IN-FLIGHT — recorded the four K-follow-ups: the manual-merge
    refactor to share `performMerge`, the K1 merge tool migrating to
    the same helper, a synthetic system user for marker comments, and
    surfacing the new tasks on /admin/autopilot.

Nothing in §4 was deleted or renamed.
Claude committed on May 13, 2026Parent: 2b9055e
1 file changed+2237c225215a7600a9f1e952245858317f747cac6ed
1 changed file+22−3
ModifiedBUILD_BIBLE.md+22−3View fileUnifiedSplit
139139| 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. |
140140| 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. |
141141| 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. |
142| 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`. |
142| 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. |
143| 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. |
144| 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. |
143145| 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`. |
144146| 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`. |
145147| 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. |
326328- **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.
327329- **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.
328330
331### BLOCK K — Claude-native development loop
332The "feature description → live site, no human merge step for the routine cases" loop. Three sub-blocks shipped together (PR #62) on 2026-05-13.
333- **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.
334- **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.
335- **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".
336
329337---
330338
331339## 4. LOCKED BLOCKS (DO NOT UNDO)
375383- `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.
376384- `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).
377385- `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).
386- `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.
378387
379388### 4.2 Git layer (locked)
380389- `src/git/repository.ts` — tree / blob / commits / diff / branches / blame / search / raw / tags / commitsBetween
427436- `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.
428437- `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.
429438- `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.
439- `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.
440- `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.
441- `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.
430442
431443### 4.5 Platform (locked)
432444- `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.
440452- `src/lib/observability.ts` — minimal dependency-free observability layer. Wired into `app.onError`. Sinks: `ERROR_WEBHOOK_URL` and/or `SENTRY_DSN`. Never throws.
441453- `src/lib/sse.ts` — in-process topic-based pub/sub broadcaster for Server-Sent Events. Backs the live UI updates layer.
442454- `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.
443- `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`. `AUTOPILOT_DISABLED=1` opt-out. Observability via `getLastTick()` + `getTickCount()`.
455- `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). `AUTOPILOT_DISABLED=1` opt-out. Observability via `getLastTick()` + `getTickCount()`. K3-added 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`.
444456- `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`.
445457- `src/lib/import-helper.ts` — small helpers for the GitHub import flow (`src/routes/import.tsx`).
446458- `src/lib/import-verify.ts` — post-migration smoke verifier (object count, branches, default-branch HEAD). Re-runnable from `src/routes/migrations.tsx`.
600612```bash
601613bun install
602614bun dev # hot reload
603bun test # 1214 tests currently pass (8 skipped, 0 failed) as of 2026-04-30 — run `bun install` first
615bun test # 1350 tests currently pass (0 failed) as of 2026-05-13 after K1/K2/K3 — run `bun install` first
604616bun run db:migrate
605617```
606618
649661
650662(Intentionally empty. Add here if a block is partially complete at session end.)
651663
664**2026-05-13 — BLOCK K (PR #62) follow-ups (all four are non-blocking; the suite is 1350/0 green):**
665
6661. 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.
6672. 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.
6683. 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.
6694. 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.
670
652671**2026-04-29 reconciliation pass (branch `claude/platform-launch-assessment-8dWV8`):**
653672
654673The 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.
655674