Skip to content

YC / marketing SPA — investigation result

Conclusion

No separate YC/marketing SPA exists in this repository as product code.

There is no project, folder, package, image, or deploy surface that is a dedicated single-page app built only for YC reviewers or marketing. The label yc-reviewer is not the name of an SPA; it is an audience used for cloud product tenancy/sessions and for filtering hosted docs. Design docs mention optional future “SPA mounts” under WingCMS; those mounts are not implemented (no wwwroot/assets/spas/ or equivalent).

Standing split (see also technical/mpa-site.md, business/mpa-strategy.md):

Surface Stack Role
Public content / marketing site ASP.NET Razor Pages + htmx MPA (src/WorkWingman.Site) Brochure, team, contact, “open the app” doors
Product UI Angular SPA (frontend/) Desktop (Electron) and cloud browser product
Hosted docs (subset of product) Markdown + cloud-docs-manifest.json via API Audience-filtered reading inside a cloud session

Do not invent a fourth “YC SPA” surface when documenting architecture.

What does not exist (negative evidence)

Check Result
Folder named spa/, marketing/, yc/, landing/ as app roots None in repo tree
Second Angular app (besides frontend/) Only frontend/angular.json
React / Vue / Svelte / Next marketing SPA package None
WorkWingman.Site as SPA Razor Pages MPA (Microsoft.NET.Sdk.Web, AddRazorPages); no Site package.json
Implemented CMS “mounted SPA” bundles Design-only in docs/design/wingcms-design.md; no runtime assets under Site
Deploy image for a marketing SPA App image = Angular+API (Dockerfile); site image = MPA (Dockerfile.site → wwsite / ww-site)

What does serve YC reviewers

1. Marketing MPA (not a product SPA)

  • Code: src/WorkWingman.Site (Razor Pages + htmx).
  • Deploy (ladder): Cloud Run service ww-site, host workwingman.ai (ops/cloud-run/env-ladder.json surface site.prod).
  • YC-facing UI: /App door labeled YC reviewer links to https://app.workwingman.ai/ with copy “Enter your invitation code” (Pages/App.cshtml).
  • Not: Angular shell, invite redemption UI, or sessioned product workspace.

2. Product Angular SPA + server API (same SPA as cloud/desktop product)

  • Code: frontend/ (Angular CLI project) + src/WorkWingman.Api.
  • Cloud packaging: Dockerfile builds the Angular app into API wwwroot; SPA fallback serves index.html (WorkWingman.Api/Program.cs MapFallback).
  • Deploy (ladder, prod app): Cloud Run ww-team @ app.workwingman.ai (ops/cloud-run/env-ladder.json surface app.prod).
  • Entry: invitation codes configure the audience. Since 2026-08-06 (WING-316), yc-reviewer (tenant id ycrev) redeems only on the dedicated ww-yc pilot service; ww-team carries team-test alone. Audience comes from the authenticated tenant session, not a separate SPA binary.
  • Runtime model: frontend types include audience: 'yc-reviewer' | 'team-test' on tenant session views (frontend/src/app/core/models.ts). API capabilities branch on yc-reviewer (WorkWingman.Api/RuntimeCapabilities.cs).
  • Topology (verified live 2026-08-06): ww-yc is a real, separate Cloud Run pilot service behind the shared load balancer at yc.workwingman.ai, digest-pinned via WorkWingman.CloudDeploy, with its own ww-yc Firestore database and rung pilot. ww-team no longer wires the yc invitation — the dual-audience-on-ww-team era ended with WING-316's one-code-one-surface ruling.

3. Hosted docs filtered by audience (including yc-reviewer)

  • Manifest: docs/cloud-docs-manifest.json — each entry has audiences (observed values: yc-reviewer, team-test).
  • API: GET /api/docs/manifest and GET /api/docs/content?path= in DocsController filter by session Audience and fail closed when the path is not on the allowlist or the audience does not match.
  • UI: Angular feature frontend/src/app/features/docs/ loads those endpoints inside the product SPA.
  • Manifest sample (as of investigation): shared plain/technical getting-started and packaging docs for both audiences; plain/yc-only.md → yc-reviewer only; technical/team-only.md → team-test only.

yc-reviewer is therefore:

  1. A tenant invitation / session audience for the cloud product, and
  2. A per-document docs tier in the cloud-docs manifest

—not a separate SPA codebase.

WorkWingman.Site also has Google OAuth docs-reader cookies and tier allowlists (DocsReaderSignInService, cookie scheme docs-reader in Program.cs) used with site Resources/docs reader flows. That is site perimeter + reader tiers, distinct from cloud-docs-manifest.json + /api/docs/* on the app service. Do not collapse the two models when debugging access.

TODO(verify): exact mapping of Site reader tier env names to production allowlists (names only; no secrets).

Angular projects inventory

Project Path Role
Product UI frontend/ Sole Angular app; Electron desktop shell + cloud server-edition SPA
Electron shell electron/ Loads built frontend (not a second Angular project)
Site src/WorkWingman.Site Not Angular
Doc Why
technical/mpa-site.md MPA stack and standing rule vs Angular
technical/cloud-run-server-edition.md Server edition, invite audiences, SPA+API same origin
technical/oauth-and-reader-tiers.md Cloud docs site OAuth / reader tier facts
business/access-control-posture.md Business view of reader tiers
plain/wing-202-release.md ww-team / historical ww-yc freeze narrative
business/mpa-strategy.md Why marketing is MPA, product is SPA

TODO(verify)

  • Whether production still runs a frozen ww-yc Cloud Run service separate from ww-team, or only invitation-partitioned ww-team (ladder documents the latter for prod app).
  • Full live host map for marketing vs app vs CMS vs any legacy LB path splits (app.workwingman.ai vs apex vs /app/* history in handoffs).
  • Complete inventory of which markdown paths are listed in cloud-docs-manifest.json in production images (manifest in git may lag image contents).
  • Site DocsReaderSignInService tier env var names (RT-3 ladder hazard noted in oauth/access-control docs).

Non-goals of this doc

  • Pitch content or YC narrative copy.
  • Implementation of a future marketing SPA.
  • Full WingCMS design (see design docs; SPA-mount sections are aspirational).