Shipshape spine v1: design contract + schema migration#13
Conversation
|
@Dexploarer is attempting to deploy a commit to the SYMBiEX's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughIntroduces Shipshape v1 infrastructure comprising a type-checking Git hook, Claude configuration (settings and skills for audits/spikes), comprehensive PostgreSQL schema additions (organizations, contributor penalties, draft reviews, enhanced contributors/projects), migration SQL, and architectural documentation spanning specifications, design, and implementation planning. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 23 minutes and 7 seconds.Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces the 'Shipshape' contribution-economy spine, adding necessary database schema migrations, organization-level configuration inheritance, and new project-level state management. It also includes utility hooks for typechecking and architecture spikes. My feedback focuses on improving the robustness of the typecheck hook and optimizing the SQL migration backfill to avoid redundant updates.
| # This hook never blocks (never exits 1) — typecheck failures are surfaced as | ||
| # warnings so the user can decide whether to act, not as hard stops. | ||
|
|
||
| set -uo pipefail |
| UPDATE "projects" | ||
| SET "launch_state" = 'launched' | ||
| WHERE "status" IN ('live', 'paused', 'killed', 'simulated_live'); |
There was a problem hiding this comment.
The UPDATE statement lacks a WHERE clause that limits the scope to only those rows that actually need the update. While it is likely safe here, adding a condition like WHERE launch_state != 'launched' would be more efficient and prevent unnecessary row updates.
UPDATE "projects"
SET "launch_state" = 'launched'
WHERE "status" IN ('live', 'paused', 'killed', 'simulated_live')
AND "launch_state" != 'launched';Two MEDIUM-priority polish items from gemini-code-assist: 1. .claude/hooks/typecheck-on-stop.sh: the previous `cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0` silently swallowed the not-in-a-git-repo case — `cd ""` is a no-op in bash, so the `|| exit 0` never fired and the rest of the hook ran in whatever directory the user was sitting in. Now resolves the repo root into a variable, exits 0 when empty, then cd's. 2. 0018_awesome_boom_boom.sql backfill: added `AND launch_state <> 'launched'` to the UPDATE so it only touches rows that still need promotion. No-op on a clean run (column was just added with default 'pending_install'), but scopes partial-retry / re-applied migrations to the actual delta — cheaper, idempotent, self-documenting. Smoke: hook exits 0 with no .ts changes; bun run typecheck clean across all 4 packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
apps/web/db/schema/pending-draft-reviews.ts (1)
47-48: Enforce terminal-state close metadata invariants in the table schema.Right now, Line 47–48 allows closed states without
closedAt/closeReason(and vice versa). Add a DB check so state and close metadata stay consistent.Suggested schema-level guard
import { pgEnum, pgTable, text, integer, timestamp, index, uniqueIndex, + check, } from "drizzle-orm/pg-core"; +import { sql } from "drizzle-orm"; ... (t) => ({ ... stateAgeIdx: index("pending_draft_reviews_state_age_idx").on( t.state, t.firstReviewedAt, ), + closeStateConsistency: check( + "pending_draft_reviews_close_state_consistency", + sql`( + ${t.state} IN ('stale_closed', 'no_penalty_closed', 'merged_via_maintainer') + AND ${t.closedAt} IS NOT NULL + AND ${t.closeReason} IS NOT NULL + ) OR ( + ${t.state} NOT IN ('stale_closed', 'no_penalty_closed', 'merged_via_maintainer') + AND ${t.closedAt} IS NULL + AND ${t.closeReason} IS NULL + )`, + ), }), );Also applies to: 58-62
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/db/schema/pending-draft-reviews.ts` around lines 47 - 48, Add a DB CHECK constraint to ensure terminal-state/close metadata consistency: for the table definition containing columns state, closedAt, and closeReason, add a constraint (e.g. chk_close_metadata_consistency) that enforces "if state is a terminal/closed value then closedAt IS NOT NULL AND closeReason IS NOT NULL, and if state is not terminal then closedAt IS NULL AND closeReason IS NULL"; implement the same check on the other similar table/block referenced (lines 58–62). Name the constraints clearly and use the table's state enum/values to detect terminal states when building the boolean expression.apps/web/db/schema/projects.ts (1)
76-99: ConstrainAlignmentConfig.modeto currently shipped modes.Line 78 currently allows
"multiplicative"and"gate"in persisted config even though this phase documents asymmetric alignment as the shipped v1 behavior. Limiting persisted modes now avoids unsupported runtime states.Based on learnings: `SPEC.md` is the frozen list of invariants and explicit non-goals.Suggested diff
export interface AlignmentConfig { enabled: boolean; - mode: "informational" | "asymmetric" | "multiplicative" | "gate"; + mode: "informational" | "asymmetric"; floor: number; ceiling: number;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/db/schema/projects.ts` around lines 76 - 99, The AlignmentConfig.type union currently allows unsupported persisted values ("multiplicative" and "gate"); update the AlignmentConfig interface (specifically the mode field on AlignmentConfig) to restrict the union to only shipped runtimes (e.g., "informational" | "asymmetric") so persisted configs cannot contain unsupported modes, and run/describe any needed migration or validation to handle existing records using the old values (modify the AlignmentConfig.mode type and add a conversion/validation path in any deserialization/DB read logic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/hooks/typecheck-on-stop.sh:
- Around line 24-27: The current git diff used to populate changed_ts (the git
diff --name-only --diff-filter=ACMR HEAD ...) excludes deletions, so removed
.ts/.tsx files won't trigger the typecheck; update the diff-filter to include
deleted files (e.g., change ACMR to ACMRD) in the command that assigns
changed_ts so deletions are considered when deciding to run the typecheck hook.
In `@apps/web/db/schema/contributor-penalties.ts`:
- Around line 53-56: Add a DB CHECK constraint to enforce consistency between
issuedBy (contributorPenaltyIssuedByEnum "issued_by") and issuedByUserId
("issued_by_user_id"): when issuedBy indicates a human issuer require
issuedByUserId IS NOT NULL, and when it indicates a CI workflow require
issuedByUserId IS NULL; implement this as a table-level CHECK combining both
cases (e.g. (issued_by = '<human-value>' AND issued_by_user_id IS NOT NULL) OR
(issued_by = '<ci_value>' AND issued_by_user_id IS NULL)), and add the same
constraint where the second occurrence of issuedBy/issuedByUserId is defined
(lines ~81-84) so both schema objects enforce the rule.
In `@CLAUDE.md`:
- Line 103: Update the documentation line about proxy.ts to match repo
guidelines: remove any claim that `proxy.ts` mints per-request CSP nonces (the
`x-nonce` behavior) and instead state that `proxy.ts` (Next 16 renamed
middleware) is for redirects only; clarify that CSP/nonce generation belongs to
the app layouts or request handlers and that auth must be revalidated inside
every protected Server Component, Server Action, and route handler (referenced
symbols: proxy.ts, x-nonce, next.config.ts, script-src, protected Server
Component, Server Action, route handler).
In `@docs/shipshape-design.md`:
- Around line 143-145: The design doc uses two different pacing config keys
(perWindowPrCap and perDayMergedPrCap) which will cause divergence; pick one
canonical key (e.g., perDayMergedPrCap) and replace all occurrences of the
alternate (perWindowPrCap) throughout the document so the contract and follow-up
implementation reference the same identifier; ensure related keys
(perPrCommitCap, draftQueueEnabled) remain unchanged and update any
cross-references or sections (§6.5 and lines ~749-751) to use the chosen
canonical symbol.
- Around line 986-987: Remove the stale "14-day window" phrasing in the table
row that currently reads "Existing project owners caught off-guard by 14-day
window" and replace it with language that reflects the frozen invariants in
SPEC.md and §11 (e.g., state there is no grace/claim/migration window and owners
must explicitly unpause to resume), ensuring the table entry and any nearby text
do not imply a migration or grace period and instead reference the explicit
non-goal of claim/grace windows from SPEC.md to keep documentation consistent.
In `@docs/shipshape-implementation-plan.md`:
- Line 37: The fenced dependency-graph block is missing a language tag; update
the opening code fence for that dependency-graph block (the triple-backtick
block that renders the dependency graph) to include a language specifier such as
"text" (i.e., change the opening ``` to ```text) so markdown-lint and docs
checks pass, leaving the closing ``` unchanged.
- Around line 143-145: The documentation defines a mismatched config key —
perWindowPrCap is introduced but later backfilled as perDayMergedPrCap — causing
ambiguous wiring and broken defaults; update the docs and any wiring notes so
the same identifier is used consistently (choose either perWindowPrCap or
perDayMergedPrCap), update all references in Phase 4 (wiring) and the backfill
section to that single chosen symbol, and ensure the described default value and
merging behavior are documented under that unified key so implementation code
(where wiring reads e.g. perWindowPrCap) and any backfill scripts (which
currently reference perDayMergedPrCap) align.
---
Nitpick comments:
In `@apps/web/db/schema/pending-draft-reviews.ts`:
- Around line 47-48: Add a DB CHECK constraint to ensure terminal-state/close
metadata consistency: for the table definition containing columns state,
closedAt, and closeReason, add a constraint (e.g.
chk_close_metadata_consistency) that enforces "if state is a terminal/closed
value then closedAt IS NOT NULL AND closeReason IS NOT NULL, and if state is not
terminal then closedAt IS NULL AND closeReason IS NULL"; implement the same
check on the other similar table/block referenced (lines 58–62). Name the
constraints clearly and use the table's state enum/values to detect terminal
states when building the boolean expression.
In `@apps/web/db/schema/projects.ts`:
- Around line 76-99: The AlignmentConfig.type union currently allows unsupported
persisted values ("multiplicative" and "gate"); update the AlignmentConfig
interface (specifically the mode field on AlignmentConfig) to restrict the union
to only shipped runtimes (e.g., "informational" | "asymmetric") so persisted
configs cannot contain unsupported modes, and run/describe any needed migration
or validation to handle existing records using the old values (modify the
AlignmentConfig.mode type and add a conversion/validation path in any
deserialization/DB read logic).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8e76cc82-403e-4ade-b984-b36a5067c843
📒 Files selected for processing (18)
.claude/hooks/typecheck-on-stop.sh.claude/settings.json.claude/skills/audit/SKILL.md.claude/skills/spike/SKILL.md.gitignoreCLAUDE.mdSPEC.mdapps/web/db/migrations/0018_awesome_boom_boom.sqlapps/web/db/migrations/meta/0018_snapshot.jsonapps/web/db/migrations/meta/_journal.jsonapps/web/db/schema/contributor-penalties.tsapps/web/db/schema/contributors.tsapps/web/db/schema/index.tsapps/web/db/schema/organizations.tsapps/web/db/schema/pending-draft-reviews.tsapps/web/db/schema/projects.tsdocs/shipshape-design.mddocs/shipshape-implementation-plan.md
| perPrCommitCap: number; // NEW; default 5 | ||
| perWindowPrCap: number; // NEW; default 10 — see §6.5 | ||
| draftQueueEnabled: boolean; // NEW; default true — see §6.5 |
There was a problem hiding this comment.
Unify pacing config key naming across the design contract.
Line 144 uses perWindowPrCap, while Line 750 switches to perDayMergedPrCap. Keep one canonical key or the follow-up implementation will diverge.
Suggested correction
- - `perDayMergedPrCap = 10`
+ - `perWindowPrCap = 10`Also applies to: 749-751
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/shipshape-design.md` around lines 143 - 145, The design doc uses two
different pacing config keys (perWindowPrCap and perDayMergedPrCap) which will
cause divergence; pick one canonical key (e.g., perDayMergedPrCap) and replace
all occurrences of the alternate (perWindowPrCap) throughout the document so the
contract and follow-up implementation reference the same identifier; ensure
related keys (perPrCommitCap, draftQueueEnabled) remain unchanged and update any
cross-references or sections (§6.5 and lines ~749-751) to use the chosen
canonical symbol.
Adds the cross-cutting agent + scope-discipline files that the shipshape spine work depends on: - SPEC.md — frozen invariants and explicit non-goals (SOL-only, community-only, Bags handles Incorporation, no grace windows). Scaled from past sessions where invented-adjacent scope caused full reverts. - CLAUDE.md — adds Product Model Constraints, Debugging Priority, and Scope Discipline sections; documents cacheComponents / cacheLife profiles, two-super-admin cosign gate, embed/ route realm. - .claude/skills/audit — production-readiness audit skill that reports against SPEC.md invariants without editing. - .claude/skills/spike — pre-flight architecture validator that prototypes risky assumptions before feature work. - .claude/settings.json + .claude/hooks/typecheck-on-stop.sh — Stop hook that runs typecheck only when uncommitted .ts/.tsx files exist. - .gitignore — share project-level .claude config, keep settings.local per-developer; ignore /spikes/ scratch dir. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single source of truth for the v1 contribution-economy product spine.
Captures every decision from the design conversation:
- Three-PR plan (spine + dogfood / launch gate + install kit / dashboard
configurator) with scope and rough sizing.
- Scoring v1 formula — adds populated reviews, merges, substantive review
score, co-authored credit; wires applyTimeDecay; per-PR commit cap;
fixed self-merge dampener; squash-merge attribution fix.
- Alignment v1 — asymmetric multiplier (×0.5 / ×1.0 / ×1.2) with the
full signal hierarchy (issue links, label matches, file area matches,
CI signals, closed-without-merge penalty).
- Shipshape generator (static rules) + logbook generator (live state),
with public routes at /r/[org]/[repo]/{shipshape,logbook}.md.
- CI ingest endpoint with GitHub Actions OIDC primary auth and HMAC
fallback, four event types (test_result, coverage_delta, bundle_delta,
perf_delta).
- Schema additions: extended ScoreInputs with ci sub-object,
alignmentConfig, agentRoutingPolicy, attributionType,
routesToContributorId.
- Existing-project policy: 14-day migration window with notifications
then auto-pause if shipshape not installed.
- Launch gate (PR 2 preview): pending_install → awaiting_pr_merge →
ready_to_launch → launched, with admin permission re-verification at
every transition.
- Risks + mitigations + test plan + commit-by-commit landing order.
Implementation does not start until this doc is approved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds §6.5 capturing the pacing mechanism: honest contributors can ship unlimited PRs, but score earns are capped per period with carry-forward, and over-cap or deferred drafts auto-route through a no-penalty review. Mechanism in two parts: 1. Per-window scoring cap (default 10 merged PRs). Excess merged PRs score 0 this period but their counts persist; if those PRs merge in future periods, they earn full credit then. This is the burnout-safe property — a contributor who shipped 15 PRs and rested for 30 days still earns from the over-cap PRs in whatever period they merged. 2. Draft auto-review (cron every 6h). Drafts older than draftAutoReviewDelayHours (default 24h) run cheap heuristics: mergeable + no-conflict + substantive + aligned + author-active. All five → elevate (un-draft, request maintainer review). Any failure → no-penalty close (gitshipt:no-penalty-close label, excluded from closedWithoutMerge alignment penalty). Non-intrusive: no PR is auto-converted to draft. gitshipt-bot posts a single guidance comment when a contributor exceeds cap, explaining their options. A gitshipt/score-status check shows cap state but never blocks. Edge case decisions made: - Stale auto-elevated PR: 14d auto-close with 7d reminder ping. Same no-penalty alignment treatment. - Re-elevation abuse: a PR closed-low-value cannot be re-elevated unless the head commit is materially different (passes trivial filter + touches priority area or ≥10 substantive lines + content-hash differs from prior reject). Rate-limited to 1 reattempt per PR per period. - Maintainer manual review beats auto-review. Approve → un-draft and score normally. Request-changes → pause auto-review timer until next push. - gitshipt:wip opt-out label pauses the timer for max 30d (or one full window). Re-applying after expiry requires a material commit. Schema additions (§5.3 ScoringConfig): - perWindowPrCap: number (default 10) - draftQueueEnabled: boolean (default true) - draftAutoReviewDelayHours: number (default 24) Alignment v1 amendments (§8.1): - closedWithoutMerge denominator/sum excludes no-penalty closures - auto_elevated_merged: +0.05 alignment bonus when an elevated PR later merges New workflow: processDraftQueue (apps/web/workflows/processDraftQueue.ts). Updated implementation order to land it after CI ingest and before docs honesty fix. Shipshape output gains §9 "Pacing and the draft queue" so contributors read the rules from the runbook itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sory Pivots from elaborate algorithmic prevention to maintainer-driven catch-at-the-PR enforcement, plus opt-in community verification for the human-element layer algorithm cannot reach. §6.5 simplification: - Drops re-elevation material-commit rules, content-hash normalization, gitshipt:wip cap, self-opened-issue/self-applied-label preflight hardening. These are arms races we will lose. - Keeps three 1-liners that have no exploitable surface: head-SHA delta on review-iteration bonus, linkedOpenIssue author rule, co-author noreply email verification. §6.6 NEW — Penalty system: - Maintainer slash commands parsed from PR comments via webhook handler: /gitshipt flag (yellow, ×0.5 alignment for 30d), /gitshipt ban (red, no earnings 90d + alignment forced to 0), /gitshipt clear, /gitshipt no-penalty-close, /gitshipt confirm-quality, /gitshipt verify-community. - Repeat-red within 12 months auto-escalates to black (permaban, two-super-admin cosign to lift). - Schema: contributor_penalties table with issued_by enum (human_maintainer | ci_workflow), evidence_url required on CI-issued. - Enforcement at compute/dispatch time, never stored on score. - PR check gitshipt/penalty-status (informational, never blocks CI). - Idempotency on (project, contributor, pr, action) within 5min window. §6.7 NEW — Community verification (advised, not enforced): - projects.communityLinks (discord/telegram/x/custom) declared by owner. - contributors.communityVerified flag with audit trail. - Optional payoutConfig.communityVerifiedThresholdLamports gates payouts above threshold on community verification. Below threshold: paid normally. Unverified above threshold: held in escrow until verified or threshold waived. - shipshape.md publishes community section so contributors know the rules. GitShipt does not run, host, or attest to community verification. §10 CI ingest extension: - Two new event types: penalty_issue (level: yellow|red, evidenceUrl required) and penalty_clear. CI cannot issue black. - Rate limit: > 10% of active contributors flagged in window pauses CI penalty events and notifies project owner. Defends against runaway CI rules. §17.2 NEW — Acknowledged "oh well" limitations: - Maintainer-author collusion via alt accounts. - Cross-repo single-human arbitrage. - Owners writing biased CI rules. - Community channels GitShipt does not run. - Substance-checking AI reviews (v2). These attacks exist, are not solvable algorithmically, and are explicitly documented in SPEC.md non-goals. v1 mitigates via the social layer but does not pretend to defeat the human element. The 80/15/5 rule: algorithm 80%, maintainers 15%, accept 5%. SPEC.md updated with the same five acknowledged limitations under a new "Acknowledged limitations — accepted, not defended" section. Implementation order updated to land penalty system after alignment v1 and community verification after penalties. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fleshes out §13 from a thin "format adapters" placeholder into the full install-kit specification. When an owner installs the GitShipt App on a repo, the App opens a single PR (chore: install GitShipt) containing everything the spine needs: Required (locked, can't opt out): - shipshape.md at repo root - .github/workflows/gitshipt-report.yml (CI ingest source) Strongly advised (default on, opt-out at install time): - README.md badge insertion - CLAUDE.md adapter (additive — append, don't overwrite) - .cursor/rules/gitshipt.md (single new file) - .github/copilot-instructions.md adapter (additive) - AGENTS.md pointer (additive) Additive merge logic uses gitshipt:start/gitshipt:end marker comments so re-running install (or auto-update PRs as rules change) replaces just the managed section, never overwriting unrelated content. Idempotent. README badge: [] (https://gitshipt.com/r/{org}/{repo}) Served from app/badge/r/[org]/[repo].svg/route.ts. v1 is static "tracked"; v2 can render live KPIs. Insertion places after first H1, appending to existing badges block if present. Searching GitHub for "gitshipt.com/badge" surfaces all tracked repos — analytics + social proof. Owner install-time checklist UI; choices stored on projects.installPreferences so re-runs honor them. GitHub App permissions documented (Contents r/w, Pull Requests r/w, Metadata r, Issues w for the penalty label management from §6.6). Implementation order updated: badge SVG route lands in PR 1 (needed before any badge resolves); README badge insertion + install kit mechanics in PR 2 as already planned. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ritance Adds §14.5 Organizations capturing the v1 org model. Two decisions made: 1. v1 scope: org-as-namespace + default-config inheritance + read-only org dashboard + cross-repo contributor visibility. Org-level tokens, org-level shipshape, org-wide slash commands, and auto-sync defer to v2 (each needs its own design pass). 2. Inheritance behavior: snapshot at project creation. When org defaults change, existing projects keep their snapshotted config. No auto-sync, no surprise mutations to live payouts. Owner can re-apply explicitly via the dashboard. New schema: - organizations table (gh_org_id, gh_login, default_*_config columns, installed_at/by, primary_wallet_address, audit timestamps) - projects.gh_org_id soft FK Permissions: - Editing org defaults: GitHub-org admin role + wallet-linked - Org dashboard: any contributor with project-level access - Cosign reserved for future irreversible org-level actions §17.1 deferred capabilities updated with the four org-level v2 items. SPEC.md acknowledged-limitations updated with explicit org-coordination non-goals so the system is honest about what's per-repo. Implementation order grows by one item: org dashboard + settings page in PR 1 (cheap once schema is in). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands the data spine for the contribution-economy upgrade. Reflects
every decision in docs/shipshape-design.md across §5, §6.5–6.7, §8,
§10, §12, and §14.5.
New tables:
- organizations — per-org default config templates (§14.5). Snapshotted
into projects at creation; no auto-sync, predictable behavior.
- contributor_penalties — yellow/red/black flags issued by maintainers
via slash commands or CI workflows (§6.6). CHECK constraint enforces
evidence_url for ci_workflow-issued rows.
- pending_draft_reviews — state for the §6.5 auto-review pipeline:
pending_review → elevated_awaiting_maintainer → elevated_reminded
(7d) → stale_closed (14d). Or no_penalty_closed if heuristics fail,
or merged_via_maintainer if a human approves.
contributors gains:
- inputs default rewritten with v1 zeros (merges, reviewSubstantiveScore,
coAuthored, ci.*) so old code that omits them resolves cleanly. v1
fields are TS-optional during transition.
- attribution_type enum (human / agent_routed / bot_treasury /
agent_unrouted) for §6.1 operator routing.
- routes_to_contributor_id soft FK to operator's row.
- community_verified flag + audit trail (verified_by, verified_at) for
§6.7 optional payout-threshold gate.
- gh_user_id index for §14.5 cross-repo aggregation.
projects gains:
- gh_org_id soft FK for org namespace (§14.5).
- alignment_config jsonb (§8) — asymmetric-multiplier defaults.
- agent_routing_policy jsonb (§6.1) — operator bindings + share cap.
- community_links jsonb (§6.7) — Discord/Telegram/X/custom.
- install_preferences jsonb (§13.4) — per-file install opt-in/out.
- launch_state enum (§12) — pending_install / awaiting_pr_merge /
ready_to_launch / launched / failed.
- install_runbook_pr_url / _number / installer_gh_user_id for the
install-PR webhook flow.
Backfill:
- Existing projects with status in ('live','paused','killed',
'simulated_live') get launch_state = 'launched' so they are not
gated by the §12 state machine when PR 2 ships. Draft and
launch_configured projects keep launch_state = 'pending_install'
so they go through the new flow.
Migration safety:
- ENUMs in DO blocks (matches 0011 pattern) — rerun-safe on partial
failure.
- All ADD COLUMN, CREATE TABLE, CREATE INDEX guarded with IF NOT
EXISTS — idempotent.
- FK constraints inline in CREATE TABLE so they only run when the
table is being created.
Verification:
- bun run typecheck across @repo/lib, @repo/shared, @repo/ui, apps/web
— clean.
- bun run db:generate after edits — "No schema changes, nothing to
migrate" confirms 0018_snapshot.json matches schema files.
- Migration body manually scoped to spine work only; auto-generated
drift from missing 0003-0017 snapshots removed by hand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to docs/shipshape-design.md. Lays out a commit-by-commit
landing strategy where each commit is self-contained, independently
testable, and preserves v0 behavior until the explicit wiring commit
lands.
Six phases, 32 commits on top of the schema migration already shipped:
- Phase 1 (12 commits): pure foundations. lib/scoring/v1, alignment,
penalty, pacing; lib/github/indexer/{reviews,squash,coauthor,trivial};
lib/agents/{shipshape,logbook}; lib/badge/svg; lib/orgs/inheritance.
No behavior change; each export is independently testable.
- Phase 2 (5 commits): public routes + dashboards. Badge SVG route,
shipshape/logbook/runbook.json public routes, contributor cross-repo
view, org dashboard read-only, org settings display. Renders DB data;
v0 projects unaffected.
- Phase 3 (3 commits): ingest. Slash-command parser + webhook handler,
CI ingest endpoint with OIDC + HMAC. Writes to DB; scoring still
ignores until Phase 4.
- Phase 4 (5 commits): wiring. computeLeaderboard dispatches v0/v1 by
formulaVersion; indexProjectDeltas calls new helpers when v1;
alignment + pacing in computeLeaderboard; penalty enforcement in
computeLeaderboard + executePayout; community-verified payout gate.
v1 behavior turns on for projects flagged v1; v0 unchanged.
- Phase 5 (3 commits): async + UX. processDraftQueue cron workflow,
gh-bot over-cap comment + score-status check, gh-bot penalty-status
check.
- Phase 6 (4 commits): dogfood + docs honesty. GitShipt own shipshape.md,
gitshipt-report.yml, README badge, /docs/page.tsx update to publish v1
truth.
Includes a dependency graph, reviewable batching suggestions (4 review
chunks), out-of-scope items deferred to PR 2/3, per-phase verification
commands, and pinned answers to five open implementation questions so
they don't keep reappearing per-commit.
This plan supersedes the loose order at the bottom of shipshape-design.md
section 18 and adds the v0-protection invariant + atomicity discipline
that earlier sessions repeatedly violated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier draft had a 14-day grace period for existing v0 projects to
install shipshape before payouts paused. That's a grace window, which
SPEC.md already forbids ("A 7-day or N-day grace/claim window..." was
already an explicit non-goal). This was inconsistent with our own rules.
Replaced with a single SQL backfill (0019_promote_v0_to_v1.sql, lands as
commit 44 after all Phase 4 wiring is verified) that flips every existing
project from v0 to v1 in one cut-over:
- formulaVersion: "v0" → "v1" on all rows
- v1 config defaults populated: perPrCommitCap, perDayMergedPrCap,
draftQueueEnabled, draftAutoReviewDelayHours, trivialCommitFilter,
substantiveReviewFloor, plus v1 weights for merges,
reviewSubstantiveScore, coAuthored
- alignmentConfig / agentRoutingPolicy / communityLinks remain NULL.
v1 code paths short-circuit when null (alignment forced to
informational mode, routing defaults to treasury, no community
surface). Owners populate at their pace via PR 3 configurator.
The v0 dispatch path stays in code as a per-project kill-switch — admin
can flip a single project back to v0 by editing scoringConfig directly
if a critical v1 bug surfaces. Nothing auto-sets it.
Implementation plan updated:
- Phase 4.5 added as a single-commit phase (commit 44) for the backfill,
between wiring (Phase 4) and async/UX (Phase 5).
- Phase 5 commits renumbered 45–47, Phase 6 commits 48–51 (was 47–50).
- "Migration window enforcement" removed from PR 2 out-of-scope list.
- Review chunk C grows by one commit (9 instead of 8) to include the
cut-over.
SPEC.md gets the 14-day window listed as a previously-considered-and-
rejected mechanism alongside the contributor grace window, so future
sessions can't accidentally re-introduce the same shape.
Design doc §2 (non-goals) updated to be explicit: existing-project
grace windows of any kind are forbidden.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes from review feedback on the design + schema PR: 1. .claude/hooks/typecheck-on-stop.sh — graceful bun-not-in-PATH guard (gemini-code-assist and codereviewbot-ai). When `bun` is missing the hook now prints a clear "bun not in PATH; skipping typecheck" message listing the changed TS files, then exits 0 instead of producing a tangled error mixed with shell plumbing. 2. apps/web/db/schema/contributor-penalties.ts — declare the contributor_penalties_ci_evidence_required CHECK constraint via the Drizzle `check()` helper (gemini-code-assist medium-priority finding). The constraint was hand-written into 0018_awesome_boom_boom.sql but absent from the schema, so the next db:generate would have flagged it as drift and tried to drop it. Mirrored declaration prevents that. The `check()` helper from drizzle-orm/pg-core is supported in drizzle-orm@0.45.2 (already pinned). Patched 0018_snapshot.json to include the constraint so the schema-snapshot pair stays consistent and `bun run db:generate` reports "No schema changes, nothing to migrate". Other review comments triaged but not actioned in this commit: - Backfill of launch_state to 'launched' for live/paused/killed/ simulated_live: matches design intent in shipshape-design.md §11 (existing projects past install go directly to launched, regardless of operational status). Will respond inline on the PR. - text-bool 'excluded' column on contributors: pre-existing schema decision, not introduced by this PR. - JSONB query-perf concern on organizations defaults: acceptable tradeoff for the per-org-template inheritance pattern. - pending_draft_reviews state-transition enforcement at DB level: app enforces (workflow updates row only along defined transitions); DB-level state machine would be over-engineering for v1. - tierWeights sum-to-1.0 invariant: pre-existing PayoutConfig type, enforced in the launch-config Server Action, not at the schema layer. bun run typecheck clean, bun run db:generate confirms snapshot matches schema (no migration produced). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two MEDIUM-priority polish items from gemini-code-assist: 1. .claude/hooks/typecheck-on-stop.sh: the previous `cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0` silently swallowed the not-in-a-git-repo case — `cd ""` is a no-op in bash, so the `|| exit 0` never fired and the rest of the hook ran in whatever directory the user was sitting in. Now resolves the repo root into a variable, exits 0 when empty, then cd's. 2. 0018_awesome_boom_boom.sql backfill: added `AND launch_state <> 'launched'` to the UPDATE so it only touches rows that still need promotion. No-op on a clean run (column was just added with default 'pending_install'), but scopes partial-retry / re-applied migrations to the actual delta — cheaper, idempotent, self-documenting. Smoke: hook exits 0 with no .ts changes; bun run typecheck clean across all 4 packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67e241e to
6f08ad9
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (4)
docs/shipshape-design.md (2)
986-987:⚠️ Potential issue | 🟠 MajorRisk table still reintroduces a forbidden 14-day window.
This conflicts with §11’s immediate cut-over and the frozen no-grace-window rules.
As per coding guidelines: "`SPEC.md` is the frozen list of invariants and explicit non-goals."🧩 Suggested fix
-| Existing project owners caught off-guard by 14-day window | Multiple notifications (in-app, email, dashboard banner); explicit unpause is one-click | +| Existing project owners caught off-guard by immediate v0→v1 cut-over | Pre-deploy comms + in-app notice + rollback via per-project `formulaVersion: "v0"` kill-switch |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/shipshape-design.md` around lines 986 - 987, The risk table row introducing an "Existing project owners caught off-guard by 14-day window" violates the frozen invariant in SPEC.md and §11's immediate cut-over/no-grace-window rule; remove or replace the "14-day window" phrasing in the table (the offending table row text) so it reflects immediate cut-over behavior and the frozen no-grace-window policy, and update the mitigation column to list immediate notifications (in-app, email, dashboard banner) and require an explicit one-click unpause—ensure the docs reference SPEC.md/§11 as the authoritative invariant.
143-145:⚠️ Potential issue | 🟠 MajorUnify the pacing config key name across the document.
perWindowPrCapandperDayMergedPrCapare both used; keeping both will create wiring/backfill ambiguity.🧩 Suggested fix (one canonical key)
- - `perDayMergedPrCap = 10` + - `perWindowPrCap = 10`Also applies to: 749-751
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/shipshape-design.md` around lines 143 - 145, The document uses two different pacing keys (perWindowPrCap and perDayMergedPrCap) which will cause ambiguity; pick one canonical key name and replace all occurrences (including the instances at lines referenced and the other occurrence around 749-751) so the config keys are consistent—e.g., choose either perWindowPrCap or perDayMergedPrCap, update the definition block (where perPrCommitCap, perWindowPrCap, draftQueueEnabled are declared) and update every reference across the doc to that single name, and ensure any explanatory notes (like “see §6.5”) reflect the chosen canonical key.docs/shipshape-implementation-plan.md (1)
37-83:⚠️ Potential issue | 🟡 MinorAdd a language tag to the dependency-graph fenced block.
This still triggers markdown lint (
MD040).🧩 Suggested fix
-``` +```text (DONE) 18: schema migration ──┐ @@ └──────────────────────┘</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/shipshape-implementation-plan.mdaround lines 37 - 83, The fenced
dependency-graph block starting with "(DONE) 18: schema migration ──┐" is
missing a language tag which triggers MD040; update the opening triple-backtick
fence to include a language (e.g., add "text" so the fence reads ```text) so the
block is explicitly tagged and Markdown lint stops flagging it.</details> </blockquote></details> <details> <summary>.claude/hooks/typecheck-on-stop.sh (1)</summary><blockquote> `26-26`: _⚠️ Potential issue_ | _🟡 Minor_ **Include deleted `.ts/.tsx` files in the trigger condition.** A deleted TS file can break imports, but `--diff-filter=ACMR` ignores `D`, so this hook can skip typecheck when it should run. <details> <summary>🔧 Suggested fix</summary> ```diff -changed_ts=$(git diff --name-only --diff-filter=ACMR HEAD 2>/dev/null | grep -E '\.(ts|tsx)$' || true) +changed_ts=$(git diff --name-only --diff-filter=ACMRD HEAD 2>/dev/null | grep -E '\.(ts|tsx)$' || true) ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.claude/hooks/typecheck-on-stop.sh at line 26, The hook's changed_ts detection (variable changed_ts) uses git diff --diff-filter=ACMR which omits deleted files (D) so deleted .ts/.tsx that break imports won't trigger typecheck; update the git diff invocation to include deleted files (e.g. change --diff-filter=ACMR to --diff-filter=ACMRD or remove the filter) so deleted .ts/.tsx are captured by the grep and cause the hook to run. ``` </details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/audit/SKILL.md:
- Around line 63-80: The markdown audit report template in
.claude/skills/audit/SKILL.md uses an unlabeled code fence which triggers MD040;
update the opening triple-backtick fence that wraps the template block (the one
before "## Audit — ") to include a language label such as "text" (i.e.,
changetotext) so the fence is labeled and the MD040 warning is
resolved; leave the closing fence as-is and ensure other fences in the file
follow the same pattern if present.In @.claude/skills/spike/SKILL.md:
- Around line 55-70: The markdown fenced code block in the SKILL.md spike report
template is missing a language tag and triggers MD040; update the fenced block
that begins with "# Spike report — " to use a language tag (e.g.,
addtext instead of) so the linter stops flagging it—ensure the opening
fence for the template is changed and the closing fence remains ``` to keep the
block intact.In
@apps/web/db/migrations/0019_awesome_boom_boom.sql:
- Around line 216-217: Edit the backfill comment that currently reads "The
14-day shipshape migration window (§11)..." in the migration comment block and
remove or replace the "14-day shipshape migration window" wording so it no
longer implies a migration window; keep the rest of the comment context but
ensure it aligns with SPEC.md invariants and the no-grace/no-migration-window
contract (i.e., delete the phrase or rewrite to state there's no migration
window).- Around line 106-108: The CHECK constraint concedes empty strings; update the
constraint "contributor_penalties_ci_evidence_required" so that when "issued_by"
= 'ci_workflow' the "evidence_url" is not only non-null but also not blank (e.g.
require evidence_url IS NOT NULL AND trim(evidence_url) <> '' or
length(trim(evidence_url)) > 0) to reject empty/whitespace-only URLs while
preserving the existing issued_by logic.In
@apps/web/db/schema/projects.ts:
- Around line 147-150: Add a state-dependent CHECK constraint on the projects
table to require launch metadata when state advances: update the
schema/migration in apps/web/db/schema/projects.ts to add a CHECK that when
state IN ('awaiting_pr_merge','ready_to_launch','launched') then
installerGhUserId IS NOT NULL AND installPrNumber IS NOT NULL AND
installPrHeadSha IS NOT NULL (and/or any other PR metadata columns used). Apply
the same constraint placement where the projects table is defined/altered (also
for the section referenced around lines 225-230) so the DB rejects rows that
enter those states without the required metadata.- Around line 147-158: The schema is defaulting launchState to "pending_install"
globally which creates impossible combos for non-install projects (e.g., status
= "tracked"); remove the global default on launchState in the project schema (do
not set pending_install in pgEnum creation alone), and instead either make
launchState nullable until the installer owns the row or add an explicit CHECK
constraint that enforces valid (status, launchState) pairs (e.g., only allow
pending_install/awaiting_pr_merge/ready_to_launch/failed when status indicates
an installable project); update any insert paths to explicitly set launchState
where appropriate (references: projectLaunchStateEnum, launchState, status) and
apply the same change where this enum/default appears later in the file (lines
mentioned in review).In
@docs/shipshape-design.md:
- Line 234: There are unlabeled fenced code blocks using just
which trigger markdownlint MD040; update each unlabeled block (the bareoccurrences) to
include a language identifier such astext (or an appropriate language likejson/```yaml) so the linter passes; search for the naked fence tokens and
replace them with labeled fences for the blocks referenced in the comment.
Duplicate comments:
In @.claude/hooks/typecheck-on-stop.sh:
- Line 26: The hook's changed_ts detection (variable changed_ts) uses git diff
--diff-filter=ACMR which omits deleted files (D) so deleted .ts/.tsx that break
imports won't trigger typecheck; update the git diff invocation to include
deleted files (e.g. change --diff-filter=ACMR to --diff-filter=ACMRD or remove
the filter) so deleted .ts/.tsx are captured by the grep and cause the hook to
run.In
@docs/shipshape-design.md:
- Around line 986-987: The risk table row introducing an "Existing project
owners caught off-guard by 14-day window" violates the frozen invariant in
SPEC.md and §11's immediate cut-over/no-grace-window rule; remove or replace the
"14-day window" phrasing in the table (the offending table row text) so it
reflects immediate cut-over behavior and the frozen no-grace-window policy, and
update the mitigation column to list immediate notifications (in-app, email,
dashboard banner) and require an explicit one-click unpause—ensure the docs
reference SPEC.md/§11 as the authoritative invariant.- Around line 143-145: The document uses two different pacing keys
(perWindowPrCap and perDayMergedPrCap) which will cause ambiguity; pick one
canonical key name and replace all occurrences (including the instances at lines
referenced and the other occurrence around 749-751) so the config keys are
consistent—e.g., choose either perWindowPrCap or perDayMergedPrCap, update the
definition block (where perPrCommitCap, perWindowPrCap, draftQueueEnabled are
declared) and update every reference across the doc to that single name, and
ensure any explanatory notes (like “see §6.5”) reflect the chosen canonical key.In
@docs/shipshape-implementation-plan.md:
- Around line 37-83: The fenced dependency-graph block starting with "(DONE)
18: schema migration ──┐" is missing a language tag which triggers MD040;
update the opening triple-backtick fence to include a language (e.g., add "text"
so the fence reads ```text) so the block is explicitly tagged and Markdown lint
stops flagging it.</details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `85fab804-6046-449b-9a32-a343670f64bd` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 67e241eb183546e37dd34f6bca0898b0af9bf0f8 and 6f08ad9d4adc5ae4b21375886db287fe00b6004e. </details> <details> <summary>📒 Files selected for processing (18)</summary> * `.claude/hooks/typecheck-on-stop.sh` * `.claude/settings.json` * `.claude/skills/audit/SKILL.md` * `.claude/skills/spike/SKILL.md` * `.gitignore` * `CLAUDE.md` * `SPEC.md` * `apps/web/db/migrations/0019_awesome_boom_boom.sql` * `apps/web/db/migrations/meta/0019_snapshot.json` * `apps/web/db/migrations/meta/_journal.json` * `apps/web/db/schema/contributor-penalties.ts` * `apps/web/db/schema/contributors.ts` * `apps/web/db/schema/index.ts` * `apps/web/db/schema/organizations.ts` * `apps/web/db/schema/pending-draft-reviews.ts` * `apps/web/db/schema/projects.ts` * `docs/shipshape-design.md` * `docs/shipshape-implementation-plan.md` </details> <details> <summary>✅ Files skipped from review due to trivial changes (5)</summary> * .claude/settings.json * apps/web/db/schema/index.ts * apps/web/db/migrations/meta/_journal.json * apps/web/db/schema/contributor-penalties.ts * apps/web/db/schema/pending-draft-reviews.ts </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (3)</summary> * .gitignore * apps/web/db/schema/organizations.ts * apps/web/db/schema/contributors.ts </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| -- when PR 2 ships. The 14-day shipshape migration window (§11) handles the | ||
| -- runbook side separately. |
There was a problem hiding this comment.
Remove stale migration-window wording from the backfill comment.
The “14-day shipshape migration window” note contradicts the no-grace/no-migration-window contract and can mislead future maintainers.
As per coding guidelines: "SPEC.md is the frozen list of invariants and explicit non-goals."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/db/migrations/0019_awesome_boom_boom.sql` around lines 216 - 217,
Edit the backfill comment that currently reads "The 14-day shipshape migration
window (§11)..." in the migration comment block and remove or replace the
"14-day shipshape migration window" wording so it no longer implies a migration
window; keep the rest of the comment context but ensure it aligns with SPEC.md
invariants and the no-grace/no-migration-window contract (i.e., delete the
phrase or rewrite to state there's no migration window).
| /** | ||
| * Project launch state machine (shipshape design doc §12). Every transition | ||
| * re-verifies the launching user's GitHub admin permission on the target | ||
| * repo; the gate fails closed. |
There was a problem hiding this comment.
Enforce launch metadata once the state machine advances.
The state-machine contract says later transitions re-verify the launching GitHub admin and depend on the install PR, but the schema allows awaiting_pr_merge / ready_to_launch / launched rows with installerGhUserId and PR metadata still null. That creates unrecoverable rows the launch flow cannot safely resume or validate. A state-dependent CHECK in the schema/migration would close that gap.
Also applies to: 225-230
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/db/schema/projects.ts` around lines 147 - 150, Add a state-dependent
CHECK constraint on the projects table to require launch metadata when state
advances: update the schema/migration in apps/web/db/schema/projects.ts to add a
CHECK that when state IN ('awaiting_pr_merge','ready_to_launch','launched') then
installerGhUserId IS NOT NULL AND installPrNumber IS NOT NULL AND
installPrHeadSha IS NOT NULL (and/or any other PR metadata columns used). Apply
the same constraint placement where the projects table is defined/altered (also
for the section referenced around lines 225-230) so the DB rejects rows that
enter those states without the required metadata.
Re-targeted from a closed PR on the fork. Same content; aimed at upstream now.
Summary
Lays the design contract and DB foundation for the contribution-economy spine described in
docs/shipshape-design.md. Implementation lands incrementally on this branch perdocs/shipshape-implementation-plan.md.What's in this PR (10 commits, foundation only):
SPEC.mdinvariants,CLAUDE.mdproduct-model constraints,/auditand/spikeskills, typecheck Stop hook.0018_awesome_boom_boom.sql: extendscontributorsandprojects, addsorganizations,contributor_penalties,pending_draft_reviewstables. Hand-trimmed from auto-gen drift; usesDO $$ EXCEPTIONfor ENUM safety.check()declaration of the CI-evidence-required CHECK constraint).What's NOT in this PR yet (held for follow-up commits on this same branch per the implementation plan): all phases of implementation work — pure-function libs, public routes, ingest endpoints, workflow wiring, dogfood, etc.
Architecture highlights
scoringConfig.formulaVersion.SPEC.md's no-grace-windows rule./gitshipt flag/ban/clear/...slash commands; CI workflows can issue yellow/red but never black; black auto-escalates from repeat reds.communityLinks(Discord/Telegram/X), optionalpayoutConfig.communityVerifiedThresholdLamportsgate. GitShipt does not run or attest to community channels — it surfaces the link and the gate.Test plan
bun run typecheckclean across@repo/lib,@repo/shared,@repo/ui,apps/webbun run db:generatereturns "No schema changes, nothing to migrate" — confirms0018_snapshot.jsonmatches schema filesDO $$ EXCEPTION duplicate_objectfor rerun safetyADD COLUMN/CREATE INDEX/CREATE TABLEuseIF NOT EXISTSlaunch_state = 'launched'so they aren't trapped behind PR 2's launch state machine🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation