Skip to content

Commit ee9d645

Browse files
committed
polish public beta UX and harden production flows
1 parent 3f68448 commit ee9d645

104 files changed

Lines changed: 10754 additions & 9446 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Vercel production paste list: config/vercel-env-paste.template
2-
# Quick table: config/VERCEL_ENV_KEYS.md
1+
# Deployment key reference: config/VERCEL_ENV_KEYS.md
32

43
# App
54
NEXT_PUBLIC_APP_URL=http://localhost:3000
@@ -19,7 +18,7 @@ GUEST_JWT_SECRET=change-me-minimum-32-characters-long
1918
# LLM generation — Groq is PRIMARY for all text + image (fast, reliable JSON).
2019
# Comma-separate multiple keys (gsk-...) to rotate rate limits.
2120
GROQ_API_KEYS=
22-
# qwen3.6-27b handles BOTH text + vision (fast, reliable JSON with thinking off).
21+
# Text and vision models are configured separately so either can be changed safely.
2322
GROQ_MODEL=openai/gpt-oss-120b
2423
GROQ_VISION_MODEL=qwen/qwen3.6-27b
2524

@@ -43,19 +42,7 @@ CRON_SECRET=local-dev-cron-min-32-chars
4342
SUPERMEMORY_API_KEY=
4443
SUPERMEMORY_BASE_URL=http://localhost:6767
4544

46-
# Observability
47-
LANGFUSE_PUBLIC_KEY=
48-
LANGFUSE_SECRET_KEY=
49-
LANGFUSE_BASE_URL=https://cloud.langfuse.com
50-
SENTRY_DSN=
51-
NEXT_PUBLIC_SENTRY_DSN=
52-
53-
# Billing Phase 2
54-
RAZORPAY_KEY_ID=
55-
RAZORPAY_KEY_SECRET=
56-
RAZORPAY_WEBHOOK_SECRET=
57-
58-
# Email Phase 2
45+
# Email reminders
5946
RESEND_API_KEY=
6047
RESEND_FROM_EMAIL=noreply@localhost
6148

@@ -67,10 +54,6 @@ TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
6754
# Optional dedicated SMS from-number (E.164). Trial: verify destination numbers.
6855
TWILIO_SMS_FROM=
6956

70-
# Analytics
71-
NEXT_PUBLIC_POSTHOG_KEY=
72-
POSTHOG_API_KEY=
73-
7457
# Rankings
7558
LEADERBOARD_MIN_SAMPLE=5
76-
LEADERBOARD_PUBLIC_ENABLED=false
59+
LEADERBOARD_PUBLIC_ENABLED=false
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Bug report
2+
description: Report a reproducible product defect without personal case data.
3+
title: "[Bug]: "
4+
labels: [bug]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: Do not include account numbers, documents, phone numbers, credentials, or other personal data.
9+
- type: textarea
10+
id: behaviour
11+
attributes:
12+
label: What happened?
13+
description: Include the route, expected result, and actual result.
14+
validations:
15+
required: true
16+
- type: textarea
17+
id: reproduce
18+
attributes:
19+
label: Safe reproduction
20+
description: Use sample data only.
21+
validations:
22+
required: true
23+
- type: input
24+
id: environment
25+
attributes:
26+
label: Browser and device
27+
placeholder: Chrome 140, Android 15, Pixel 7
28+
- type: checkboxes
29+
id: privacy
30+
attributes:
31+
label: Privacy check
32+
options:
33+
- label: I removed all personal data and secrets.
34+
required: true

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Private security report
4+
url: https://github.com/thribhuvan003/unhold/security/advisories/new
5+
about: Report vulnerabilities privately; never attach real case data.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Feature request
2+
description: Propose a focused improvement to the user-controlled case workflow.
3+
title: "[Feature]: "
4+
labels: [enhancement]
5+
body:
6+
- type: textarea
7+
id: problem
8+
attributes:
9+
label: User problem
10+
description: Describe the problem before proposing UI or implementation.
11+
validations:
12+
required: true
13+
- type: textarea
14+
id: outcome
15+
attributes:
16+
label: Desired outcome
17+
validations:
18+
required: true
19+
- type: checkboxes
20+
id: boundary
21+
attributes:
22+
label: Product boundary
23+
options:
24+
- label: This keeps external submissions review-before-send and makes no outcome guarantee.
25+
required: true

.github/dependabot.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: npm
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
day: monday
8+
groups:
9+
production-dependencies:
10+
dependency-type: production
11+
development-dependencies:
12+
dependency-type: development
13+
open-pull-requests-limit: 5
14+
- package-ecosystem: github-actions
15+
directory: /
16+
schedule:
17+
interval: weekly
18+
day: monday
19+
open-pull-requests-limit: 3

.github/pull_request_template.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## User problem
2+
3+
<!-- What becomes safer, clearer, or more reliable? -->
4+
5+
## Changes
6+
7+
<!-- Keep this concrete. -->
8+
9+
## Safety and data impact
10+
11+
- [ ] No secrets or real user data are included.
12+
- [ ] External communication remains review-before-send.
13+
- [ ] Authentication/authorisation and migration order were considered.
14+
- [ ] Product copy follows `docs/PRODUCT_AND_SAFETY.md`.
15+
16+
## Verification
17+
18+
- [ ] `pnpm verify`
19+
- [ ] `pnpm test:e2e:smoke` when a user journey changed
20+
- [ ] Mobile and keyboard checks when UI changed

.github/workflows/ci.yml

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,104 @@ name: CI
22

33
on:
44
push:
5-
branches: [main]
5+
branches:
6+
- main
7+
- "codex/**"
68
pull_request:
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: ci-${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
env:
19+
CI: "true"
20+
CRON_SECRET: ci-cron-secret-at-least-32-characters
21+
GUEST_JWT_SECRET: ci-guest-secret-at-least-32-characters
22+
NEXT_PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
23+
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-anon-key
24+
SUPABASE_SERVICE_ROLE_KEY: ci-service-role-key
725

826
jobs:
927
verify:
28+
name: Static checks, tests, and build
1029
runs-on: ubuntu-latest
11-
timeout-minutes: 15
30+
timeout-minutes: 20
1231
steps:
13-
- uses: actions/checkout@v4
32+
- name: Checkout
33+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
1434

15-
- uses: pnpm/action-setup@v4
35+
- name: Set up pnpm
36+
uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6
1637

17-
- uses: actions/setup-node@v4
38+
- name: Set up Node.js
39+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
1840
with:
1941
node-version: 22
2042
cache: pnpm
2143

22-
- name: Install
44+
- name: Install dependencies
2345
run: pnpm install --frozen-lockfile
2446

47+
- name: Lint
48+
run: pnpm lint
49+
2550
- name: Typecheck
2651
run: pnpm typecheck
2752

28-
- name: Unit + golden agent tests
53+
- name: Unit and golden-agent tests
2954
run: pnpm test:unit
3055

31-
- name: API contract tests
56+
- name: API and migration contracts
3257
run: pnpm test:contract
3358

3459
- name: No-auto-send guard
3560
run: pnpm verify:no-auto-send
61+
62+
- name: Production build
63+
run: pnpm build
64+
65+
secrets:
66+
name: Current-tree secret scan
67+
runs-on: ubuntu-latest
68+
timeout-minutes: 5
69+
steps:
70+
- name: Checkout
71+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
72+
with:
73+
fetch-depth: 1
74+
75+
- name: Scan with Gitleaks 8.30.1
76+
uses: docker://ghcr.io/gitleaks/gitleaks:v8.30.1
77+
with:
78+
args: dir . --redact --no-banner
79+
80+
browser-smoke:
81+
name: Desktop and mobile browser smoke
82+
needs: verify
83+
runs-on: ubuntu-latest
84+
timeout-minutes: 15
85+
steps:
86+
- name: Checkout
87+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
88+
89+
- name: Set up pnpm
90+
uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6
91+
92+
- name: Set up Node.js
93+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
94+
with:
95+
node-version: 22
96+
cache: pnpm
97+
98+
- name: Install dependencies
99+
run: pnpm install --frozen-lockfile
100+
101+
- name: Install Chromium
102+
run: pnpm exec playwright install --with-deps chromium
103+
104+
- name: Run smoke journeys
105+
run: pnpm test:e2e:smoke

.github/workflows/cron.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
name: Unhold cron (job queue + tick)
22

3+
permissions:
4+
contents: read
5+
36
# Vercel's Hobby plan allows only 2 daily crons (used by reminders + rankings in
47
# vercel.json). Frequent job processing + monitor ticks run here instead — free,
58
# works on any Vercel plan. On Vercel Pro you may instead add these to
69
# vercel.json "crons" and disable this workflow.
710
#
811
# Required repo secrets (Settings → Secrets and variables → Actions):
9-
# APP_URL e.g. https://unholdd.vercel.app (no trailing slash)
12+
# APP_URL e.g. https://www.unhold.live (no trailing slash)
1013
# CRON_SECRET same value as the Vercel env var
1114

1215
on:
1316
schedule:
14-
- cron: '*/10 * * * *' # every 10 minutes (GitHub Actions minimum is 5)
17+
- cron: "*/10 * * * *" # every 10 minutes (GitHub Actions minimum is 5)
1518
workflow_dispatch: {}
1619

1720
concurrency:
@@ -24,15 +27,17 @@ jobs:
2427
steps:
2528
- name: Process job queue
2629
run: |
27-
curl -fsS -m 280 -X POST "$APP_URL/api/v1/internal/jobs/process" \
28-
-H "Authorization: Bearer $CRON_SECRET" || echo "jobs/process failed (non-fatal)"
30+
curl --fail-with-body --silent --show-error --retry 2 --retry-all-errors \
31+
--max-time 280 -X POST "$APP_URL/api/v1/internal/jobs/process" \
32+
-H "Authorization: Bearer $CRON_SECRET"
2933
env:
3034
APP_URL: ${{ secrets.APP_URL }}
3135
CRON_SECRET: ${{ secrets.CRON_SECRET }}
3236
- name: Monitor tick
3337
run: |
34-
curl -fsS -m 280 -X POST "$APP_URL/api/v1/internal/cron/tick" \
35-
-H "Authorization: Bearer $CRON_SECRET" || echo "cron/tick failed (non-fatal)"
38+
curl --fail-with-body --silent --show-error --retry 2 --retry-all-errors \
39+
--max-time 280 -X POST "$APP_URL/api/v1/internal/cron/tick" \
40+
-H "Authorization: Bearer $CRON_SECRET"
3641
env:
3742
APP_URL: ${{ secrets.APP_URL }}
3843
CRON_SECRET: ${{ secrets.CRON_SECRET }}

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ out/
1010
.env
1111
.env.local
1212
.env.*.local
13+
config/vercel-env-paste.template
1314

1415
# debug
1516
npm-debug.log*
@@ -23,6 +24,7 @@ pnpm-debug.log*
2324

2425
# supabase local
2526
.supabase/
27+
supabase/.temp/
2628

2729
# playwright
2830
test-results/
@@ -33,6 +35,9 @@ blob-report/
3335
.DS_Store
3436
coverage/
3537
.vercel
38+
.cache/
39+
.local/
40+
.pnpm-home/
3641

3742
# crash dumps
3843
*.stackdump
@@ -44,4 +49,4 @@ coverage/
4449

4550
# scratch / MCP tooling dirs
4651
.playwright-mcp/
47-
mcps/
52+
mcps/

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Changelog
2+
3+
All notable product changes are documented here. The project follows a pre-1.0, date-based release cadence.
4+
5+
## Unreleased
6+
7+
### Security
8+
9+
- Moved application data access behind explicit server authorisation and removed direct browser table/RPC grants.
10+
- Added profile-role escalation defence, invoker-security views, rate limiting, replay protection, and redacted provider logs.
11+
- Removed a tracked deployment-value file and added current-tree secret scanning.
12+
13+
### Reliability
14+
15+
- Added bounded queue retries, real monitor/escalator dispatch, production build CI, and mobile browser smoke tests.
16+
17+
### Experience
18+
19+
- Simplified the worked example, made long letters progressive-disclosure, and improved mobile touch targets.
20+
21+
## 0.1.0 - 2026-07-14
22+
23+
- Public beta foundation: guided intake, evidence handling, case dashboard, draft ladder, proof gates,
24+
monitoring, data export/erasure, English/Hindi UI, and private evidence storage.

0 commit comments

Comments
 (0)