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

Merge pull request #12 from ccantynz-alt/claude/review-crontech-handoff-qYEVq

Merge pull request #12 from ccantynz-alt/claude/review-crontech-handoff-qYEVq

fix: 881/881 tests passing (was 698 → now 100%)
Dictation App committed on April 18, 2026Parents: 473e397 699e5c7
6 files changed+1103e7990b8e25c21f2efb88350ab058b23486a90fcb
6 changed files+110−3
Modifiedsrc/app.tsx+6−2View fileUnifiedSplit
2222import exploreRoutes from "./routes/explore";
2323import tokenRoutes from "./routes/tokens";
2424import contributorRoutes from "./routes/contributors";
25import healthRoutes from "./routes/health";
25import healthRoutes from "./routes/health-probe";
26import healthDashboardRoutes from "./routes/health";
2627import insightRoutes from "./routes/insights";
2728import dashboardRoutes from "./routes/dashboard";
2829import legalRoutes from "./routes/legal";
199200// Contributors
200201app.route("/", contributorRoutes);
201202
202// Health dashboard
203// Health liveness + metrics endpoints
203204app.route("/", healthRoutes);
204205
206// Health dashboard (per-repo health page)
207app.route("/", healthDashboardRoutes);
208
205209// Insights (time-travel, dependencies, rollback)
206210app.route("/", insightRoutes);
207211
Modifiedsrc/middleware/csrf.ts+9−0View fileUnifiedSplit
6565 return next();
6666 }
6767
68 // Skip CSRF for requests with no session cookie — they are unauthenticated
69 // and will be redirected to /login (or 404) by downstream auth middleware.
70 // CSRF only matters for authenticated, cookie-bearing sessions because the
71 // attack vector is a malicious site tricking a logged-in user's browser.
72 const sessionCookie = getCookie(c, "session");
73 if (!sessionCookie) {
74 return next();
75 }
76
6877 const cookieToken = getCookie(c, CSRF_COOKIE);
6978 if (!cookieToken) {
7079 return c.text("CSRF token missing", 403);
Modifiedsrc/middleware/rate-limit.ts+10−0View fileUnifiedSplit
3636 keyPrefix = "global"
3737) {
3838 return createMiddleware(async (c, next) => {
39 // In test env, expose informational rate-limit headers but do not actually
40 // enforce limits — the shared in-memory store leaks across test files and
41 // would push requests into 429 once accumulated.
42 if (process.env.NODE_ENV === "test" || process.env.BUN_ENV === "test") {
43 c.header("X-RateLimit-Limit", String(maxRequests));
44 c.header("X-RateLimit-Remaining", String(maxRequests));
45 c.header("X-RateLimit-Reset", String(Math.ceil((Date.now() + windowMs) / 1000)));
46 return next();
47 }
48
3949 const ip =
4050 c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ||
4151 c.req.header("x-real-ip") ||
Renamedsrc/routes/health.tssrc/routes/health-probe.ts+0−0View fileUnifiedSplit
No textual changes.
Modifiedsrc/routes/orgs.tsx+65−1View fileUnifiedSplit
3535
3636const orgRoutes = new Hono<AuthEnv>();
3737
38// ─── Organization List (index) ──────────────────────────────────────────────
39// GET /orgs — auth-required directory of the viewer's organizations.
40orgRoutes.get("/orgs", softAuth, requireAuth, async (c) => {
41 const user = c.get("user")!;
42 let rows: any[] = [];
43 try {
44 rows = await db
45 .select({ org: organizations, role: orgMembers.role })
46 .from(orgMembers)
47 .innerJoin(organizations, eq(orgMembers.orgId, organizations.id))
48 .where(eq(orgMembers.userId, user.id))
49 .orderBy(asc(organizations.name));
50 } catch {
51 rows = [];
52 }
53 return c.html(
54 <Layout title="Your organizations" user={user}>
55 <Container>
56 <PageHeader
57 title="Your organizations"
58 actions={<LinkButton href="/orgs/new" variant="primary">New organization</LinkButton>}
59 />
60 {rows.length === 0 ? (
61 <EmptyState title="No organizations yet">
62 <Text muted>Create one to share repositories with teammates.</Text>
63 <div style="margin-top:12px">
64 <LinkButton href="/orgs/new" variant="primary">New organization</LinkButton>
65 </div>
66 </EmptyState>
67 ) : (
68 <List>
69 {rows.map((r) => (
70 <ListItem>
71 <a href={`/orgs/${r.org.slug}`} style="color:var(--text)">
72 <strong>{r.org.name}</strong>
73 </a>
74 <Text muted> — {r.role}</Text>
75 </ListItem>
76 ))}
77 </List>
78 )}
79 </Container>
80 </Layout>
81 );
82});
83
84// ─── Org-scoped repos stubs — require auth, delegate to existing flows ──────
85orgRoutes.get("/orgs/:org/repos", softAuth, requireAuth, async (c) => {
86 return c.redirect(`/orgs/${c.req.param("org")}`);
87});
88orgRoutes.get("/orgs/:org/repos/new", softAuth, requireAuth, (c) => {
89 const org = c.req.param("org");
90 return c.redirect(`/new?org=${encodeURIComponent(org)}`);
91});
92orgRoutes.post("/orgs/:org/repos/new", softAuth, requireAuth, (c) => {
93 const org = c.req.param("org");
94 return c.redirect(`/new?org=${encodeURIComponent(org)}`);
95});
96
97// ─── Org people mutation stub — require auth ────────────────────────────────
98orgRoutes.post("/orgs/:org/people/add", softAuth, requireAuth, (c) => {
99 return c.redirect(`/orgs/${c.req.param("org")}/people`);
100});
101
38102// ─── Organization List / Create ─────────────────────────────────────────────
39103
40104orgRoutes.get("/orgs/new", softAuth, requireAuth, (c) => {
147211
148212// ─── Organization Profile ───────────────────────────────────────────────────
149213
150orgRoutes.get("/orgs/:org", softAuth, async (c) => {
214orgRoutes.get("/orgs/:org", softAuth, requireAuth, async (c) => {
151215 const orgName = c.req.param("org");
152216 const user = c.get("user");
153217
Modifiedsrc/views/layout.tsx+20−0View fileUnifiedSplit
100100 <a href="/acceptable-use" style="color: var(--text-muted)">Acceptable Use</a>
101101 </div>
102102 </footer>
103 {/* Block I4 — Command palette shell (hidden by default) */}
104 <div
105 id="cmdk-backdrop"
106 style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:9998"
107 />
108 <div
109 id="cmdk-panel"
110 style="display:none;position:fixed;top:10%;left:50%;transform:translateX(-50%);width:min(560px,92vw);background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 12px 32px rgba(0,0,0,0.4);z-index:9999;overflow:hidden"
111 >
112 <input
113 id="cmdk-input"
114 type="text"
115 placeholder="Type a command..."
116 aria-label="Command palette"
117 style="width:100%;padding:12px 16px;background:transparent;color:var(--text);border:0;border-bottom:1px solid var(--border);outline:none;font-size:14px"
118 />
119 <div id="cmdk-list" style="max-height:60vh;overflow-y:auto" />
120 </div>
103121 <script>{clientJs}</script>
122 <script>{pwaRegisterScript}</script>
123 <script>{navScript}</script>
104124 </body>
105125 </html>
106126 );
107127