Cross-app nav-link normalization: one criteria for sidebar/navbar links per role in mars+pluto+venus
Elazar 2026-07-12: analyze sidebar vs navbar links per role in each app (are they the same?), converge ONE cleaner criteria across mars/pluto/venus, implement in all three. Driven+coordinated by coder-mars-cc with coder-venus-cc + coder-pluto-cc; NO pm/audit participation in design phase; read-only DB allowed, minimal DMs/turns/tokens. Plan goes to Elazar first, then pm-mars-cc gets input and coordinates objections check with pm-venus-cc/pm-pluto-cc. Mars baseline: single-source nav (buildNav in app-sidebar.tsx renders SAME links to desktop sidebar + mobile drawer; mobile header = hamburger+logo only); sections Trabajo/Equipo/Administrar + fixed tail Notificaciones/Perfil/Cerrar sesion; known wart: /alumnos duplicated in Equipo+Administrar for roster-admin teachers; section order swaps by primaryRole. Related: MARS-77 (per-role regroup Stage A, still open - this WI sets the fleet criteria MARS-77 executes under).
Questions
Activity
-
pluto reply: (1) single-source already (buildSections in app-sidebar.tsx, desktop+mobile share SidebarContent; tail Notificaciones/Perfil/Soporte/Cerrar sesion; AUTHENTICATED_HOME_ROUTE in navigation.ts). (2) Warts: role-string gates (alumno /mi-cursada, bedelia carga-anual) mixed w/ userCan; label forks by cap; dup-href already guarded by Set; C5 never verified formally. (3) Votes: all YES; C2 amendment - allow role-membership gates ONLY where link semantics = the role itself (own-work lane), hasCap for everything else; C3 tail = notifications/profile/support(optional per app)/signout. Pending: venus.
-
venus reply: single-source YES (src/lib/nav-sections.ts buildNavSections -> sidebar + header/mobile drawer; tail lives in app-header.tsx OUTSIDE manifest). Warts: /practicas/nueva duplicated (Alumno+Academico sections, C4 hit); gating raw role-strings except isCorrectionApprover cap precedent; sections role-named not taxonomy-named; tail outside manifest invisible to C5; participaciones deliberately non-nav. Votes: all YES; amendments: C3 labels stay local w/ documented mapping; C5 verify-set = manifest + fixed tail + explicit non-nav allowlist. => 3/3 converged. Consolidated criteria C1-C6 + pluto C2/C3 amendments + venus C3/C5 amendments. Plan going to Elazar.
-
criteria converged 3/3; plan to Elazar for GO, then PM input round
-
Elazar scope expansion 2026-07-12: (A) nav de-confusion WITHOUT model refactor (Usuarios/Docentes/Alumnos triple confusing; full refactor vetoed on token budget) - cheap label/merge proposals per app; Mars Administrar ~10 links too many. (B) venus /admin/errores useless as-is (make actionable or remove) + /admin/inicio 11-step setup UI made for nobody (retire UI, keep workflow as in-repo doc if agent-useful). (C) route-usage telemetry, self-hosted on venus.pensanta.com postgres (no vercel/supabase/3rd-party): draft = shared usage schema, server-side pageview log {app, routePattern normalized no-PII, userIdHash, role, ts}, batched fire-and-forget, NOT appEvents (would flood zero-noise baseline); usageDaily rollup; >=30d collection before retire/merge decisions (/equipo-academico, Gestion academica candidates). Transport lean: direct pg pool via DATABASE_URL_USAGE. Sent to coder-venus/pluto for one-reply votes.
-
pluto expansion reply: (A) labels+grouping only, zero routes: C3 headers; rename /admin 'Admin'->'Plataforma' (vs Gestion academica=/administrar roster-ops); drop /admin/usuarios-inactivos direct link (reachable via dashboard card); net -1 link +1 rename. (C) YES schema/fields/not-appEvents/90d/30d + mars authors, apps vendor. Transport objection (mild): direct pg from 3 Vercel serverless apps = connection churn (Pluto Fluid OFF, 1 conn/invocation) -> if direct it MUST be pooled (pgbouncer/supavisor-style), else prefer tiny ingest API on venus (one fetch keepalive, shared bearer env). Caveat: fire-and-forget dies at serverless response end - primitive must use next/server after()/waitUntil or accept sampling loss. Pending: venus reply.
-
venus expansion reply: (A) Administracion 10->8, labels only: Usuarios->'Cuentas y roles', Alumnos->'Alumnos (cursada)', drop Errores, Panel absorbed as section landing; optional sub-headings Personas/Datos/Sistema. (B) /admin/errores REMOVE (579-line page, zero drill-through, agents triage via SQL + error rail; actionable version would violate no-raw-IDs UI rule); /admin/inicio RETIRE UI, checklist -> venus/pm/reference repo doc (all step actions doable via /admin/lookups + admin pages). (C) YES schema/shape/rollup/retention/window + mars authors. Transport: prefers tiny bearer'd ingest API on venus (~40 lines, one choke point, no fleet pg creds in 3 Vercel envs); direct pg would require pgbouncer prereq. Caveat: filter/mark RSC prefetch requests or layout-render hits overcount. => DECISION (driver): ingest API transport (both coders prefer it); primitive uses next after(); prefetch-filtered. Plan finalized, going to Elazar for GO before PM round.
-
pm-enamel-cc pinged (Elazar considering enamel/coder-enamel-cc as ingest-API owner, host venus, DNS telemetry.pensanta.com TBD). Replied: no existing ingest API to duplicate; NO objection to enamel owning the SERVICE; conditions: wire contract per EVO-51 spec (batched POST, normalized routePattern no-PII, shared bearer), app-side primitive stays mars-authored/vendored (after() + prefetch filter), usageDaily rollup + 90d retention, coordinate schema location with one-db owners (usage schema should live in the consolidated 'telemetry' Postgres, not a new db). Will spec endpoint contract with coder-enamel-cc once Elazar confirms ownership.
-
Ownership confirmed: coder-enamel-cc owns ingest API service under EVO-51 conditions; DNS telemetry.pensanta.com + Caddy via nw-venus-cc (pm-enamel routes after Elazar GO). Endpoint contract v1 spec'd to coder-enamel-cc: POST /v1/usage, per-app bearer bound to app name, batched JSON {app, events[{routePattern normalized no-ids, userIdHash sha256+salt 16hex app-side, primaryRole, ts}]} cap 50/64KB, 204/401/400 no partial accept, async write after respond, schema 'usage' INSIDE ONEDB telemetry pg (coordinate bin-whey/nw-whey before create), usageEvents append-only + usageDaily rollup, raw 90d/rollup forever, no dedup, non-goals: no dashboards/no client beacon. Awaiting coder-enamel OK + Elazar GO on overall plan.
-
coder-enamel-cc OK on contract v1 + 3 amendments, all accepted -> contract FINAL: (1) app set extensible via server token registry, no hard mars|pluto|venus enum in wire contract (enamel will emit too eventually); (2) 403 valid-bearer-but-app-mismatch, 401 unknown/missing bearer; (3) server ts sanity clamp (reject ts>now+5min or older than 7d). All builds gated on Elazar GO.
-
Mars implementation LANDED: commit 4e9b9de v2.24.40 — usage-telemetry primitive (src/lib/usage-telemetry.ts, mounted via (protected)/template.tsx) + nav: Usuarios->'Cuentas y roles', C4 dedup Alumnos/Comisiones vs Equipo. Pre-push hook fail root-caused: MARS-408 lint runs --strict (exits 1 despite WARN banner); fixed via inline nsf-allow markers on the two contract-bound swallows. Pluto nav shipped 0a4e7f7 v2.5.7; awaiting enamel ingest URL+bearers. pm-venus dedicated-Postgres flag relayed to coder-enamel-cc.
-
audit-mars-ca BLOCK:4e9b9de (High privacy: non-uuid dynamic segments exported verbatim). Fixed same turn: c3cf2ba v2.24.41 fail-closed ROUTE_PATTERNS manifest ([unmatched] fallback), static-over-dynamic precedence bug also caught+fixed, 16-case harness all pass, response.ok delivery-health warn added. No data leaked (env-gated, ingest not live). Pluto+Venus alerted to re-vendor from c3cf2ba (Pluto 4b3f377 had vendored the pre-fix version).
-
audit PASS:c3cf2ba. Enamel ingest LIVE (telemetry.pensanta.com/v1/usage, webusage schema, e2e verified). Mars: 3 USAGE_TELEMETRY_* Vercel prod envs set (URL + bearer + generated salt, sensitive); activate on NEXT Mars deploy - no manual redeploy per git->Vercel rule. v2.24.41 confirmed live. Pluto+venus bearers going to their coders directly.
-
Mars 645bb91 v2.24.44: normalization now derives from the MARS-155 build route manifest via new matchRouteTemplate() (zero-drift, venus a17996d idea upstreamed; my interim gen-route-manifest.mjs generator abandoned after discovering it clobbered the existing MARS-155 route-manifest.ts). This deploy also ACTIVATES Mars telemetry (envs bake in). Cold brief sent to audit-mars-ca. Pluto active-ready v2.5.10; venus a17996d + envs going in. All 3 apps converged on derived manifests.
-
audit PASS:645bb91; live probe confirms v2.24.44 serving (dpl_Dfscdm6xKMvF9CTy4kdQtZUiLHVr) - Mars telemetry ACTIVE. Venus confirmed its generated manifest already excludes its [...notFoundPath] catch-all (fail-closed parity). All 3 apps live w/ derived manifests. WI stays inProgress: ~30d collection, then route retire/merge review (suspects: /equipo-academico, Gestion academica).