Skip to Content

← All archived runs

Run: agentic-app-scaffold

run.md

Run: agentic-app-scaffold

  • branch: claude/agentic-app-scaffold-pipeline-amlenr
  • pr: #894

02_define/output/spec.md

Spec: Scaffold the agentic app

  • slug: agentic-app-scaffold
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer
  • touches: apps/agent (new), turbo.json, package.json, eslint.config.ts, .icm/_shared/ci.md, apps/docs
  • complexity: standard

Problem

The agentic-layer architecture David approved calls for a separate app beside the platform — living on top of and independently from the classic screens — and no such app exists. Until it does, every agentic feature has nowhere to land: there is no seventh app in the monorepo, no deploy target, and no signed-in surface to build the conversation into. This is the first stub of the agentic-app-foundation batch (1 of 2; agentic-chat-interface builds inside the app this one creates), and it advances Scale the bridge / Q2-2026 Objective 3 — Validate Technical Infrastructure & Payout Flow: the agentic layer is new infrastructure, and this stub is the smallest slice that proves it builds, deploys and authenticates on its own before any AI call is made.

Proposed change

Create apps/agent (package @sustentus/agent), the agentic layer's home, as a minimal but production-shaped Next.js 16 app in the monorepo:

  • Monorepo wiring — picked up by the apps/* workspace glob; catalogue versions only; a dev script on port 3007 (the next free port after apps/console's 3006) backed by a real @sustentus/agent#dev turbo task, since turbo resolves an undefined task to an empty graph and a dev script without one silently does nothing; root agent:dev / agent:build scripts; a globals block for apps/agent/** in the root ESLint config; vercel.json carrying the standard npx turbo-ignore @sustentus/agent --fallback=main ignore command.
  • Sign-in gate — a Clerk proxy.ts (this repo's Next 16 middleware file) gating every route on an authenticated session, deny-by-default at the edge rather than per-layout, so the gate covers every route the app will ever have. The gate is "authenticated user" and nothing finer — no roles, no metadata marker. The app runs against the same Clerk production instance as the platform, so on a shared root domain a signed-in platform user is already signed in here; on the Vercel preview domain, where the session is not shared, the app's own /sign-in route issues one.
  • The landed surface — a branded shell page (@sustentus/ui, Geist, Tailwind 4, the platform's light/dark treatment) confirming who is signed in: name, email, and a sign-out control. That is the whole product surface; the conversation itself is stub 2.
  • Deploy accounting — the existing Vercel project agentic-interface is re-pointed by the operator at apps/agent; .icm/_shared/ci.md gains its commit status so the pipeline reads a seventh Vercel project instead of six.

No existing app is touched: no change to apps/web, apps/demo, apps/marketing, apps/docs, apps/help, apps/storybook or apps/console beyond the docs pages Ship updates, and no change to either shared package.

Acceptance criteria

  • apps/agent exists as @sustentus/agent — a Next.js 16 App Router app whose dependency versions all come from the pnpm catalogue (catalog:) where the catalogue defines them, and which declares no dependency on @sustentus/services.
  • Quality Project is green on the PR with apps/agent included — format, lint (within the 45-warning ceiling), typecheck and test all pass, and no existing app's build or lint output changes.
  • pnpm agent:dev starts the app on port 3007 via a @sustentus/agent#dev turbo task, and no other app's dev command is affected (pnpm web:dev still starts apps/web on 3000).
  • Requesting any route of the deployed app signed out/ and an arbitrary unknown path alike — redirects to sign-in rather than rendering; the redirect is issued by proxy.ts, not by a page or layout.
  • Signed in, / renders the branded shell and displays the signed-in user's name and email read from the Clerk session, with a working sign-out control.
  • The app deploys as its own Vercel project (agentic-interface, root directory apps/agent) and reports its own commit status on the PR; a commit touching only apps/agent leaves every other turbo-ignore-guarded project skipped ("Canceled by Ignored Build Step"), and a commit touching only apps/web leaves the agent project skipped.
  • .icm/_shared/ci.md lists the agent app's Vercel commit status alongside the existing six, so a stage reading the CI contract accounts for it.
  • The deployment and repository-structure docs pages account for the new app (written at Ship by docs-sync, in this PR).

Out of scope

  • The chat interface, any AI call or AI Gateway wiring, and any @sustentus/services data access — stub 2 (agentic-chat-interface) and later.
  • Role-aware routing, a route-policy table, View As, and lifting the auth/tenant/permission core out of apps/web.
  • The ai.sustentus.com production domain cut-over and the Clerk production root-domain verification — operator actions (see Operator preconditions), not feature work.
  • New environment variables. The app reads only CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, both already in turbo.jsonglobalEnv; the operator mirrors their values into the Vercel project. An AGENT_APP_URL / authorized-parties allowlist of the kind apps/console carries is deferred until something needs it, so globalEnv is unchanged by this run.
  • Making apps/web skip its own builds. The stub's rough criterion asked that an AI-only change not rebuild the platform "nor the reverse". The first half is delivered; the second is not, because apps/web/vercel.json deliberately sets no ignoreCommand — the platform builds on every push by design (.icm/_shared/ci.md). Adding one would change the platform's deploy behaviour and contradicts "the classic apps are untouched". Left as-is; raise it as its own chore if the build minutes are worth it.
  • Unit tests for the app. The scaffold introduces no branching logic of its own — the gate is Clerk's clerkMiddleware and a route matcher, exercised by the Quality build and the signed-out redirect check above. @sustentus/agent ships no test script this run; stub 2 adds one with the first real logic.
  • A landing/marketing surface, navigation, or any link between the platform and the agent app in either direction.

Open questions

  • None blocking. Two decisions were settled at pickup and are recorded here rather than left open: the app directory is apps/agent (apps/console was taken by the tenant-management console after this stub was cut, and agent was preferred over ai as a directory name), and the app deploys through the existing agentic-interface Vercel project re-pointed at apps/agent rather than a newly created one.

Operator preconditions

Not build work — these are Jamie's/the operator's:

  1. Re-point agentic-interfacealready done. Vercel project prj_u0liBVQaTAlGNaX1RsvBbjJHcHEh (team sustentus) is rooted at apps/agent, confirmed from this PR's first deploy. Nothing left to do here.
  2. Mirror the Clerk env varsCLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, the same production instance as the platform — into that project for Preview and Production. Until this is done the app builds but cannot authenticate, so the two sign-in criteria above are not observable on the preview.
  3. Before any subdomain go-live (not this run): verify the Clerk production instance's root domain is sustentus.com, which is what makes the platform session shared with the agent app.

Expected red until Build lands

Vercel – agentic-interface fails on every commit of this branch — and on main — with The specified Root Directory "apps/agent" does not exist. That is the project waiting for the directory this run creates, not a defect in the diff. It clears on the first Build push and must be green before Verify hands off.

Notes

Context budget: within band. Two reads beyond the Inputs table, both targeted and both because the stub's own notes were stale: apps/console's package/config shape (the stub assumed apps/console was not in the repo — it is, and it is the tenant-management console), and the Vercel project list (to confirm agentic-interface still exists and what state it is in).

Scope provenance: this batch has no scope.md. .icm/intake/agentic-app-foundation/breakdown.md records why — it was cut from the approved agentic-layer architecture report (David, 2026-08-27) on Jamie's instruction to produce a minimal first-win batch, without passing through /pipeline scopeapprove. The stub plus that breakdown are the settled source this spec traces to; there are no Q-n numbers to carry.

03_build/output/notes.md

Build notes: agentic-app-scaffold

  • commits: 30a9a10 feat: agentic-app-scaffold — apps/agent, the agentic layer's home
  • ci: GREEN on 213e8ad — including Vercel – agentic-interface, which had been failing on every commit of this branch (and on main) with The specified Root Directory "apps/agent" does not exist. This push created it.

What changed

  • apps/agent/** (new): the app. package.json (@sustentus/agent, catalogue versions, dev/start on port 3007), next.config.ts (noindex headers, transpilePackages: ["@sustentus/ui"]), tsconfig.json, postcss.config.mjs, vercel.json (turbo-ignore @sustentus/agent), proxy.ts (the Clerk gate), components/providers.tsx (theme-aware ClerkProvider), app/layout.tsx, app/page.tsx (the shell), app/(auth)/** (sign-in), AGENTS.md.
  • turbo.json: added @sustentus/agent#dev, depending on @sustentus/ui#build only — the app carries no @sustentus/services dependency. Reworded the neighbouring comment, which asserted apps/web was the only package with a dev task; that stopped being true with this entry.
  • package.json: agent:dev / agent:build root scripts.
  • eslint.config.ts: browser + node globals block for apps/agent/**.
  • pnpm-lock.yaml: the new workspace package. Purely additive (+55 lines) — CI runs pnpm install --frozen-lockfile, which fails without it.
  • .icm/_shared/ci.md: the Vercel project table gains Vercel – agentic-interface.

Acceptance criteria status

  • apps/agent exists as @sustentus/agent — Next.js 16 App Router, catalog: for every dependency the catalogue defines (next 16.2.12, @clerk/nextjs ^7.0.0 and geist are not in the catalogue and are pinned exactly as apps/web and apps/console pin them). No @sustentus/services dependency.
  • Quality Project green — passed on the pushed head, alongside Project run labels and Review diff against CONVENTIONS.md; all three advisory jobs pass too.
  • pnpm agent:dev on port 3007 via the @sustentus/agent#dev turbo task. No existing task was edited, so pnpm web:dev is untouched.
  • Signed out, every route redirects to sign-in — proxy.ts matches all non-static paths and treats only /sign-in(.*) as public, so an unknown path redirects exactly as / does. The redirect is issued at the edge, not by a page or layout.
  • Signed in, / renders the branded shell with the user's name and email from currentUser(), and a SignOutButton-wrapped control.
  • Own Vercel project — vercel.json carries the standard turbo-ignore command, so a commit that does not affect @sustentus/agent skips this project. The agentic-interface project is already rooted at apps/agent. Observable only on the preview (see below).
  • .icm/_shared/ci.md lists the agent app's commit status.
  • Docs pages — Ship's job (docs-sync), by design; not this stage.

Notes for Verify

  • The two sign-in criteria are not observable until the operator mirrors the Clerk env vars (CLERK_SECRET_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) into the agentic-interface Vercel project — spec → Operator preconditions #2. Without them the app builds but cannot authenticate. Check that before reading a failed sign-in as a code defect.
  • One deliberate step beyond the spec's letter, flagged rather than done silently: the ci.md table was missing Vercel – tenant-management (apps/console), which has been reporting on every PR since that app shipped. Adding the agent row to a table that was already wrong by one would have left the contract misleading in the same edit, so both rows landed and the counts in the surrounding prose were corrected with them.
  • No unit tests, per the spec's Out of scope: the scaffold has no branching logic of its own — the gate is Clerk's clerkMiddleware plus a route matcher. @sustentus/agent ships no test script; turbo skips the package for test.
  • eslint-config-next is deliberately not a dependency here. apps/web and apps/console carry it without using it (neither has an app-level ESLint config — the root flat config governs); copying that would have added a speculative dependency, which CONVENTIONS.md forbids. apps/marketing and apps/demo do use it, via their own eslint.config.js.
  • apps/agent/app/globals.css reproduces apps/console's @source lines verbatim.

Context budget: within band. One read beyond the Inputs table — apps/console's app files, as the closest sibling to model the Clerk gate and the theme-aware provider on.

04_verify/output/verify.md

Verify: agentic-app-scaffold

  • ci: GREEN on 1655fb0 — the Verify fix push, settled via ci-status.sh after it. The final commit on the branch edits only this file, so it re-runs the same checks against identical app code; Ship must re-establish the verdict on whatever head it inherits, as its contract requires.
  • previews built for 1655fb0: Vercel – agentic-interface (dpl_9jzCMN4bw6p6eLf4ajm96F9iv8jU, alias agentic-interface-git-claude-agentic-app-scaff-d9dc26-sustentus.vercel.app) and Vercel – web (unaffected by the change, built because the diff touches shared root config). The other six projects are skipped for this diff — recorded as skipped, not quoted as green. This closes the finding raised earlier in the stage: the Build head 0ba529d was an .icm-only commit with no agent preview; the Verify fix push restored one.
  • production-readiness: run — 1 blocker, 4 advisories. Blocker is operator-side (Clerk env vars).
  • code-review: medium (spec complexity: standard) — 2 findings, both fixed on branch. The CI Review diff against CONVENTIONS.md check passed with zero posted comments, so /code-review was run rather than triaged.
  • security-review: run — no HIGH or MEDIUM finding. Detail below.
  • playwright: TODO — manual DoD smoke performed instead

DoD smoke (on the preview — each line says who verified it)

The agent could not reach the preview at all — a preview exists, but it is not reachable without Vercel credentials. agentic-interface has deployment protection ssoProtection: all_except_custom_domains, so every path returns 302 → vercel.com/sso-api before the app's own middleware runs:

path result
/ 302 → vercel.com/sso-api
/sign-in 302 → vercel.com/sso-api
/some-unknown-path 302 → vercel.com/sso-api

So criteria 4 and 5 are operator-only by construction, not merely unverified.

  • apps/agent exists as @sustentus/agent, catalogue versions, no @sustentus/services — demonstrated: apps/agent/package.json + pnpm-lock.yaml entry; grep confirms no services import anywhere in the app (agent)
  • Quality Project green with apps/agent included — demonstrated: check run on 213e8ad, the commit that carries the app (agent)
  • pnpm agent:dev on port 3007 via @sustentus/agent#dev — demonstrated: turbo.json task block + root script; no existing task edited, so pnpm web:dev is untouched (agent)
  • signed-out redirect to sign-in — NOT demonstrated. Vercel SSO intercepts first, and the Clerk keys are absent so clerkMiddleware cannot initialise. Code path traced instead: proxy.ts:17 redirects when userId is falsy, and the deploy's route table lists ƒ Proxy (Middleware), so the middleware is genuinely deployed (agent — traced, not observed)
  • signed-in shell with name + email — NOT demonstrated, same two blockers. app/page.tsx reads currentUser() (agent — traced, not observed)
  • own Vercel project, turbo-ignore isolation — demonstrated live: 213e8ad (touches apps/agent) built agentic-interface; 0ba529d (.icm only) skipped it. The isolation works in both directions on the evidence of this branch (agent)
  • .icm/_shared/ci.md lists the agent app's commit status — demonstrated: the file (agent)
  • docs pages — Ship's job (docs-sync), by design
  • auth: persona sign-in + dashboard OK — operator, pending (needs the env vars first)
  • payments: not touched — no payment surface exists in this app (agent)
  • notifications: none expected — the app has no notification surface, no @sustentus/services dependency, no Ably or Resend usage (agent)

Findings & cleanup

Fixed on this branch:

  • apps/agent/app/(auth)/sign-in/[[...sign-in]]/page.tsx — Clerk's default "Sign up" footer action rendered, but this app has no /sign-up route and proxy.ts makes only /sign-in(.*) public, so the link bounced the visitor straight back to /sign-in. apps/console hides footerAction for exactly this reason and the guard was dropped in the copy. Now hidden, with the reason in the file. — fixed
  • .icm/_shared/ci.md — the six→eight project rename missed one line still quoting "all six previews are green", so a stage reading the contract could call a PR done on six statuses. — fixed

Operator actions — the gate should not pass as "demonstrated" until these are done:

  1. Set CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY (platform production instance) on the agentic-interface Vercel project, Preview and Production, then redeploy. Vercel's runtime error table for that project currently shows @clerk/nextjs: Missing publishableKey — count=41, routes=/middleware. That error is attributed to the project's old production deployment, so it is not this branch's code — but it proves the variable is absent from the project, and this branch needs the same one. Without it the app builds and then 500s on every request, which is a worse production state than the current "root directory does not exist", because it looks deployed.
  2. Then exercise criteria 4 and 5 on dpl_9LNEh853g6xJeVjtZ7Z8aYYsrsCP and report back.

Accepted / routed elsewhere, not fixed here:

  • Vercel Node version driftagentic-interface is set to 24.x while apps/agent/package.json declares engines.node: 22.x. The build warns and honours engines, so the runtime is right only because that field exists. Worth setting the project to 22.x. tenant-management has the identical drift and is pre-existing — route both as a chore rather than widening this run.
  • Sign-in redirect discards the requested pathproxy.ts redirects to a bare /sign-in and the page hard-codes forceRedirectUrl="/". Invisible with one page; a real deep-link bug once stub 2 adds conversation URLs. Intake note for agentic-chat-interface.
  • globals.css @source paths resolve to non-existent directories (they are relative to the CSS file). Copied verbatim from apps/console, latent there too, and Tailwind's automatic detection covers app/ regardless. Not introduced by this PR — left alone deliberately.

Security review

No HIGH or MEDIUM finding. What was checked, and why the obvious candidates are not findings:

  • Middleware matcher bypass — the matcher excludes paths ending in static-file extensions, the standard Clerk/apps/console pattern. The app serves exactly two routes (/, /sign-in/[[...sign-in]]), neither matching an excluded extension, so no bypass path exists. Worth re-checking when stub 2 adds routes.
  • XSS — the only user data rendered is fullName / primaryEmailAddress from the Clerk session, JSX-escaped. No dangerouslySetInnerHTML anywhere in the app.
  • Secrets — no key material in the diff; the app reads no process.env directly, and .gitignore covers /.clerk/.
  • Self-registration exposure — initially read as an open funnel, corrected by the code review: the sign-up link was broken, not a working registration path, and it is now hidden. Note the standing consequence of the specified gate: it admits any authenticated user on the shared Clerk instance, and the platform runs open self-registration. That is what the spec asks for ("authenticated user and nothing finer"); role-aware routing is the next batch. Flagging it as a fact to be aware of, not a defect in this diff.
  • authorizedParties not set — apps/console carries an optional allowlist and leaves it unset by default because previews need it that way. Matching the sibling; not a vulnerability.

Context budget: within band. Beyond the Inputs table: the Vercel project/deployment/runtime-error reads that the production-readiness pass needed, and one anonymous HTTP probe of the preview.

05_ship/output/investor-update.md

The agentic layer has a home to build in

Who it's for: Sustentus engineering — nothing persona-facing yet What shipped: A new app beside the platform, on its own deploy, gated on a signed-in Sustentus account. Why it matters: Agentic features now have somewhere to land, released without touching the classic screens — the first step of Scale the Bridge.

Dig deeper: https://github.com/sustentus/sustentus/pull/894

05_ship/output/release.md

Ship: agentic-app-scaffold

  • pr: #894 · merge: authorised — Ready to merge ticked by Jamie; this commit rides the squash
  • CI: GREEN on the head established by ci-status.sh after this stage's last push (the verdict line below is written before that push, so the PR's own merged state is the record)
  • technical docs: updated in this PR — technical/applications, technical/architecture, technical/architecture/repository-structure, technical/architecture/system-diagram, technical/deployment, technical/page.mdx and technical/development (the two port tables)
  • business docs: no business docs impact — the app adds no persona capability, no service-journey step and no headline product capability; its only surface is a shell page naming the signed-in user. business/** describes what users can do, and users can do nothing new.
  • release notes: ship-note-only — no end-user note. This is infrastructure: the app is reachable only by an authenticated Sustentus account and does nothing a user could be told about. A changelog entry announcing it would be noise in the help centre. The end-user note belongs to agentic-chat-interface, which puts a usable surface inside this app.
  • sent: ship note queued to #product-update by the merge (ship-note.yaml)
  • close-out: archive the run to apps/docs/archive/pipeline-runs/agentic-app-scaffold/. The epic agentic-app-foundation is not finished — agentic-chat-interface (2 of 2) is still in intake — so the epic folder stays put.

Acceptance check (vs spec)

  • apps/agent exists as @sustentus/agent, catalogue versions, no @sustentus/services — verified in Verify from package.json + pnpm-lock.yaml, and a grep for services imports
  • Quality Project green with apps/agent included — verified in Verify on 213e8ad
  • pnpm agent:dev on port 3007 via @sustentus/agent#dev, no other app's dev affected — verified in Verify from the turbo.json task block and the root script
  • signed-out redirect to sign-innot demonstrated by this pipeline. The operator has confirmed the two Clerk env vars are now set, which clears the blocker, but no one has reported exercising the criterion, and the agent cannot: agentic-interface carries Vercel deployment protection ssoProtection: all_except_custom_domains, so every preview path returns 302 → vercel.com/sso-api before the app's own middleware runs. Code path traced in Verify (proxy.ts:17, and the deploy's route table lists ƒ Proxy (Middleware)). Operator-verifiable only.
  • signed-in shell with name + email — same: not demonstrated, same access constraint, app/page.tsx reads currentUser(). Operator-verifiable only.
  • own Vercel project, turbo-ignore isolation in both directions — demonstrated live on this branch in Verify: 213e8ad (touches apps/agent) built agentic-interface; 0ba529d (.icm only) skipped it
  • .icm/_shared/ci.md lists the agent app's Vercel commit status — the file, updated at Build
  • The deployment and repository-structure docs pages account for the new app — this stage, in this PR (both named pages plus the four other pages the change made stale)

Notes

Two of the eight criteria merge unverified, not verified. That is a deliberate, recorded decision, not an oversight: they are the two the pipeline structurally cannot reach, Jamie authorised the merge knowing it, and Verify's record carries the same statement. If the operator exercises them and either fails, that is a bug against apps/agent, not a re-run of this stage.

Docs edits that go one line beyond this change, and why: the pages this change touched were each already stale by one app — apps/console was added without them being updated. Adding agent to a monorepo tree that omits console, or to a port table where 3006 reads as free, would have left the very table being edited self-contradicting. console was brought to truth in the same lines. Nothing else in those pages was rewritten. Pre-existing drift left alone deliberately: the docs still call apps/demo "dashboards" (apps/dashboards) throughout — wrong before this run, unrelated to it, and a chore of its own.

Checks: not run locally (block-local-checks.sh); the Vercel – docs preview build is what proves the MDX compiles, and the verdict is read back from CI before the merge.

Context budget: within band.