Repository navigation
refactor: lay the v2 workspace foundation and add the forges boundary - #71
Conversation
Diagnoses the current dashboard, auth, source-control and deployment problems, proposes the supastarter-style workspace layout, and records the maintainer's answers on layout, ESM, licensing, billing, forges and deployment targets.
Pins Better Auth, nuxt-better-auth, Vue, UnoCSS, Vitest, Vue testing, Postgres/Drizzle, Stripe, taste, modern CSS, code review, GitHub Actions, Dockerfile, Cloudflare and anti-slop skills to their source commits.
agent, runner, models, config and shared now live in packages/core/*. Their npm names are unchanged, so imports stay identical; only relative tooling paths, TypeScript project references, the workspace globs and the documentation that names their directories change.
The deployable app becomes @code-zero/web in apps/web, matching the v2 layout. Paths, the Turborepo task key, the Dockerfile, the CD workflow, the Vercel build command, i18n tooling globs and the docs follow. The September dashboard plan moves to docs/plans unchanged.
The shared tsdown configuration drops the CommonJS output and validates with attw's esm-only profile. Package exports keep a single types and default condition. forges, the next source-control client, ships no CJS build, and every consumer is a Node 24 process or a Vite/Nitro build.
packages/ui owns the colour tokens, focus and accessibility media queries (tokens.css), the brand theme, the RTL and a11y dev presets and a defineCodeZeroUno factory. apps/web and apps/marketing drop their copies and keep only their density shortcuts and page backgrounds. DESIGN.md records the rules a UI change must respect. The marketing site renders identically in both schemes; the dashboard gains the reduced-motion rule the marketing site already had.
Four edges in the better-auth@1.6.26 subgraph named peer-qualified ids truncated one level (prisma@7.9.1 instead of prisma@7.9.1(better-sqlite3@12.11.1)), and one unctx edge named an unplugin variant with no snapshot. aube hashes the id as written, so a clean aube ci linked node_modules/.aube/@better-auth+drizzle-adapter* /node_modules/drizzle-orm (and unctx's unplugin) at virtual-store directories it never creates, and the web Nitro build failed with ENOENT. Repointed the five edges at the snapshots already in the lockfile. A clean aube ci now reproduces the file byte for byte and leaves no dangling links.
@nuxt/test-utils boots the whole app in a beforeAll hook before the first spec of each worker. On a cold cache or a loaded machine that boot outlasts Vitest's default 10 s hook timeout and fails whole files before any assertion runs. Raise hookTimeout to 60 s for the nuxt projects of apps/web and apps/marketing.
packages/forge (@code-zero/forge) builds the source-control boundary on the forges library for GitHub, GitLab, Gitea, Forgejo, Bitbucket Cloud and Azure DevOps: connections and a registry, FORGES_* environment parsing, signed webhook ingestion into review and issue events, open change-request discovery, status publication with explicit degradation, progress, evidence and issue-validation comments, and issue-to-PR. Every delivery is untrusted: bad signatures and malformed payloads are typed rejections, library errors are redacted against the connection's credentials, and issue events start nothing unless policy opts in. Tests run against forges' in-memory fake and signed fixtures, with no network. Nothing imports the package yet; packages/source-control stays until packages/api and apps/web are rewired.
Architecture Inside the changed components — 3 viewsComponent view — Web App Container Internal modules and server route handlers of the renamed apps/web application. Component view — Core Runtime Packages Execution boundary and state engine modules consolidated under packages/core/*. Component view — Multi-Forge Layer The new packages/forge abstraction integrating multi-provider SCM interactions via the forges library. Data flow Review ✨ Nothing flagged · reviewed Tip The diagrams are links. Click one to open it on the canvas, then press W or click play to walk through the change 🪧 More tips
Thanks for using PR Lens! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out on X, Bluesky, Mastodon, Reddit or LinkedIn |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Checked by hand, since CI cannot cover it:
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The github-actions-generator skill vendors sample action.yml templates under .agents/skills. zizmor scanned the whole checkout and failed the pull request on template-injection findings in that sample code, which nothing ever runs. Point it at .github, where every workflow and action of this repository lives.
connectionsFromEnvironment() builds factories from an arbitrary environment object, but the registry only recorded credentials for connections it built itself. When that object was not process.env, a token matching no known secret pattern passed redaction untouched into outgoing comments and ForgeRequestError messages. Prepared connections now carry the credential values of their variable set, PEM keys in both escaped and real-newline form, and the registry registers them like any other connection. Regression tests use an opaque token absent from process.env and fail without the fix.
…ceiling With `execution.max_network` below `full` and no deployment sandbox, a repository choosing `isolation: local` ran on the host network: `LocalRunner` carries the policy without enforcing it, and a hosted provider may hand back a runner wider than the lease asked for. The ceiling was a label, not a limit. `runTask` now checks the runner it actually built: under a ceiling below `full` the run executes only when the runner is isolated and its network is no wider than the run's effective policy (the repository's own, narrowed to the ceiling), so a run that must have `none` gets exactly `none`. A RunnerPool lease is released before refusing, and the task is recorded as `failed` with the redacted reason, on the same terms as `ISOLATION_UNAVAILABLE`. The settings page hint (EN/IT) and the deployment-settings guide now say that a ceiling below Full needs the container sandbox or a hosted sandbox, and that local runs are refused.
`AUTH_ENABLE_MAGIC_LINK=true` or `AUTH_ENABLE_TWO_FACTOR_EMAIL_OTP=true` stopped the dashboard at startup: `packages/auth` refuses either flag without its delivery callback, and the app supplied neither `sendMagicLinkEmail` nor `sendTwoFactorOtpEmail`. `packages/mail` gains two templates, `magicLink` (the sign-in link, named to no account) and `twoFactorOtp` (the code printed once as text, never inside a link), registered beside the existing four with the compiled artifact re-rendered. The app's delivery bindings move out of `server/auth.config.ts` into `server/utils/auth-mail.ts`, which now wires every callback a policy flag can require, so turning on an email sign-in option is a configuration change rather than a startup error. Neither the link nor the code is ever logged.
A password sign-in on an account with two-factor authentication answered with `twoFactorRedirect: true` and nothing happened: the sign-in page showed a success it could not act on, and no page existed to take the code, so the sign-in could never finish. `app/auth.config.ts` passes `onTwoFactorRedirect` to `twoFactorClient()`, which navigates to the new `/two-factor` page (auth layout, guest-only like `/login`). The page takes the authenticator code in a numeric `one-time-code` field, switches to a backup code, and, when `/api/capabilities` reports mailed codes, sends one and verifies it; it offers to trust the device, refreshes the session and opens the dashboard on success, keeps a rejected code on the page with a `role="alert"` message through `useAuthErrorMessage`, and links back to sign-in. Copy is in the `auth` namespace (EN/IT) with the schema regenerated.
Signing in to approve a CLI request starts at /login?redirect=/device?…, but the two-factor hook opened /two-factor without it and a verified code always went to /. The visitor ended up signed in on the dashboard while the CLI kept waiting. The hook now carries the return address to the challenge page, which goes back there after verification and keeps it on its link back to sign-in. The address comes from the URL, so only a path on this app is accepted: no //host or /\host, no control characters, and not another sign-in page. The method switch also stayed enabled while a code was being checked. Switching changed which action the page read its pending state from, so it re-enabled submission and dropped the first check's answer. Pending now covers all four actions, and switching or mailing a code is disabled until the running check answers.
Summary
Related to #5. First slice of the v2 restructuring in
docs/plans/2026-10-17-code-zero-v2-brainstorm.md. Nothing here changes what a run does; it moves the workspace into the v2 shape and adds the next source-control boundary next to the old one.packages/core/*. npm names stay the same, so no import changes.apps/dashboardbecomesapps/web(@code-zero/web).packages/uiholds the colour tokens, UnoCSS theme, RTL/a11y dev presets and aDESIGN.md. Web and marketing stop carrying copies.packages/forgeis the source-control boundary rebuilt onforges, for GitHub, GitLab, Gitea, Forgejo, Bitbucket Cloud and Azure DevOps. Nothing imports it yet.aube cileftdrizzle-ormdangling and the web build died withENOENT. Same repair as4ff2b5don the old dashboard branch, which never made it tomain.hookTimeout; the app boot inbeforeAllregularly took longer than 10 s on a cold cache.Why
The plan's diagnosis is that the dashboard, auth and source-control edges need rewriting while the runtime holds up. Doing the moves first, as refactors that keep every check green, keeps the behaviour changes coming next small enough to review.
forgesreplaces sixteen hand-written adapter files with one maintained client that covers more forges, reports what each one can do throughcan(), and ships an in-memory fake for tests.packages/forgedepends only on@code-zero/shared, so the adapter rule holds.Verification
aube run check:repoaube run lint:ciaube run typecheckaube testaube run buildSafety and compatibility
observemode as read-only, or explained the policy change above.Packages no longer ship CommonJS. Everything in the repo that loads them is Node 24 or a Vite/Nitro build, but anything outside the repo that
require()s them will break. Local setups move their env file fromapps/dashboard/.envtoapps/web/.env.Agent context
packages/forge.packages/uiandpackages/forge, and ran the checks after every commit. I made every decision recorded in §10 of the plan.Reviewer notes
@code-zero/docsnever finished building locally: the Docus prerender runs out of V8 heap on a 3.8 GB machine. CI built all 19 workspaces uncached, docs included.github-actions-generatorskill ships sampleaction.ymltemplates, and zizmor failed on them. zizmor now audits.githubonly, where every workflow and action of this repo lives.unctxnamed anunpluginvariant whose nestedesbuilddiffers from the only snapshot, rather than a truncated id. It dangled on disk either way, so I repointed it. Worth a second look if you know why aube wrote it.packages/source-controlstays untilapiandwebare wired topackages/forge. That and the rest (auth v2, billing, worker, the GitHub Action, the web pages, Docker and serverless deploys) come as separate PRs.

Confidence Score: 5/5
No outstanding findings block merging this PR.Reviews (6) · Last reviewed commit: "fix(web): keep the sign-in destination t..." · Reviewed by Greptile