chore(deps): bump zod from 4.3.6 to 4.4.3 - #67
Conversation
- Root configs: package.json, pnpm-workspace.yaml, turbo.json - Env: .env.example with per-app DB URLs + app/migrator role separation - Infra: docker-compose (pgvector/pg16 + redis:7-alpine) + multi-db init script - Git hygiene: .gitignore, .gitattributes (LF normalization), .nvmrc (Node 20) - License: MIT - Docs: 13-part design bible in docs/design/ covering Boardly + Knowlex - Overview, monorepo structure, requirements, ER schemas (Prisma) - OpenAPI specs, Week 3 daily task breakdown - ADR 0001-0022, STRIDE threat model, Runbook, rate limits, retention - RAG prompt registry + golden QA eval pipeline - Interview Q&A 30 + portfolio LP + demo storyboards - Self-review critical/high fix log
apps/ - collab (Boardly): Next.js 16 + Turbopack + TailwindCSS + TS, port 3000 - knowledge (Knowlex): Next.js 16 + Turbopack + TailwindCSS + TS, port 3001 packages/ - @craftstack/ui: shared React components entry - @craftstack/auth: Auth.js v5 wrapper entry - @craftstack/db: Prisma client + withTenant helper entry - @craftstack/logger: pino + Sentry entry - @craftstack/config: shared tsconfig.base.json + prettier.config.js - @craftstack/api-client: OpenAPI-generated types entry Workspace install verified (pnpm 9.15, Node 20, 355 packages). Collab production build passes.
Shared packages (ui/auth/db/logger/api-client) had lint/typecheck scripts pointing to tools that were not installed (eslint/tsc configs missing). Turbo invoked them and failed with exit code 2. Scripts will be re-added per package when actual source + configs land. Apps (collab/knowledge) retain their Next.js-provided lint/typecheck.
- prisma + @prisma/client 7.7.0, dotenv, tsx dev deps - prisma.config.ts: Prisma 7 native config (migrator URL resolution) - .gitignore: /src/generated/prisma - dotenv/config imported for env var loading - pg_trgm extension declared for future full-text search
…ership, Invitation) Schema follows docs/design/05_prisma_schemas.md: Auth.js v5 adapter tables: - Account (provider+providerAccountId unique) - Session (sessionToken unique) - VerificationToken (identifier+token unique) Core domain (Boardly): - User: email-unique identity + locale/theme preferences - Workspace: slug-unique tenant with owner + soft-delete - Membership: user x workspace with Role enum (OWNER/ADMIN/EDITOR/VIEWER) - Invitation: email invites with tokenHash + expiresAt + optional inviter (SetNull) Design decisions applied: - onDelete cascades follow docs/design/12_critical_fixes.md C-1 - Invitation.inviterId nullable + SetNull so invites survive user deletion - Indexes: email, slug, owner, deletedAt, workspace+role, workspace+email Generator: Prisma Client 7.7 with fullTextSearchPostgres + postgresqlExtensions. prisma format and prisma generate both pass.
…ity log
Schema additions (docs/design/05_prisma_schemas.md):
Enums:
- ActivityAction (24 variants) for AuditLog-style append-only tracking
- NotificationType (MENTION/ASSIGNED/DUE_SOON/INVITED/COMMENT_ON_CARD)
Kanban domain:
- Board: workspace-scoped with color/icon/archived/position, soft delete
- List: LexoRank position, optional WIP limit
- Card: optimistic locking via version column (ADR-0007),
title/description/dueDate/position, Cascade from List
Labels (many-to-many via CardLabel):
- Label: workspace-scoped name+color, unique per workspace
- CardLabel: composite PK (cardId, labelId)
- CardAssignee: composite PK (cardId, userId) with assignedAt
Comments & attachments:
- Comment: self-referential parentId for threads, soft delete
- Mention: (commentId, userId) unique for per-user notification dispatch
- Attachment: R2-backed (r2Key unique), uploader reference, MIME metadata
Observability:
- ActivityLog: workspace-scoped audit trail with JSON payload,
actor SetNull on user delete (ADR-0010 C-1)
- Notification + NotificationSubscription: Web Push VAPID endpoints
prisma format/validate/generate all pass.
Full monorepo lint/typecheck/build verified locally.
src/lib/db.ts exports a global-cached PrismaClient so HMR in Next.js does not spawn a new connection pool on every module reload. - dev: log query/error/warn - prod: log error only (avoid PII leakage, ADR-0009 observability plan) - globalThis-based caching gated on NODE_ENV !== production
src/lib/lexorank.ts wraps the `lexorank` npm package (Jira-compatible) per ADR-0006 + ADR-0021. API: - first(): rank placed before everything - last(): rank placed after everything - between(prev?, next?): insert between two neighbors - compare(a, b): stable comparator for Array.sort Tests (src/lib/lexorank.test.ts, all passing): 1. first() < last() 2. between(first, last) strictly between 3. between(null, null) yields a valid rank 4. between(prev, undefined) places after prev 5. between(undefined, next) places before next 6. repeated insertions remain strictly ordered 7. compare is antisymmetric vitest.config.ts: node env, v8 coverage over src/lib/.
…r-pg)
Prisma 7 constructor requires either an `adapter` or `accelerateUrl`;
plain DATABASE_URL no longer auto-configures the client.
- @prisma/adapter-pg + pg + @types/pg added
- src/lib/db.ts instantiates PrismaClient({ adapter, log })
- same HMR-safe globalThis caching preserved
- production can later swap the adapter for Neon HTTP driver without
touching call sites
Also: .gitignore the harness-local .claude/ so it does not pollute the repo.
Auth.js v5 (next-auth@5.0.0-beta.31) + @auth/prisma-adapter setup per
ADR-0003 (database session strategy for server-side revocation).
src/auth/
- config.ts : NextAuthConfig with PrismaAdapter + Google/GitHub
providers + signin page override, session callback
exposes internal user.id
- index.ts : re-exports { handlers, signIn, signOut, auth }
- handlers.ts : thin shim for the app route handler
- rbac.ts : roleAtLeast / hasRole / requireRole (throws RoleError)
- rbac.test.ts: exhaustive 4x4 hierarchy matrix (16 Vitest cases)
- types.d.ts : augment Session with internal user.id
vitest.config.ts updated to resolve @/ path alias and widen coverage
to src/**/*.ts (excluding app/, generated/, and *.test.ts).
…guard src/app/api/auth/[...nextauth]/route.ts - Re-exports GET/POST from the Auth.js handlers src/app/signin/page.tsx - Server component with async action handlers for Google/GitHub signIn - Inline SVG brand icons (no extra dependency) - Redirects to /dashboard (or callbackUrl) if already authenticated - Dark neutral-950 aesthetic matching the Boardly brand src/proxy.ts - Next.js 16 renamed `middleware` to `proxy` - Default-export auth() so the proxy runtime recognizes the function - Matcher excludes /signin, /api/auth, and Next static assets Verified with preview: /signin renders 200, /dashboard redirects 307 through the proxy to /signin as expected (no dashboard route yet).
- ApiError base class with status/code/message/details - UnauthorizedError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), BadRequestError (400) - Shape matches OpenAPI Error schema (docs/design/06_openapi_specs.md) - handle(): Route Handler wrapper that turns thrown ApiErrors into JSON 4xx responses and unexpected errors into logged 500s
Returns every non-deleted workspace the authenticated user belongs to,
ordered by join date desc. Unauthenticated requests get a 401 JSON
payload (handled by the new errors/handle wrapper).
src/server/workspace.ts
- listWorkspacesForUser(userId): joins Membership + Workspace,
filters out soft-deleted workspaces, returns { id, name, slug,
color, iconUrl, role }
src/app/api/workspaces/route.ts
- GET handler wired through handle() so errors become proper JSON
- auth() checked first; throws UnauthorizedError if missing session
src/app/page.tsx
- Root '/' now acts as a session gate: authenticated -> /dashboard,
unauthenticated -> /signin. Removes the scaffold landing content.
src/app/dashboard/page.tsx
- Server component rendering header with user email + sign-out form
- Workspace grid (1/2/3 columns responsive) with color swatch,
name, /slug path, and role badge (OWNER/ADMIN/EDITOR/VIEWER)
- Empty-state CTA when the user has no memberships
- 'New workspace' button placeholder pointing at /workspaces/new
src/proxy.ts
- Matcher now excludes /api/* so API routes handle their own auth
and return 401 JSON instead of 307 redirects to /signin.
Confirmed: GET /api/workspaces without session returns
{ code: 'UNAUTHORIZED', message: 'Authentication required' }
with HTTP 401. Page routes still redirect as expected.
Uniform LF, double quotes, trailing commas 'all' applied across existing source files after wiring up prettier config.
src/lib/validation.ts
- parseCreateWorkspaceInput(raw): trims, lowercases slug, enforces
slug regex ^[a-z0-9-]{3,32}$, 80-char name cap, #RRGGBB color
- Aggregates every field error into BadRequestError.details.fieldErrors
so the client can highlight individual fields
src/lib/validation.test.ts
- 8 cases: valid body, valid color, non-object body, bad slug,
missing name, overlong name, malformed color, slug normalization
src/server/workspace.ts
- createWorkspace(userId, input): transactional create with owner
Membership row; throws ConflictError('SLUG_TAKEN') on collision
src/app/api/workspaces/route.ts
- POST handler: auth check, body parse, create, 201 with the row
- Validation errors surface as 400 fieldErrors; slug conflict as 409
src/app/workspaces/new/page.tsx
- Server component form with Tailwind dark-mode styling
- Async server action that catches ApiError, rehydrates field values
via query string, and re-renders with the error banner
- Native HTML pattern='[a-z0-9-]{3,32}' mirrors server-side regex
Verified: POST /api/workspaces unauthenticated returns 401 JSON;
/workspaces/new unauthenticated redirects to /signin with callbackUrl.
apps/collab/next.config.ts
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: camera/microphone/geolocation/payment denied
- Strict-Transport-Security: 2 years + includeSubDomains + preload
- poweredByHeader: false
- reactStrictMode: true
- CSP intentionally deferred until external origins are finalized
docs/architecture/system-overview.md
- Mermaid graph of Edge, Vercel, Fly.io, Data, External APIs,
Observability layers with all relationships drawn
- Per-app resource isolation table
- Two concrete request paths (card edit, RAG question) walking
through the full round trip
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 6. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](pnpm/action-setup@v4...v6) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the react group with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). Updates `react` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react) Updates `react-dom` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-dom) --- updated-dependencies: - dependency-name: react dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
/api/health - Cheap liveness check (no DB hit) for UptimeRobot 4-minute pings - Keeps the Neon free tier warm (ADR-0016) - Cache-Control: no-store so the poll always reaches the app not-found.tsx - Branded 404 with Back to dashboard CTA error.tsx - Global error boundary (client component) - Shows error.digest for Sentry correlation once wired - Try again + Go home buttons - Uses next/link per no-html-link-for-pages lint rule
Same X-Content-Type-Options / X-Frame-Options / Referrer-Policy / Permissions-Policy / HSTS plus poweredByHeader=false, reactStrictMode=true.
prisma/seed.ts - Uses the Prisma PG driver adapter + DIRECT_DATABASE_URL (migrator) - Upserts 2 users, 1 workspace with 2 memberships + 3 labels - Creates a Welcome board with 3 LexoRank-ordered lists and 3 cards - Idempotent via upsert + skipDuplicates for rerun safety pnpm --filter collab db:seed once Docker/Neon is reachable.
src/server/workspace-detail.ts
- loadWorkspaceForMember(userId, slug): returns null for non-members
so the caller can 404 without leaking existence
- One round trip via nested include: boards + memberships + user
- Skips soft-deleted workspaces and boards
src/app/w/[slug]/page.tsx
- Header with workspace color swatch + slug crumb + back link
- Boards grid with 'New board' CTA gated by role (OWNER/ADMIN/EDITOR)
- Empty state when no boards exist
- Members list with role badges and avatar initials
- Self role displayed inline next to member count
- Consistent RoleBadge component (also used in dashboard)
src/server/board.ts - createBoard: enforces role >= EDITOR via roleAtLeast() before INSERT - New boards are placed at the bottom of the workspace via LexoRank last() - Throws NotFoundError if workspace missing, ForbiddenError otherwise src/app/w/[slug]/boards/new/page.tsx - Server-action form with Tailwind dark styling - Rehydrates prior values + error banner via query string - Redirects to /w/[slug]/b/[boardId] on success src/app/w/[slug]/b/[boardId]/page.tsx - Workspace-scoped board query: 404 for non-members or missing board - Static kanban layout (lists + cards); realtime editing arrives in Week 6 - Per-list card count with optional WIP limit display - Subtle board-color tint on the header gradient
…ural + 3 honest-disclose (ADR-0057) (#46) * feat(v0.5.8): drift-audit framework completeness — 13 axes, 10 structural + 3 honest-disclose (ADR-0057) User-side review on 2026-04-28 identified that the 6-axis framework (v0.5.7) had at least 7 more axes uncovered, several with high-impact failure modes. v0.5.8 ships the 13-axis complete framework: 10 structurally enforced via PR-time CI gates + smoke probes, 3 honestly disclosed in threat-model.md as T-07/T-08/T-09. Axis 7 — ADR-claim ↔ Implementation (highest impact, was structural blind spot): - scripts/check-adr-claims.mjs (new) — reads docs/adr/_claims.json, asserts each load-bearing ADR claim against the codebase. Three match modes (regex/contains/exists). 22 initial entries covering ADR-0027 / 0034 / 0035 / 0040 / 0041 / 0046 / 0049 / 0051 / 0053 / 0054 / 0056. PR-blocking via doc-drift-detect job. - docs/adr/_claims.json (new) — claim inventory (JSON to avoid yaml dep) Axis 3 — internal cross-reference (ADR ID resolution): - scripts/check-adr-refs.mjs (new) — walks docs/code, asserts every ADR-NNNN reference resolves to an existing docs/adr/NNNN-*.md. Catches typos (transposed digits) and dangling refs. Axis 12 — external artefact freshness: - .github/workflows/smoke.yml — new step curl -fL --head probes shields.io endpoint badge, both Loom URLs, both Vercel deploys. 4xx/5xx fails the smoke run within 6h. Axes 8/11/13 — honest disclose: - threat-model.md T-07 (axis 8): tests are name-defined not behavior- verified, mutation testing deferred to v0.7.0+ - T-08 (axis 11): decisions without ADR are not auto-detected (false-positive rate of feat:/fix: grep would exceed signal) - T-09 (axis 13): live free-tier quota usage is not in /api/attestation; structural mitigation via ADR-0046 fail-closed ADR-0057 — full MADR with the 13-axis matrix, decision per axis, alternatives explicitly rejected (mutation testing, auto-decision- detection, vendor API integration, OpenAPI contract testing). Cross-references: - docs/adr/README.md index entry - ci.yml: doc-drift-detect job runs check-adr-refs.mjs + check-adr-claims.mjs after check-doc-drift.mjs (~1s extra) - README + portfolio-lp + page.tsx Stat block — ADR count 55 → 56 (jump in numbering: ADR-0055 deliberately skipped, 0057 added) - Banner v0.5.7 → v0.5.8 in 4 status-bearing docs - attestation-data.json regenerated (adr=56, tag=v0.5.8) Local verification: - node scripts/check-doc-drift.mjs → 0 failures / 0 warnings - node scripts/check-adr-claims.mjs → 22/22 pass - node scripts/check-adr-refs.mjs → 53 valid IDs, 0 dangling - pnpm --filter knowledge test → 50/50 pass - pnpm --filter knowledge typecheck/lint/build → clean - pnpm --filter collab build → clean * fix: remove unused imports caught by CodeQL js/unused-local-variable - scripts/generate-attestation-data.mjs: 'dirname' from node:path unused - scripts/check-doc-drift.mjs: 'statSync' from node:fs unused Both were note-severity warnings but CodeQL is configured to PR-block on any new alert. Removing the imports keeps the module imports lean and silences the warning. No behavior change. Local: doc-drift-detect 0 failures / 0 warnings, generate- attestation-data still produces tag=v0.5.8 adr=56. * chore: empty commit to trigger CodeQL re-evaluation after alert dismissal Alerts #19 and #20 (js/unused-local-variable on already-removed imports) were dismissed as false-positive after the underlying fix in 1525275 — the alert tracker did not auto-close them. This empty commit re-triggers the PR checks so the CodeQL gate sees the dismissed state. * chore: trigger CodeQL re-evaluation after pre-existing alerts dismissal * fix: remove TOCTOU race in scripts/check-adr-refs.mjs (CodeQL js/file-system-race) The walk() function called statSync(p) followed by readdirSync(p) / readFileSync(p), which is a TOCTOU race CodeQL flagged as high-severity js/file-system-race. Refactored to use readdirSync(p, { withFileTypes: true }) which returns Dirent objects carrying the file-vs-directory bit in a single syscall. processFile() handles the leaf case (top-level paths like README.md fall through via ENOTDIR catch). Local: still finds 56 ADRs, 53 valid refs, 0 dangling. --------- Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…sclose ratchet on ADR-0057 (#47) Session 265 self-audit identified two issues with the v0.5.8 13-axis framework: (1) the framework foundation was unenforced — `main` had no branch protection or repository ruleset, so all 10 structurally-enforced axes rested on convention rather than policy; (2) the v0.5.8 axis 7 row was an overclaim relative to the actual `_claims.json` coverage (22 entries spanning 11 of 56 ADRs ≈ 20%). v0.5.9 closes both: the foundation via a repository ruleset, and the overclaim via an explicit Coverage honest-disclose section in ADR-0057 itself. Branch protection — repository ruleset on `main` (ADR-0058): - New ruleset `main-branch-protection` (id 15652440) configured via `gh api -X POST repos/.../rulesets`. Rules: - pull_request (required_approving_review_count: 0) - required_status_checks (strict: true) for 7 PR-time contexts (free-tier compliance / lint+typecheck+test+build / doc drift detect / knowlex integration (pgvector) / knowlex a11y gate / Analyze (javascript-typescript) / authed Playwright) - non_fast_forward (force-push to main blocked) - deletion (main cannot be deleted) - bypass_actors: [] (admin bypass disabled) - New `.github/RULESET_DECLARED.md` — offline-auditable marker file mirroring the live ruleset configuration. Asserted by `_claims.json` (axis 7 recursive claim) so the framework defends its own foundation. - New `docs/adr/0058-branch-protection-ci-enforcement.md` — full MADR with rejected-alternatives section (classic protection, no-PR-only- checks, required reviews ≥ 1, repository_admin bypass). Axis 7 honest-disclose — Coverage scope explicit (ADR-0057 ratchet): - ADR-0057 axis 7 row updated from `✅ structural` to `✅ structural (judged-load-bearing coverage; see § Coverage honest-disclose below)`. - New § Coverage honest-disclose section names the actual coverage as 22 entries spanning 11 of 56 ADRs (≈20%), lists the covered ADRs explicitly (0027 / 0034 / 0035 / 0040 / 0041 / 0046 / 0049 / 0051 / 0053 / 0054 / 0056), and distinguishes ADRs with no checkable claim (0001 monorepo / 0002 Prisma / 0017 release-order architectural intent) from ADRs that could be covered but weren't in v0.5.8 (0044 / 0045 / 0048 / 0050 / 0052). Coverage expansion is incremental future-work, not a v0.5.9 blocker. Banner + Stat sync (doc-drift-detect green): - README + portfolio-lp — ADR count 56 → 57 - portfolio-lp lead paragraph + Audit-survivable engineering paragraph cite ADR-0057 + ADR-0058 - interview-qa + system-overview + runbook — banner v0.5.8 → v0.5.9 - apps/collab/src/app/page.tsx Stat block — ADRs 56 → 57 `_claims.json` — ADR-0058 recursive integrity (axis 7 self-assertion): - New entry: `.github/RULESET_DECLARED.md` exists (`match: exists`) - New entry: ruleset id 15652440 contained in marker (`match: contains`) Local verification: - node scripts/check-doc-drift.mjs → 0 failures, 0 warnings - node scripts/check-adr-claims.mjs → 24/24 pass (was 22 + 2 ADR-0058) - node scripts/check-adr-refs.mjs → 54 valid IDs, 0 dangling - pnpm lint → 0 errors (4 pre-existing warnings) - pnpm typecheck → clean - pnpm test → 216/216 (166 collab + 50 knowledge) - pnpm build → clean Verification post-merge: gh api repos/.../rulesets/15652440 \ --jq '{enforcement, bypass_actors, current_user_can_bypass}' → { "enforcement": "active", "bypass_actors": [], "current_user_can_bypass": "never" } Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…future-drift closure + freeze (ADR-0059) (#48) Session 265 audit identified that the v0.5.9 framework, while structurally complete, was at risk of an audit-of-audit loop: each session's self-audit produced new findings, each finding produced a new ratchet, each ratchet introduced a new meta-gap. v0.5.10 escapes the loop by (a) adopting OpenSSF Scorecard for hygiene axes the project was duplicating, (b) closing axes 6/7 future-drift modes, (c) freezing the framework at v1.0 with a date-bound + incident-driven re-audit rule. OpenSSF Scorecard (industry baseline): - .github/workflows/scorecard.yml (new) — weekly + on push to main + on branch_protection_rule. Publishes SARIF to GitHub Security tab + the public scorecard.dev registry. - Coverage delegated (drops self-built duplicates): Branch-Protection live-state / Pinned-Dependencies (Action SHA) / Dependency-Update-Tool / Token-Permissions / Security-Policy / License / Code-Review / Dangerous-Workflows / CII-Best-Practices Axis 7 — ADR-add-without-claim PR-time block: - scripts/check-adr-claims.mjs (modified) — new ADRs must touch _claims.json or carry `<!-- no-claim-needed: <reason> -->` marker. Closes the silent-coverage-shrink failure mode (a maintainer adds a new ADR but forgets the claim entry, leaving 11/N coverage). - ADR-0059 itself uses the no-claim-needed opt-out (meta-decision, individual changes are claim-checked under their respective ADRs). Axis 6 — cron stale enforcement: - .github/workflows/smoke.yml (modified) — 6-hourly smoke now reads /api/attestation, fails when daysSinceLastGreenRun > 7. Threshold reasoning: ADR-0049 retry-contract absorbs 1-2 nights of Neon cold-start flake; 7 consecutive nights is unambiguously broken. Honest-disclose TTL on T-07 / T-08 / T-09: - docs/security/threat-model.md (modified) — each row gains a Re-evaluation date. T-07: v0.7.0 / 2026-Q3. T-08: v0.6.0 / 2026-06-30. T-09: v0.7.0 / 2026-Q3. Without TTLs, an honest-disclose can become a permanent dodge. Framework freeze at v1.0: - ADR-0059 (new) declares the framework frozen at v1.0 effective v0.5.10 ship. Future ratchet expansion requires one of: 1. Real incident (canonical: v0.5.0 → v0.5.2 schema-vs-prod) 2. External reviewer feedback (NOT self-audit-driven discovery) 3. Re-evaluation date — 2026-Q3 mandatory window (2026-09-30) - ADR-0058 § Recursive integrity meta-gap (marker ↔ live divergence) is now closed by Scorecard's Branch-Protection check. Banner + Stat sync (doc-drift-detect green): - README + portfolio-lp — ADR count 57 → 58 - portfolio-lp + interview-qa + system-overview + runbook — banner v0.5.9 → v0.5.10 - apps/collab/src/app/page.tsx Stat block — ADRs 57 → 58 - SECURITY.md — Last reviewed footer updated to 2026-04-28 (v0.5.10) Local verification: - node scripts/check-doc-drift.mjs → 0 failures, 0 warnings - node scripts/check-adr-claims.mjs → 24/24 claim(s), PR-time pass - node scripts/check-adr-refs.mjs → 55 valid IDs, 0 dangling - pnpm lint / typecheck / test (216) / build → clean Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-0060) (#49) First product-feature ship after the ADR-0059 framework v1.0 freeze. Closes T-01 honest-disclose by migrating Boardly board fanout from public Pusher channels (`board-<id>`) to auth-required private channels (`private-board-<id>`). The defence is no longer access-control-by-id-secrecy; it's a server-signed token verifying workspace membership at subscribe time. Server-side: - apps/collab/src/lib/pusher.ts — refactored: getPusherServer() exported, new helpers boardChannelName(boardId) + parseBoardChannel(name) centralise the channel-name contract. broadcastBoard() uses the helper. - apps/collab/src/app/api/pusher/auth/route.ts (new) — POST handler with four-step gate: 1. Auth.js session verified (401 if missing) 2. Form body parsed (socket_id + channel_name); 400 on malformed 3. Channel name matched against private-board-<id> allow-list (rejects every other private-* shape — not a generic Pusher signing oracle); 403 UNSUPPORTED_CHANNEL otherwise 4. Workspace-membership check via Prisma; 403 BOARD_NOT_FOUND or NOT_A_MEMBER on negative cases. 503 PUSHER_NOT_CONFIGURED on env miss (defends a misconfigured deploy from looking like auth denial) Client-side: - apps/collab/src/lib/pusher-client.ts — authEndpoint: '/api/pusher/auth' configured. Auth.js session cookie sent automatically (same-origin POST). - apps/collab/src/app/w/[slug]/b/[boardId]/BoardClient.tsx — subscribes via boardChannelName() helper instead of hardcoded `board-${boardId}`. Single contract surface across server-emit, client-subscribe, and auth-route allow-list. Tests: - apps/collab/src/lib/pusher.test.ts (new) — 8 Vitest cases pinning boardChannelName round-trip + parseBoardChannel allow-list (legacy public name rejected, unrelated private-* rejected, separator- smuggling defended, empty-id rejected). Helpers are the single contract surface for three independent files; pinning prevents silent drift. Threat-model T-01: - Status changed from "honest scope note" to "Resolved in v0.5.11 (ADR-0060)". First T-NN graduating from honest-disclose to structural closure — concrete instance of the v0.5.10 honest-disclose TTL pattern (ADR-0059) producing closure rather than perpetual dodge. - attestation script: T-01 entries removed from scope.deferred and honestScopeNotes; attestation-data.test.ts updated to assert T-01 ABSENCE (so re-introducing the public-channel scope note without re-shipping the migration would fail at PR time). Numerics ratchet (doc-drift consequence of new auth route + tests): - Vitest total 216 → 224 (174 collab + 50 knowledge); README badge URL + interview-qa + portfolio-lp + page.tsx Stat block + layout.tsx description + opengraph-image.tsx all updated - Boardly route+page count 38 → 39 (new auth route) - ADR count 58 → 59 - Banners 4 docs (portfolio-lp / interview-qa / system-overview / runbook): v0.5.10 → v0.5.11 - _claims.json: 3 ADR-0060 entries (auth route exists, private- prefix in pusher.ts, pusher.test.ts exists) Local verification: - node scripts/check-doc-drift.mjs → 0 failures, 0 warnings - node scripts/check-adr-claims.mjs → 27/27 claim(s); PR-time integrity pass (ADR-0060 has _claims.json updates so the new-ADR block fires correctly) - node scripts/check-adr-refs.mjs → 56 valid IDs, 0 dangling - pnpm lint / typecheck / test (224) / build → clean Closes: T-01 (Pusher channel eavesdropping) Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…solved (ADR-0061) (#50) Second T-NN/I-NN graduation in two ships (after ADR-0060 closing T-01). Closes the access-control half of ADR-0047 (deferred since v0.5.0 schema-partitioning, ~6 months on the books). The ADR-0059 honest-disclose TTL pattern is producing actual closures. Schema migration (additive only, 20260428_auth_tenancy): - Adds 5 tables: User / Account / Session / VerificationToken (Auth.js v5 standard) + Membership (user × workspace × role). - Workspace gains members Membership[] relation. - Seeded wks_default_v050 demo workspace untouched. No column changes; no existing-row mutations. Auth.js v5 setup: - apps/knowledge/src/auth/{config,index}.ts (new) — NextAuthConfig with Google + GitHub OAuth, JWT session strategy, PrismaAdapter. - apps/knowledge/src/app/api/auth/[...nextauth]/route.ts (new) — catch-all handlers. - apps/knowledge/src/app/signin/page.tsx (new) — minimal signin UI. Two-shape access layer (preserves live RAG demo brand): - apps/knowledge/src/auth/access.ts (new): requireDemoOrMember (read paths) — demo workspace anonymously readable via allow-list; non-demo requires Membership row. requireMemberForWrite (write paths) — always requires session, even for demo workspace. Auto-grants OWNER on demo signin (intentional sandbox sharing). Closes anonymous-write cost-attack vector named in ADR-0046. - /api/kb/ask wired with requireDemoOrMember - /api/kb/ingest wired with requireMemberForWrite - 15 Vitest cases pinning the access matrix (read/write × demo/non-demo × authed/anonymous × member/non-member) Schema canary (axis 2) extension: - /api/health/schema EXPECTED constant adds 5 new tables; the expected.test.ts cross-checks both directions so a column drop without an EXPECTED update fails CI. Threat-model + ADR-0047 closure: - I-01 status: "single-tenant honest scope note" → "Resolved in v0.5.12 (ADR-0061)". - ADR-0047 § Status: Partially Accepted → Fully Accepted. - attestation script: Auth-gated Knowlex removed from scope.deferred; I-01 removed from honestScopeNotes; attestation-data.test.ts tightened to assert both T-01 and I-01 ABSENT (re-introduction of either disclosure without re-shipping the migration would fail at PR time). Numerics ratchet: - ADR count 59 → 60 - Vitest 224 → 239 (174 collab + 65 knowledge, +15 access.test.ts) - Banner v0.5.11 → v0.5.12 across 4 docs Live activation prerequisites (post-merge, knowlex Vercel project): - AUTH_SECRET (openssl rand -base64 32) - GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET - GITHUB_CLIENT_ID + GITHUB_CLIENT_SECRET Until configured: demo /api/kb/ask continues to work (no session lookup); ingest + non-demo paths return 500. Honest-disclosed in ADR-0061 § Negative. Local verification: - node scripts/check-doc-drift.mjs → 0 failures - node scripts/check-adr-claims.mjs → 37/37 (was 27 + 10 ADR-0061 entries); PR-time integrity pass - node scripts/check-adr-refs.mjs → 57 valid IDs, 0 dangling - pnpm lint → 0 errors; typecheck clean; test 239 passed; build clean Closes: I-01 (Cross-tenant read; Knowlex single-tenant honest scope) Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…paraphrase-fragility (ADR-0062) (#51) Third graduation in three ships (after T-01 closure / ADR-0060 and I-01 closure / ADR-0061). The named-but-deferred fix from ADR-0049 § 8th arc for substring-OR scoring paraphrase fragility ships as an opt-in faithfulness rubric pass. The honest-disclose TTL discipline (ADR-0059) is consistently producing closures, three for three. Module split: - apps/knowledge/src/lib/judge-rubric.ts (new) — pure module: buildJudgePrompt + parseJudgeResponse + aggregateJudgeScores + RUBRIC_MIN/MAX + DEFAULT_JUDGE_MODEL constants. No Node-runtime entry; lives in src/lib/ so the existing vitest config glob discovers the test file. - apps/knowledge/src/lib/judge-rubric.test.ts (new) — 17 Vitest cases pinning prompt construction, response parsing (clean JSON / quoted score / code-fenced / trailing prose / unparseable / out-of-range / missing reasoning / full RUBRIC_MIN..MAX range), aggregate (mean over availables / null exclusion / empty / all-null), and the DEFAULT_JUDGE_MODEL = "gemini-2.5-pro" invariant. - apps/knowledge/scripts/eval.ts — wires --judge CLI flag + EVAL_JUDGE=1 env toggle + per-question judgeAnswer call + aggregate (judge.meanScore / available / total) into report JSON. Toggles (equivalent paths): node --import tsx scripts/eval.ts --judge EVAL_JUDGE=1 node --import tsx scripts/eval.ts EVAL_JUDGE_MODEL=<model-id> ... (advanced operator override) Rubric (integer 0..3, not Likert / not prose): 3 = correct, fully grounded 2 = correct but partial 1 = partially wrong 0 = wrong / hallucinated / refuses Output: {"score": N, "reasoning": "<one sentence>"}. Parser tolerates code-fenced / prose-trailed / quoted-integer responses; non-fatal parse failures yield score:null (separate aggregate bucket — judge unavailable doesn't silently penalise the model). ADR-0046 free-tier compliance preserved: - Default off; nightly cron continues substring-OR scoring at $0/mo - gemini-2.5-pro on AI Studio Free tier at 5 RPM / 25 RPD = sufficient for one full --judge run per day - Opt-in via workflow_dispatch / weekly cron / on-demand - No new SDK; no Vertex AI billable surface Aggregation honesty: - judge.available + judge.total exposed so a reviewer can distinguish "model is bad" from "judge call kept failing" - Mean computed over available scores only (nulls excluded from denominator) - Pass/fail threshold for judge mean DEFERRED to a future ratchet after 3-5 weekly runs calibrate the steady-state mean — v0.5.13 reports the mean as advisory only Numerics ratchet: - ADR count 60 → 61 - Vitest 239 → 256 (174 collab + 82 knowledge, +17 from judge-rubric.test.ts) - Banner v0.5.12 → v0.5.13 across 4 docs (portfolio-lp / interview-qa / system-overview / runbook) - _claims.json: 5 ADR-0062 entries (judge-rubric module exists, judge-rubric.test.ts exists, default model is gemini-2.5-pro, eval.ts honors EVAL_JUDGE env, eval.ts imports judge-rubric) Local verification: - node scripts/check-doc-drift.mjs → 0 failures, 0 warnings - node scripts/check-adr-claims.mjs → 42/42 (was 37 + 5 ADR-0062) - node scripts/check-adr-refs.mjs → 58 valid IDs, 0 dangling - pnpm lint → 0 errors; typecheck clean; test 256 passed; build clean Closes: ADR-0049 § 8th arc Action item (2) — LLM-as-judge --judge flag Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ia RRF (ADR-0063, closes ADR-0011) (#52) Fourth graduation in four ships (after T-01 / I-01 / ADR-0049 § 8th arc closures in v0.5.11 / v0.5.12 / v0.5.13). The largest deferred ADR-0039 item — ADR-0011's hybrid retrieval plan — ships as a complement to v0.5.13's --judge mode: hybrid retrieval fixes lexical recall on keyword-heavy queries (proper nouns / API names / error codes); --judge fixes scoring on paraphrase-heavy queries. Schema migration (additive only, 20260428_chunk_fts): - Generated tsvector column Chunk.tsv = to_tsvector('english', content), STORED, maintained by Postgres on every insert/update. - GIN index Chunk_tsv_gin_idx for sub-millisecond @@ lookups. - Storage cost ~150-300 bytes per 512-char chunk; negligible at portfolio scale. Lexical retrieval: - plainto_tsquery('english', $query) — natural-language tokenization + stop-word removal. - ts_rank_cd (cover-density rank) over plain ts_rank — closer to BM25's proximity component. - Same workspace pre-filter shape as the existing pgvector path so ADR-0061 access layer holds. RRF fusion module (apps/knowledge/src/server/rrf.ts, new): - Reciprocal Rank Fusion at the application layer: 1/(k+rank+1) contribution per list with weight + custom-k support. - RRF_K = 60 per Cormack et al. (2009) canonical default. - Per-source rank provenance for debug. - 9 Vitest cases (rrf.test.ts) pin fusion invariants: rank preservation / symmetric merge / two-list dominance / per-source provenance / weight bias / limit / custom k / empty input / id collision. retrieve.ts wiring: - HYBRID_RETRIEVAL_ENABLED=1 env flag (default off for v0.5.13 baseline preservation + run-to-run eval comparability). - Hybrid path: both lists return up to 2K candidates; fuseRRF combines; top-K materialised back from union; vector row preferred for the cosine distance, lexical row falls back. - RetrievedChunk.hybridSources?: Record<string, number> for per-source rank provenance. Schema canary EXPECTED.Chunk extended with the tsv column (ADR-0057 axis 2 catches a stale build that didn't run the migration). ADR-0011 status: Accepted (planned, deferred) → Fully Accepted (hybrid + RRF shipped via ADR-0063; Cohere Rerank explicitly remaining deferred — billable API key would break ADR-0046). Numerics ratchet: - ADR count 61 → 62 - Vitest 256 → 265 (174 collab + 91 knowledge, +9 from rrf.test.ts) - Banner v0.5.13 → v0.5.14 across 4 docs - _claims.json: 6 ADR-0063 entries (rrf module exists, rrf.test.ts exists, retrieve.ts honors HYBRID_RETRIEVAL_ENABLED env, lexical uses plainto_tsquery, schema canary covers tsv, FTS migration shipped) Local verification: - node scripts/check-doc-drift.mjs → 0 failures, 0 warnings - node scripts/check-adr-claims.mjs → 48/48 (was 42 + 6 ADR-0063) - node scripts/check-adr-refs.mjs → 59 valid IDs, 0 dangling - pnpm lint → 0 errors; typecheck clean; test 265 passed; build clean Closes: ADR-0011 hybrid retrieval (Cohere Rerank still deferred) Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…(ADR-0064) (#53) Calibration attempt for v0.5.14 hybrid retrieval (ADR-0063) surfaced an architectural gap rather than producing a numerical lift figure: post-v0.5.12 multi-tenant transition (ADR-0061 line 52) intentionally omits the CI Credentials provider for Knowlex, so the unauthenticated eval client cannot ingest fresh corpus on a post-v0.5.12 server (returns 401 UNAUTHENTICATED). Per ADR-0059 § 3-trigger ratchet rule (incident / external feedback / 2026-Q3 re-audit window), implementing the bypass mechanism in this same ratchet would be the self-audit-loop trap. Disclose with TTL + accelerator triggers + named closure path (next-available-NNNN follow-up that ships the CI Credentials provider for Knowlex by copying the apps/collab triple-gate pattern, producing the lift figure as a byproduct). 5th graduation cycle seed established: T-01 (v0.5.11) → I-01 (v0.5.12) → ADR-0049 § 8th arc (v0.5.13) → ADR-0011 (v0.5.14) → calibration-attempt (this ADR) is now in queue for the 5th closure. Companion updates: - ADR-0011 § Implementation status: v0.5.15-rc.0 calibration-status note - ADR-0063 § Implementation status: calibration-blocked-pending note + 401-on-post-v0.5.12 caveat against Live exercise command - _claims.json: 4 ADR-0064 entries (anchor strings in ADR-0061 / eval.ts / ADR-0011 / ADR-0063) - README + portfolio-lp + page.tsx Stat: ADR count 62 → 63 - CHANGELOG: [0.5.15-rc.0] entry Tag drift cleanup bundled: created and pushed annotated tags v0.5.9 .. v0.5.14 retroactively (the 6-ship S265 arc shipped via PR-merge only with no git tag push between ships). git ls-remote --tags origin now shows v0.5.0 through v0.5.14 reachable. On-disk attack-surface reduction: cleared the GEMINI_API_KEY value from apps/knowledge/.env (gitignore'd, was never committed). Ephemeral local calibration container (docker pgvector/pgvector:pg16) and migrator Postgres role go away with container teardown. Verification: - node scripts/check-doc-drift.mjs → 0 failure (ADR 63, Vitest 265) - node scripts/check-adr-claims.mjs → 52/52 pass, 0 failure - node scripts/check-adr-refs.mjs → 0 dangling Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…i Free tier revocation incident response (ADR-0067) (#54) * feat(v0.5.15): CI Credentials provider for Knowlex (ADR-0065) + Gemini Free tier revocation incident response (ADR-0067) Two ADRs ship in this single ratchet: 1. ADR-0065 — CI Credentials provider for Knowlex, mirroring apps/collab ADR-0038 triple-gate (VERCEL!=1 + E2E_ENABLED=1 + E2E_SHARED_SECRET). End-to-end signin verified during this ratchet. Closes the architectural-gap half of ADR-0064. Exports e2eGateOpen predicate for unit testing (9 Vitest cases pinning gate semantics + email allowlist). Build-time assertion in next.config.ts as additional defense layer (VERCEL=1 + E2E_ENABLED=1 → throw on next build). Auto-upsert E2E user in authorize() callback (Knowlex has no prisma seed.ts; idempotent + triple-gated upstream so unreachable on prod). 2. ADR-0067 — 2026-04-29 production incident report. Google AI Studio silently revoked Free tier access at the account level for the leagames0221@gmail.com account (Billing Tier "Free tier" → "Unavailable" within 24h, no email/banner/notification per Google abuse-detection policy). Both craftstack-knowlex (origin) and a freshly-created craftstack-knowlex-v2 inherit the revoked state, confirming account-level enforcement. Diagnostic probes (Vercel function logs, AI Studio Project listing, AI Studio Usage page) ruled out cumulative-account-history but cannot differentiate among policy sweep / multi-geo IP fingerprint / content-safety filter cascade as the actual trigger — Google's standard policy is to not disclose. Recovery design must therefore be resilient to "any free-tier provider can revoke at any time without explanation" rather than fix a specific identified trigger. Containment: ADR-0046 EMERGENCY_STOP kill-switch on the Knowlex Vercel project Environment Variables → /api/kb/{ask,ingest} return 503 EMERGENCY_STOPPED instead of cascading 500s. Runbook in ADR-0067 § Decision item 1. Calibration scope pivot: ADR-0064's lift-figure half cannot ship in this ratchet because the eval flow needs a working LLM key. Instead of bundling alt-LLM provider migration into the same ratchet (ADR-0059 § 3-trigger ratchet rule + scope discipline), the calibration is reframed as BYOK-reproducible: any operator with a Gemini-compatible (or 768-dim alternative such as Cloudflare Workers AI bge-base-en-v1.5) API key can run `pnpm --filter knowledge eval` locally and produce the lift figure. README's new "Run Knowlex locally with your own API key (BYOK)" section documents the 5-line setup. Recovery ratchet (alt-LLM provider migration) is named in ADR-0067 § Decision item 3 as needs-driven optional follow-up — not committed. 5th graduation cycle structure preserved: ADR-0064 disclose → ADR-0065 architectural-gap closure → ADR-0067 incident response → BYOK landing as the closure path. Companion doc updates: - ADR-0064 § Status: architectural-gap half closed by ADR-0065; lift-figure half BYOK-reproducible per ADR-0067. - docs/adr/README.md: index entries for ADR-0065 + ADR-0067. - docs/adr/_claims.json: 9 new entries (ADR-0065 ×5 + ADR-0067 ×4). - README.md: Live demo section pivoted to EMERGENCY_STOPPED notice + BYOK runbook + ADR-0067 link; ADR count 63→65; Vitest 265→274; tests badge 265+24 → 274+24. - docs/hiring/portfolio-lp.md + docs/hiring/interview-qa.md + docs/architecture/system-overview.md + docs/ops/runbook.md: status banner v0.5.14 → v0.5.15 + Knowlex EMERGENCY_STOP note. - apps/collab/src/app/page.tsx + layout.tsx + opengraph-image.tsx: Stat row + metadata + OG image numerics 265→274; ADR 63→65. - apps/knowledge/.env: GEMINI_API_KEY value cleared post-incident (gitignore'd, never committed). Numerics: - ADR count 63 → 65 - Vitest 265 → 274 (174 collab + 100 knowledge; +9 from apps/knowledge/src/auth/config.test.ts) - Banner version v0.5.14 → v0.5.15 Verification: - node scripts/check-doc-drift.mjs → 0 failure - node scripts/check-adr-claims.mjs → 61/61 pass - node scripts/check-adr-refs.mjs → 0 dangling - pnpm --filter knowledge test → 100 passed (was 91) User-side production action required for full ratchet release: - Vercel project Knowlex Environment Variables → add EMERGENCY_STOP=1 (Production + Preview) and Redeploy. ADR-0067 § Decision item 1 documents the runbook. - Verify post-redeploy: curl /api/kb/ask returns 503 with {"code":"EMERGENCY_STOPPED"} instead of cascading 500s. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(v0.5.15): widen e2eGateOpen param type to satisfy strict ProcessEnv The CI typecheck (`tsc --noEmit` on apps/knowledge) failed because recent @types/node versions added `NODE_ENV` as a required field on the global `NodeJS.ProcessEnv` interface. The `e2eGateOpen(env)` predicate only reads `VERCEL` / `E2E_ENABLED` / `E2E_SHARED_SECRET`; tests passed narrow env shapes which no longer satisfied the global ProcessEnv contract. Fix: widen the parameter type to `Record<string, string | undefined>`. The runtime contract is unchanged (process.env still works as the default); only the test-callable surface relaxes. Comment in the function explains the rationale. Verification: - pnpm --filter knowledge typecheck → clean - pnpm --filter knowledge test → 100 passed (no regression) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Docs-only ratchet aligning portfolio surface signal (30-second-probe reviewer view of README + portfolio-lp top-of-page) with depth signal (deep reviewer of ADR sequence + repo structure). Three Senior-tier dimensions previously inferred-only become explicit prose claims with structural evidence pointers. Refinements: - Refine 1: README BYOK runbook claim "Five steps, ~2 minutes" → "5-step BYOK runbook (~5 min total)" with per-step time estimate. Honest accuracy over aspirational under-count. - Refine 2: Knowlex live-demo URL annotated with front-loaded BYOK link so a 30-second probe reviewer sees the BYOK option without scrolling. EMERGENCY_STOPPED state stays explicit. - Refine 3: README + portfolio-lp gain "Built with AI pair-programming (Claude Code)" narrative. 46 of 187 commits carry Co-Authored-By: Claude Opus 4.7 (1M context) marking — explicit claim with structural evidence, not inference. Brand-surface front-loading: - Surface 1: README + portfolio-lp gain "5 closed graduation cycles in 5 ships" callout enumerating T-01 / I-01 / ADR-0049 § 8th arc / ADR-0011 / ADR-0064 with closure-ADR links. The graduation cycle pattern (KL-build_ci-202604-graduation-cycle) is the portfolio's brand-defining engineering-culture artifact; surfacing it explicitly converts depth-only signal to also-30-second-reader signal. - Surface 2: README + portfolio-lp gain "Real production incident response record (2026-04-29)" callout. ADR-0067 documents the Gemini Free tier revocation + ADR-0046 EMERGENCY_STOP containment + BYOK pivot. Senior+ reviewers always ask "have you handled real production incidents?"; this answers with structural evidence. - Surface 3: README gains "Framework is structurally enforced, not declared" callout. ADR-0058 branch-protection ruleset rejected the author's own `git push origin main` attempts during ratchets S266 + S267 (PR #53 + #54 commit history). Framework foundation axiom operating live. Status banner sync: docs/hiring/portfolio-lp.md + interview-qa.md + docs/architecture/system-overview.md + docs/ops/runbook.md advance v0.5.15 → v0.5.16. Why this is docs-only, not feature ratchet: - No code changes. No new tests, no new ADRs. - ADR-0059 § 3-trigger ratchet rule preserved: this ratchet is external-feedback-shaped (user surfaced the surface-vs-depth gap and asked whether refinement was worth doing). Not self-audit-loop. - Scope deliberately bounded to ~1 page of prose across 6 files. Numerics (unchanged): - ADR count remains 65 (no new ADRs). - Vitest remains 274 (no test changes). - Banner version v0.5.15 → v0.5.16. Verification: - node scripts/check-doc-drift.mjs → 0 failure - node scripts/check-adr-claims.mjs → 61/61 pass - node scripts/check-adr-refs.mjs → 0 dangling Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tag drift fix (#56) External-feedback-shaped: explicit critical-issue scan surfaced 3 classes of drift in v0.5.16 portfolio prose against the actual ADR sequence. Class 1: Single-tenant / "deferred to v0.5.4" stale claims - ADR-0061 v0.5.12 shipped multi-tenant access control (Auth.js + Membership + demo allow-list); the following stale claims contradicting that closure were updated: - README.md:135 Apps table Knowlex row → Multi-tenant + ADR-0061 + ADR-0063 + ADR-0065 references; Gemini 2.0 → 2.5 Flash - docs/hiring/portfolio-lp.md:26 header → Multi-tenant RAG header - docs/hiring/portfolio-lp.md:28 body → ADR-0061/0063/0065/0067 inline - docs/hiring/portfolio-lp.md:11 status block → multi-tenant inline - docs/hiring/interview-qa.md Q5/Q9/Q11/Q23 → contextually updated - docs/architecture/system-overview.md:60 Auth row → multi-tenant - docs/architecture/system-overview.md:78 request-path → multi-tenant - docs/architecture/system-overview.md:90 RLS deferred reasoning → cite ADR-0061 application-side enforcement choice Class 2: Gemini model version sync (2.0 Flash → 2.5 Flash) - README.md + portfolio-lp + interview-qa + system-overview + runbook all references advanced to Gemini 2.5 Flash. ADR-0062 + ADR-0063 already cite Gemini 2.5 series; the prose simply lagged. Class 3: Git tag drift cleanup - v0.5.15 + v0.5.16 squash-merged via PRs #54 + #55 but no git tag step in the chain. Retroactively created annotated tags: - v0.5.15 → 6573391 (ADR-0065 + ADR-0067 ship) - v0.5.16 → 9aa8bd4 (narrative alignment) - Pushed to origin in this ratchet so reviewer sees v0.5.0..v0.5.16 continuously. Why docs-only: - No code changes. No new tests, no new ADRs. - ADR-0059 § 3-trigger ratchet rule preserved: external-feedback-shaped (critical-issue scan surfaced specific drift instances). Numerics: ADR count 65 unchanged, Vitest 274 unchanged, banner v0.5.16 → v0.5.17 across portfolio-lp + interview-qa + system-overview + runbook. Verification: - node scripts/check-doc-drift.mjs → 0 failure - node scripts/check-adr-claims.mjs → 61/61 pass - node scripts/check-adr-refs.mjs → 0 dangling Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xivity (ADR-0068) (#57) * feat(v0.5.18): Run #5 hiring-sim findings closure + attestation reflexivity (ADR-0068) Hiring-sim Run #5 (methodology v2 against v0.5.17 / 7a93898) returned `hire`, NOT the expected `strong hire`. Three drift findings surfaced; this ratchet closes them structurally + records the methodology v3 candidate. Finding A — auto-attestation reflexivity (load-bearing): `/api/attestation.scope.deferred[]` listed Hybrid search with adr=ADR-0011 while ADR-0011 Status reads "Fully Accepted ... shipped in v0.5.14". The endpoint built to expose audit-survivable truth was lying about its own ADR. Closed via: - Removed Hybrid from scope.deferred[] (shipped per ADR-0063) - Added scope.shippedFlagGated[] section (records original adr + closingAdr + shippedIn + flag + flagDefault) - Updated Cohere Rerank reason to ADR-0046 (independent of v0.5.14 ship) - Updated PostgreSQL RLS reason to ADR-0061 multi-tenant (replacing stale "Knowlex is single-tenant per ADR-0039") - New vitest reflexivity assertion: scope.deferred[] entries cannot have ADR Status = Fully Accepted unless feature is explicitly carved out - New shippedFlagGated[] schema test - system-overview.md "what is NOT in this diagram" hybrid bullet now describes shipped + flag-gated default-off rather than deferred Finding B — methodology v3 candidate (no portfolio change): Simulator's grep-based Vitest count (`grep -hcE '^\s*(test|it)\('`) returned 258, reporting drift vs README's 274. Actual count via `pnpm exec vitest run --reporter=json` = 174 collab + 100 knowledge = 274. README correct; grep undercounts by missing test.each([...]) row-multiplied cases. Existing check-doc-drift.mjs already uses vitest's actual count via vitestCount(app) — gate would have caught real drift. Methodology hole, not portfolio bug. Recorded for v3 in ADR-0068 § Decision item B + postmortem doc 64. Finding C — README-vs-CSP coherence gate: Live script-src includes 'unsafe-inline' AND 'unsafe-eval', but README:175 only mentioned 'unsafe-inline'. (ADR-0040 itself was already correct.) Closed: - README:175 disclosed both directives + rationale - next.config.ts comment block enumerates both + references new gate - New scripts/check-csp-coherence.mjs PR-blocking forward gate: every load-bearing CSP directive ('unsafe-inline', 'unsafe-eval', 'strict-dynamic', 'wasm-unsafe-eval') in next.config.ts must appear in README "Security headers" bullet - Wired into ci.yml doc-drift-detect job Meta-finding (Decision item D): "the framework missed live drift in its own attestation endpoint" — ADR-0057's 13-axis framework gains a 14th: framework-as-its-own-substrate. Vitest reflexivity + new CSP gate are the structural mechanism. Net delta: - ADR count 65 → 66 (ADR-0068 added) - Vitest count 274 → 276 (2 new reflexivity tests in attestation-data.test.ts) - Knowledge subtotal 100 → 102 - Version banners v0.5.17 → v0.5.18 across 5 docs - attestation-data.json now references tag=v0.5.18 commit=7a93898a adr=66 - CHANGELOG + ADR-README index updated Verification (all 5 PR-time gates green): - check-doc-drift.mjs: 0 failure(s), 0 warning(s) ✓ - check-adr-refs.mjs: 63 valid IDs, 0 dangling ✓ - check-adr-claims.mjs: 61/61 claim(s), 0 failure(s) ✓ - check-csp-coherence.mjs: 0 failure(s), 2 directives checked ✓ - check-free-tier-compliance.mjs: passed ✓ - vitest attestation-data.test.ts: 7/7 pass (5 existing + 2 new reflexivity) Run #6 against v0.5.18 with v2 methodology expected to clear `strong hire` (Findings A + C closed structurally; Finding B was methodology bug, not portfolio bug). Closes the gap between simulator's "real senior-tier portfolio" closing line and the verdict-rule's `strong hire`. Refs ADR-0068, ADR-0054, ADR-0056, ADR-0057, ADR-0040, ADR-0011, ADR-0061. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(v0.5.18): add ADR-0068 claim entries to _claims.json 5 new claim entries close the PR-time integrity check that requires new ADRs to touch _claims.json: 1. CSP coherence gate exists (scripts/check-csp-coherence.mjs) 2. CSP coherence gate is wired into ci.yml doc-drift-detect job 3. attestation-data scope.deferred[] reflexivity assertion exists in vitest 4. attestation-data.json scope.shippedFlagGated section is generated 5. README discloses 'unsafe-eval' alongside 'unsafe-inline' (Finding C) ADR-claim count 61 → 66; ADR-claim summary now 66/66 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rface coverage extension (ADR-0069) (#58) Hiring-sim Run #6 (methodology v2 against v0.5.18 / 3cbdb83) returned `hire`, NOT `strong hire`. Run #6 confirmed the ADR-0068 closure shipped (Stage 3 row 12: scope.shippedFlagGated[0] = Hybrid retrieval ✓), but surfaced a NEW drift class: visible deploy front-door surfaces carried "Gemini 2.0 Flash" / "text-embedding-004" while apps/knowledge/src/lib/gemini.ts exports gemini-2.5-flash + gemini-embedding-001. Brand-foundation drift on the most visible portfolio surface. 8 findings (D1-D8); this ratchet closes 6 structurally + defers 2. Finding D1 + D8 — Deploy-visible-surface coverage extension (load-bearing): - 17 deploy-visible surface files synced to canonical models: apps/collab/src/app/{page,layout,opengraph-image,status/page, playground/page,playground/PlaygroundClient}.tsx, kb-demo.ts, humans.txt, openapi{,-types}.ts; apps/knowledge/src/app/{page,kb/page}.tsx, openapi.ts, api/kb/ask/route.ts, README.md, prompts/registry.json + 6 *.md; docs/architecture/system-overview.md - Also closed v0.5.17-cleanup-leak in apps/collab/src/app/page.tsx:84-88 (stale "single-tenant / deferred to v0.5.4" → multi-tenant per ADR-0061) - Sub-app README updated for stale "single-corpus, tenantless / Pure cosine kNN" claims (post-v0.5.12-v0.5.14 actuality: multi-tenant + hybrid + EMERGENCY_STOPPED) - New scripts/check-doc-drift.mjs axis: "Visible-deploy-surface model name coherence (ADR-0069)" — enumerates 17 surfaces + asserts none contain stale model patterns (Gemini 2\.0 Flash, gemini-2\.0-flash, text-embedding-004). Canonical truth from apps/knowledge/src/lib/gemini.ts - Allowed exceptions documented (gemini.ts migration narrative, chunking.ts historical tuning, eval.ts historical comment, migration.sql snapshot, ADR-0067 incident report, CHANGELOG entries) Finding D2 — ADR-0068 self-correction (drift inside drift-closure ADR): ADR-0068 line 13 had "174 + 100 = 274" while post-v0.5.18 reality became 174+102=276 (the 2 reflexivity tests added in ADR-0068 itself shifted knowledge subtotal during the same ratchet). Self-correction note added to ADR-0068 line referencing this ADR-0069 § D2. Finding D3 + D4 — ADR Status field drift: - ADR-0010 (RLS) Status: Accepted → "**Partially superseded — RLS deferred**" per ADR-0061 multi-tenant transition - ADR-0003 (DB session) Status: Accepted → "**Superseded by JWT strategy in practice**" with cross-ref to README ADR-index Supersession notice - Both index rows in docs/adr/README.md updated Finding D6 — ADR sequence gap notice: docs/adr/README.md gains explicit notice that ADR-0055 + ADR-0066 are intentionally unused / reserved (0055 = withdrawn during v0.5.10 framework freeze; 0066 = reserved per ADR-0067 § Decision item 3 for alt-LLM provider migration recovery) Finding D5 + D7 — Deferred: - D5 (cronHealthHint × EMERGENCY_STOP cross-ref) requires runtime logic refinement; recorded as next-available-NNNN follow-up - D7 (commit count drift 187→190) auto-resolves via post-merge propagation; intentionally a snapshot, not continuously-updated Methodology v3 sub-axis added: ADR-0069 § Decision item E adds "Stage 2.7 — Deploy-visible-surface coverage parity" to the v3 methodology candidate (already extended by ADR-0068 § Decision item B). Net delta: - ADR count 66 → 67 (ADR-0069 added) - ADR-claim count 66 → 72 (6 new ADR-0069 claim entries) - Vitest count 276 unchanged (no new tests; ADR-0068 self-correction is text-only) - Version banners v0.5.18 → v0.5.19 across 5 docs - attestation-data.json regenerated: tag=v0.5.19, adr=67 Verification (all 5 PR-time gates green): - check-doc-drift.mjs: 0 failure(s) ✓ (incl. new model coherence axis) - check-csp-coherence.mjs: 0 failure(s) ✓ - check-adr-refs.mjs: 64 valid IDs, 0 dangling ✓ - check-adr-claims.mjs: 72/72 ✓ - check-free-tier-compliance.mjs: passed ✓ Run #7 against v0.5.19 expected to clear `strong hire` (front-door deploy drift class structurally pinned, brand-reflexivity multiplier trigger removed). Refs ADR-0069, ADR-0068, ADR-0054, ADR-0057, ADR-0010, ADR-0003, ADR-0061, ADR-0067. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Existing ci.yml runs `pnpm audit --audit-level moderate || true` (intentionally non-blocking for general moderate notices). This new workflow runs `pnpm audit --audit-level=high` and **fails** on high+ severity, providing a stricter gate for supply-chain dependency hygiene. Triggered on push, PR, weekly cron, manual dispatch. Uses corepack-resolved pnpm (matches package.json packageManager). Co-authored-by: leagames0221-sys <leagames0221-sys@users.noreply.github.com>
LabelsThe following labels could not be found: Please fix the above issues or remove invalid values from |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…des (fast-uri, hono) (#75) Resolves pnpm audit failures on main (2/2 FAIL → expected GREEN after merge). ## Vulnerabilities closed (audit literal verify) Before: 22 vulns (3 low + 10 moderate + 9 high) After: 2 vulns (0 low + 2 moderate + 0 high) → `pnpm audit --audit-level=high` exit 0 ### High severity (7) — all in next.js 16.2.4 - GHSA-8h8q-6873-q5fj: DoS via Server Components (patch ≥16.2.5) - GHSA-26hh-7cqf-hhc6: Middleware/Proxy bypass via segment-prefetch (patch ≥16.2.6) - GHSA-mg66-mrh9-m8jx: DoS via connection exhaustion in Cache Components (patch ≥16.2.5) - GHSA-c4j6-fc7j-m34r: SSRF via WebSocket upgrades (patch ≥16.2.5) - GHSA-492v-c6pp-mqqv: Middleware/Proxy bypass via dynamic route param injection (patch ≥16.2.5) - GHSA-267c-6grr-h53f: Middleware/Proxy bypass in App Router segment-prefetch (patch ≥16.2.5) - GHSA-36qx-fr4f-26g5: Middleware/Proxy bypass in Pages Router using i18n (patch ≥16.2.5) → Bump next from 16.2.4 → 16.2.6 in apps/collab + apps/knowledge package.json (covers all 7) ### Transitive deps (fast-uri, hono) — pnpm overrides - fast-uri: high × 2 path-traversal + host-confusion (patch ≥3.1.2) - hono: 3 vulns in Prisma dev tooling (patch ≥4.12.18) → Add to root package.json `pnpm.overrides` (transitive deps, can't update via direct dep) ## Local verify PASSED (D-WRANGLER-LOCAL-FIRST 一般化、 pre-push verification) - `pnpm install` → lock regenerated, 0 errors - `pnpm lint` → 0 errors (4 pre-existing warnings unrelated to this change) - `pnpm typecheck` → 0 errors - `pnpm test` → 174 / 174 PASS (26 test files) - `pnpm audit --audit-level=high` → exit 0 ## Remaining moderate (2) Will be tracked separately; not blocking CI (pnpm-audit gate is `--audit-level=high`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [zod](https://github.com/colinhacks/zod) from 4.3.6 to 4.4.3. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](colinhacks/zod@v4.3.6...v4.4.3) --- updated-dependencies: - dependency-name: zod dependency-version: 4.4.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
ef32915 to
5e95ceb
Compare
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
Bumps zod from 4.3.6 to 4.4.3.
Release notes
Sourced from zod's releases.
... (truncated)
Commits
1fb56a5docs: document release procedure in AGENTS.mdf3c9ec04.4.3c2be4f8fix(v4): generalize optin/fallback to transform; restore preprocess on absent...1cab693fix(v4): restore catch handling for absent object keys (#5937) (#5939)b8dffe9docs: remove Numeric and Speakeasy (2+ missed monthly cycles)9195250docs: remove Mintlify from bronze sponsors (churned)2c70332docs: normalize bronze sponsor logos to github avatar pattern7391be8docs: prune lapsed silver/bronze sponsors and add active ones2aeec83docs: prune lapsed gold sponsors and rebalance logo sizing4c2fa95docs: use Zernio primary wordmark for gold sponsor logoMaintainer changes
This version was pushed to npm by GitHub Actions, a new releaser for zod since your current version.