mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
f795fbcb4eb24f2f77779d1735d2360cb1d8af8d
2166 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f795fbcb4e |
feat(auth): dual app-reg parallel operation - legacy gktatooine app alongside new C4A8 app
True parallel operation, no forced re-consent: existing tenants keep signing in via the legacy app (1a400946), new tenants land on the C4A8 app (886ab5e2). Model "default legacy, funnel primary, learn in the background": - Web: am_auth_app flag -> NEXT_PUBLIC_ENTRA_DEFAULT_APP -> primary; get-started CTA hands over with ?authapp=primary; auth/me homedApp updates the flag so an operator tenant flip takes effect on each user's NEXT login; AADSTS90094/65001 one-shot cross-app retry, AADSTS65004 -> failed screen with previous-app link; am_login_attempt pins redirect returns to the app that started them. - Backend: TenantConfiguration.HomedAppClientId (null = legacy, GA-only flip via PUT config) + LastAuthClientId/Since observability; EntraAppRegistry resolves the acting app per tenant (no-op until the legacy config pair is set); Graph client-credentials, admin-consent URLs and the surfaced client id follow the homed app (app-scoped cache keys); JwtBearer [Authorize] pipeline now shares the middleware audience builders (was single-audience - would 401 legacy tokens after the swap); middleware trusts EntraId:LegacyClientId implicitly. - well-known association file now lists both app ids (publisher domain); deploy-web passes NEXT_PUBLIC_ENTRA_LEGACY_CLIENT_ID / _DEFAULT_APP; mcp-server.bicep default client id -> C4A8 app. Backend xUnit 3730/3730, vitest 625/625 (13 new), tsc/ESLint clean, next build ok. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d9d22bc443 |
fix(web): drop Beta pills on Submit Logs and Report Session
Feature-maturity badges were a leftover of the preview era — with the product publicly available, a lone "Beta" pill only signals uncertainty. Both features are fully functional; the dashboard banner already carries the actively-developed framing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6bd5728652 |
feat(platform): public availability — tenant activation replaces Private Preview, optional auto-approve
Backend: shared TenantApprovalService (whitelist add + auto-promote shape-guard + welcome email), AdminConfiguration.AutoApproveNewTenants (default off, uncached fail-closed accessor), tenant-auto-approve queue (1-min visibility delay, worker drops on flag-off/approved/suspended/missing config, approves as "System (auto-approve)" + TenantAutoApproved ops event), 30s negative whitelist cache, gate 2 error PendingActivation with mode-neutral message, signup strings and welcome email without preview wording. Web: /preview → /activation (SWA 301, robots, AuthGate), AuthContext accepts both error codes and clears pending state on successful auth/me, activation page polls every 30s and auto-redirects into the portal, dashboard banner reframed to "Actively developed" + feedback call, terms/privacy/get-started updated, feature badges → Beta, admin-config web chain + auto-approve checkbox, OPS_EVENT_TYPES. Deliberately unchanged: PreviewWhitelist/PreviewConfig tables, internal preview/* API routes, preview_signup notification type, Microsoft DevPrep preview mentions. Verified: solution build 0 errors, xUnit 3701/3701, tsc 0 errors, vitest 612/612, ESLint ratchet holds (0/115), next build exports /activation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d6affae8fc |
fix(web): plan card — bootstrap bullet wording (run already before MDM enrollment)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a70c5de667 |
feat(plan): rename Enterprise plan to Pro; Pro includes Bootstrap by default and Unrestricted Mode on request
- FeatureEntitlementCatalog: TenantEdition.Pro, canonical tier "pro", legacy stored "enterprise" stays readable as Pro (no data migration); new entitlements BootstrapIncluded + UnrestrictedModeAvailable - TenantEntitlementService: IsBootstrapEnabled (Pro-included OR additive GA flag) and IsUnrestrictedModeActive (Pro AND GA gate AND tenant toggle, read-time - trial expiry/downgrade re-arms guardrails fail-closed) - All bootstrap endpoint gates + SecurityValidator use the effective check; /auth/me and feature-flags expose effective availability values - PlanManagement accepts community|pro; AlreadyPro; Pro trial wording - Web: edition.ts accepts legacy "enterprise" (deploy-order safety), Pro labels/cards + new Pro feature bullets, effective section gating, terms/privacy renamed with Last-updated bump - Tests: catalog matrix, effective-gate payload tests, helper tests incl. trial-expiry re-gate (backend 3692 green, web 612 green) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e62bac9fea |
fix(web): hooks-refs batch 2.5 — ProtectedRoute wasAuthenticated ref to state; refs category at zero
wasAuthenticated was written AND read during render (the only such case). Historical per-mount state belongs in useState, adjusted during render via the guarded official pattern — behavior-identical: the value is only consulted in !isAuthenticated renders, which can only follow a render that already set it. Auth-gate control flow (auto re-login, loading early-return, post-login return URL) untouched. react-hooks/refs: 26 -> 0. Lint ratchet 115. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1e94a7aa91 |
fix(web): hooks-refs batch 2.4 — useDashboardSessions mirror refs via useLatest
Nine render-phase mirror writes replaced: scope/filter/pagination mirrors sync post-commit via useLatest; consuming callbacks list the stable refs in their deps. Drops two write-only refs (joinGlobalAdminsRef, loadingMoreRef) that nothing read. Imperative refs (fetchGen/fetchLock/loadAllToken/submittedFilter) untouched. Lint ratchet 119. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c330e5c91a |
fix(web): hooks-refs batch 2.3 — useDashboardStats mirror refs via useLatest
Seven scope mirror refs sync post-commit via useLatest instead of render-phase writes; fetchStats lists the stable refs it reads in its deps. Lint ratchet 128. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6015548172 |
fix(web): hooks-refs batch 2.2 — useFleetHealth mirror refs via useLatest
Four scope mirror refs (routeGlobal, selectedTenantId, tenantId, days) now sync post-commit via useLatest instead of render-phase writes. fetchData lists the stable refs it reads in its deps. Lint ratchet 135. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3f661d097d |
fix(web): hooks-refs batch 2.1 — useLatest helper; useAuthenticatedFetch + presence off render-phase ref writes
Render-phase ref mirror writes tear under concurrent rendering (react-hooks/refs). New hooks/useLatest.ts syncs the mirror in a passive effect instead; refs from custom hooks are not linter-known-stable, so consumers list them in dep arrays (identity is stable — no re-creation). Lint ratchet 141 -> 139. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3392a23da6 |
feat(bootstrap): promote dev script v2.2 to stable — download.autopilotmonitor.com endpoint + OOBE-relax (#118)
The stable Install-AutopilotMonitor.ps1 is now the previously field-tested -dev variant (v2.2): agent download via the Front Door alias https://download.autopilotmonitor.com/agent/ instead of the legacy blob host, mandatory SHA-256 manifest verification (Content-MD5 fallback removed), and the OOBE-relax exemption for the Windows Backup for Organizations restore case. Test-ShouldBootstrapAgent.ps1 mirrors the same guard logic; the separate -dev.ps1 files are gone. CI dev-render anchor literals in build-agent.yml updated to the alias URLs (stable + dev channel) so the literal-substitution check keeps passing; local build.ps1 anchors updated too (gitignored). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v2.0.1387 |
||
|
|
5312216f6b |
fix(web): hooks-warnings batch 1 — static-components, purity, immutability to zero (#117)
First burn-down batch of the React-Compiler hooks warnings (163 -> 141, CI ratchet lowered to match). static-components (14, real remount bugs): SortIcon (geographic- performance), SortIndicator (VulnerabilityReportSection) and SearchIcon/ ResultsDropdown (GlobalSearch) were defined inside render — a new component type every render, so React unmounted and rebuilt their subtrees each time (GlobalSearch tore down its dropdown on every keystroke). All hoisted to module level with explicit props. purity (6): Date.now() during render replaced with mount-time clocks (useState lazy init) for the date-input caps (Maintenance sections) and trial-expiry checks (TenantManagement, SectionTenantConfigReport); FeedbackSection refreshes its clock per fetch so "Xm ago" labels track the data; DownloadProgress falls back to NaN instead of wall-clock for unparseable event timestamps (rate calc already guards via Number.isFinite). immutability (5): fetch functions were declared below the effects that call them — moved above (diagnosis, fleet-health, DistressReports). This unblocked deeper compiler analysis, surfacing 3 latent set-state-in-effect findings in those files (93 -> 96); they belong to the set-state category and stay for its own batch. Verified: eslint 0 errors / 141 warnings (gate exits 0), tsc clean, vitest 611/611, static export builds. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
72392f6f4c |
chore(web): lint findings to zero errors + CI lint gate (#116)
* feat(web): typed routes — broken internal links are now compile errors typedRoutes: true (stable in Next 16; `next typegen` already runs in CI before tsc since #114). A typo'd <Link>/router.push target now fails the build instead of 404ing at runtime — the failure class behind the recent RSC/routing incidents. - lib/routes.ts: registry helpers return `Route`; withQuery validates the base path literal. New: deviceBlockUrl (OpsEventsSection previously hand-built /admin/security/device-block against the registry rule), route() to compile-check dynamic-section literals (nav configs, section index redirects), trustedRoute() as the single named seam for runtime-data hrefs (backend-emitted notification deep links, persisted post-login return URL). - Nav types (NavItem, ExpandableSubItem, PageSectionItem) carry Route hrefs — every sidebar/navbar entry is compile-checked. - ClientRedirect is generic so dynamic-section literals infer. Verified: tsc clean; negative test (typo'd static + dynamic routes) fails compilation as intended; 611/611 vitest; static export builds with an unchanged route list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): lint findings to zero errors + CI lint gate Package deal — fixes only land together with the gate, otherwise they drift right back. CI web job now runs `eslint . --max-warnings 163`: errors fail outright, the warning cap is a ratchet (lower it when fixing warnings, never raise it). Fixed to zero (mechanical categories, behavior unchanged): - no-unused-vars (49): dead imports/locals removed; where a prop is part of a live call-site contract, only the destructured binding dropped - no-explicit-any (29): real structural types for event payloads (EnrollmentEvent.data et al -> Record<string, unknown> + per-event interfaces), SignalR passthroughs typed via the library's own signatures; five consumer files got typing-only follow-through - no-unescaped-entities (15), prefer-const, jsx-no-comment-textnodes - no-require-imports (5): scripts/**/*.js override — Node CJS scripts, require() is correct there - one stale eslint-disable directive removed Deliberately NOT fixed inline: the React-Compiler hooks rules new in eslint-plugin-react-hooks v6 (set-state-in-effect 93, refs 26, static-components 14, purity 6, immutability 5, preserve-manual-memoization 3) are demoted to warn in eslint.config.mjs — each is a per-site behavioral refactor in auth/SignalR-adjacent code, to be burned down incrementally under the ratchet. Verified: eslint 0 errors / 163 warnings (gate command exits 0), tsc clean, vitest 611/611, static export builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d65157a092 |
feat(web): typed routes — broken internal links are now compile errors (#115)
typedRoutes: true (stable in Next 16; `next typegen` already runs in CI before tsc since #114). A typo'd <Link>/router.push target now fails the build instead of 404ing at runtime — the failure class behind the recent RSC/routing incidents. - lib/routes.ts: registry helpers return `Route`; withQuery validates the base path literal. New: deviceBlockUrl (OpsEventsSection previously hand-built /admin/security/device-block against the registry rule), route() to compile-check dynamic-section literals (nav configs, section index redirects), trustedRoute() as the single named seam for runtime-data hrefs (backend-emitted notification deep links, persisted post-login return URL). - Nav types (NavItem, ExpandableSubItem, PageSectionItem) carry Route hrefs — every sidebar/navbar entry is compile-checked. - ClientRedirect is generic so dynamic-section literals infer. Verified: tsc clean; negative test (typo'd static + dynamic routes) fails compilation as intended; 611/611 vitest; static export builds with an unchanged route list. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cf45911b69 |
Web upgrade: Next 16 + React 19 + MSAL v4 (#114)
* chore(web): bump to next 16, react 19, msal-browser 4, msal-react 3 react 19.2 aligns the declared dependency with the React the app router has been running all along (Next-vendored 19 canary). msal-react 3.0.29 is the React-19-compatible bridge that only needs msal-browser 4 — deliberately NOT msal v5, whose COOP redirect bridge requires new bridge pages per origin plus app-registration changes (separate project). The react-leaflet peer overrides are gone: react 19 satisfies react-leaflet 5 natively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): react 19 / msal v4 / next 16 code adaptations - msalConfig: allowNativeBroker -> allowPlatformBroker (v4 rename); storeAuthStateInCookie stays (deprecated in v4, removed only in v5) - zero-arg useRef() removed in @types/react 19: GlobalSearch (2x), dashboard autoLoadTimerRef - React.MutableRefObject -> React.RefObject in 5 session/progress hooks (all refs are mutable in 19 types) - layout: data-scroll-behavior="smooth" opt-in so route changes keep jumping instead of animating (Next 16 stopped suppressing CSS smooth-scroll during navigation) - next.config: analyzer comment - Turbopack default build skips the webpack-only analyzer, use --webpack for ANALYZE runs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): untrack Next-managed next-env.d.ts, generate types in CI Next 16 rewrites next-env.d.ts on every dev/build run and the routes import inside flips between .next/types and .next/dev/types (vercel/ next.js#86001) - official guidance is to gitignore it. CI now runs `next typegen` before tsc so the type stubs exist on a fresh checkout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): migrate lint script to eslint CLI (next lint removed in 16) Minimal flat config from eslint-config-next@16 (core-web-vitals + typescript) with build-output ignores. Lint stays out of CI as before; the 264 pre-existing findings are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1593232898 |
fix(web): every nav click was a full-page reload — skipTrailingSlashRedirect broke RSC fetches
skipTrailingSlashRedirect (added for the next-dev /api proxy 308) also defines __NEXT_MANUAL_TRAILING_SLASH, which disables the client router's trailing-slash normalization. Under output:'export' the router then requested RSC payloads as /<path>.txt while the build only emits /<path>/index.txt — every fetch 404ed and Next silently degraded EVERY <Link> navigation to an MPA (full-page) load. Most visible symptom: the sidebar remounted on each click and its expanded groups collapsed. - next.config.ts: dev-gate skipTrailingSlashRedirect (prod build normalizes links to /foo/ again; RSC fetches hit the emitted index.txt files) - GlobalSidebar: normalize usePathname() (client nav now yields /foo/, hard loads /foo — exact matches against the slash-less nav hrefs need both), and re-run the auto-expand effect when visibleExpandableGroups arrives so a hard load (deep link, F5) still expands the active group once MSAL resolves Verified: static export + local serve, marker survives Link nav (no document request, /terms/index.txt 200); tsc clean; vitest 611/611. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e4c06a9564 |
Landing page v2 — scroll story, portal-first hero, Autopilot Monitor rebrand (#113)
* feat(web): landing page v2 — scroll story, green brand, Autopilot Monitor wordmark Complete rebuild of the landing page as a scroll story of one enrollment: - Hero shows the product live (animated event-stream timeline, real event vocabulary) - Story acts: 09:02 power-on -> 09:41 ANALYZE-ESP-001 hit (most-firing rule, ~22% hit rate) -> 09:43 MCP terminal demo (full session report via get_session_summary + get_time_attribution) -> fleet zoom-out - Floating pill navbar, capabilities strip, restyled comparison, shared SiteFooter (now also mounted on /about, /terms, /privacy which had no footer at all) - Green mark (#33B161) unified as the only brand mark via BrandMark.tsx; blue-indigo gradient marketing mark removed - Wordmark is now "Autopilot Monitor" (two words) across all user-visible text, metadata and JSON-LD; technical identifiers (script names, ProgramData paths) untouched - Theming via lp-* CSS variables on :root/.dark — new components never collide with the global .dark !important overrides; old .landing-page block kept for /preview - StatsBand replaces PlatformStats: adds success rate (successfulEnrollments payload field, previously unused) and a loading skeleton instead of fake defaults - New opengraph/twitter/apple images in the new design (green mark on dark slate) - All animations hand-rolled (rAF/CSS/IntersectionObserver), CSP-safe, zero new dependencies, prefers-reduced-motion respected; auth path (AuthGate/LoginButton/ hostRouting) and staticwebapp.config.json unchanged Checks: tsc clean, vitest 611/611, static export build clean, no horizontal overflow at 375px. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): landing feedback round — linear story timeline, honest examples, dev stats - Story is now one vertical timeline with a strict top-down reading order (time rail left, text left, visual right) instead of the zigzag grid; act 1 gets a "session appeared" card so no column is left empty - Hero live session decoupled from the story device (CONTOSO-0912/Surface Laptop 7 completes; the story's CONTOSO-4711 is the one that gets stuck) - Defender for Endpoint removed from the demo stream (it is onboarded, not installed during ESP) — replaced with Microsoft 365 Apps - StatsBand: sample numbers in local dev only; in production the band shows real figures or hides entirely on fetch failure (never fake numbers, never an endless skeleton) - Capabilities copy: "Pull a diagnostics package" instead of the incorrect "Pull ETL and event logs" Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): hero v3 — mission-control cockpit instead of single-session card The first screen now shows the product as a live fleet cockpit: KPI strip (active-now count ticks), five enrollments running concurrently with advancing progress bars, a new session popping in, two completing during the ~23s loop, and the analysis engine visibly flagging the stuck device inline (HIGH ANALYZE-ESP-001 with the culprit app). Event stream column on desktop. Headline block is left-aligned and more compact to break the centered-template look. Deterministic single-tick choreography, IntersectionObserver start, prefers-reduced-motion gets a rich static frame. Replaces LiveTimeline.tsx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): hero v4 — real portal screenshots instead of fabricated cockpit Hero visual is now the actual product: the live portal dashboard (captured via Playwright from portal.autopilotmonitor.com, demo fleet staged in the DOM — no customer data, preview banner removed, wordmark as it ships) with a real analysis finding card (ANALYZE-APP-002 incl. remediation + evidence JSON, from the docs assets) overlapping bottom-right. Bonus asset time-attribution.png captured for later use. Removes HeroCockpit.tsx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): hero v5 — clean dashboard, real-analysis terminal, refined header - Analysis-finding overlay removed: the dashboard shot stands alone (was colliding with the stats band and not catching) - Terminal demo moved into the hero as its own "Then just ask" section and rescripted from a real production analysis (anonymized): failed attempt with 0x8007EA61 decoded to unmapped installer exit code 60001 (PSADT), transient proof via clean retry, actionable recommendation - Headline block refined: uppercase eyebrow instead of pill badge, tighter type and buttons - Wordmark "Monitor" back to ink color (green variant parked for compare) - Story act 3 now shows the real time-attribution screenshot instead of duplicating the terminal Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): story act 3 — notification + one-click diagnostics instead of cropped screenshot "See where the minutes went" (half-cut time-attribution raster) replaced with the admin's real next move: the Teams/webhook alert that fired with the rule, and the on-demand diagnostics flow (requested -> collected mid-enrollment -> ZIP ready). Crafted JSX cards consistent with the other acts; unused time-attribution.png removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): hero v6 — portal-first layout from approved design draft - Full-width sticky navbar (brand, Product/Capabilities/Compare/Docs, Sign in + Get started) replaces the floating pill - Hero per draft: gray eyebrow, "See every Autopilot enrollment. Live.", sub left / CTA group right with trust line, portal shot nearly full width (max-w-7xl), green background glow removed - Stats as a full-bleed band with dividers under the shot; "issues detected" replaces success rate (underlines automated analysis), events compact-formatted (8.3M) - "Then just ask" terminal extracted into its own JustAsk section after the band Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): get-started flow page, honest Teams card, calm buttons, light-only landing - New public /get-started page: five-step schematic (sign in -> request access -> consent -> deploy bootstrapper -> watch live); "Get started" in navbar and hero now lead here, "Sign in" keeps the login handoff — resolving the two-identical-CTAs problem. Wired into hostRouting PUBLIC_PATH_PREFIXES, sitemap, robots allow-list. - StoryAction Teams card corrected against the backend renderer: the real adaptive card has exactly one action ("Open session") — the invented "Collect diagnostics" button is gone (NotificationAlertBuilder.cs:60). - Buttons no longer jump (translate-y removed); hover is brightness + shadow only. Final CTA rebuilt as a calm two-column band per draft (no dark box, no glow). - Landing/marketing surface is light-only for now: lp-* tokens re-declared under .dark .landing-v2, theme toggle removed from LandingNavbar (screenshots are light; portal keeps full dark support). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): about/terms/privacy on the landing shell — no more sidebar flash The three public pages now render exactly like /get-started: LandingNavbar + landing-v2 tokens + eyebrow/headline header + SiteFooter. GlobalSidebar no longer treats them as sidebar pages for anonymous visitors — that was the "weird loading": app sidebar + PublicSiteNavbar popping in after hydration on marketing pages. PublicSiteNavbar and PublicPageHeader are dead and deleted; terms/privacy links switched from indigo to the accent green. Authenticated users keep the portal chrome on these pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): landing navbar anchors root-anchored so they work from subpages #story/#features/#comparison -> /#story/... — clicking Product/Capabilities/ Compare on /get-started (or any public subpage) now navigates back to the landing section instead of dead-ending on a missing local anchor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): calmer CTA green, about de-blued, public-page typography aligned - All real CTAs (navbar, hero, final CTA, get-started, story mock button) switch from bright #33b161 to the deep accent-ink green — matches the dark-green family in the portal screenshot, less loud - About page fully on the new design language: rainbow colorMap collapsed to the single green accent, audience cards/step bubbles/links/quick-link hovers de-blued - About/terms/privacy typography scaled to landing sizes: 15px body, smaller page h1s and section headings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): terms callouts on lp tokens — no more stray blue/amber banners Info callout ("Who can use it") now uses the green accent-soft style; the AS-IS warning uses the palette warn tones. Verified terms/about carry no remaining raw blue/amber/indigo utility colors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): about tech stack — drop version numbers, fix stale SSR claim Versions age silently on marketing pages (.NET 10 / Next.js 15 / React 18 removed, technologies stay). Also corrects "server-rendered React app" — the portal has been a static export since the RSC-prefetch incident fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): terminal demo app renamed to Contoso CRM App Neutral fictional app instead of a vendor-adjacent print driver name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): analysis card confidence score, 40+ rules claim, final CTA to /get-started - StoryAnalysis card gets the portal-style confidence bar (80%) - Act 2 copy: "40+ built-in rules" (exactly 40 in rules/analyze today) - "Start monitoring now" leads to /get-started like the other Get-started CTAs; sign-in stays in the navbar and on /get-started itself Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): portal brand aligned to landing, subtle Enterprise anchoring - Portal navbar (both variants) now uses BrandMark + the landing wordmark style (w-6 mark, 15px bold tracking-tight, strong ink in light/dark) — completes the brand consolidation follow-up - Enterprise plan anchored softly, free-first tone preserved: final-CTA subline + /get-started closing note ("same service, nothing to migrate") + "Plans" link in the footer Product column, all pointing to the docs plans page Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): final CTA right-aligned, get-started enterprise note "and more" Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): social images refreshed to current hero claim and tagline "See every Autopilot enrollment. Live." + the site tagline (real-time monitoring, automated analysis, AI-ready telemetry) on the green-mark dark card; opengraph + twitter regenerated at 1200x630. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): social card wordmark single-color, matching the site Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): portal sidebar selection states green — brand-aligned like the docs Active nav items, expandable group actives, page-section title/divider switch from blue to the green brand accent (light + dark variants). Purple Global Admin mode stays purple (semantic distinction); broader portal blues (status pills, buttons, KPI tiles) deliberately untouched — that is a separate re-theme pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): preview feedback round — dark-mode leak, alignment, renamed repo links - About/terms/privacy no longer render half dark for dark-mode visitors: the light-only marketing scope now neutralizes the global .dark utility overrides (bg-white / text-gray-* / border-gray-* / gray hovers) inside .landing-v2 - All landing sections aligned to one container width (max-w-7xl): story, just-ask, capabilities, comparison, how-it-works now share the same left edge as hero, stats band and final CTA - GitHub links updated to the renamed repository okieselbach/AutopilotMonitor (old hyphenated URLs redirected, now correct at the source) - /get-started wording: "Preview" instead of "Private Preview" (legal pages and portal status texts intentionally unchanged) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): preview page on the new design + ?demo=1 review mode; footer width aligned - /preview rebuilt on lp tokens: BrandMark, single wordmark, calm card, accent notify box, neutral next-steps, ghost actions — "Preview" wording instead of "Private Preview" - ?demo=1 skips the auth redirects and renders placeholder identity so the page can be reviewed unauthenticated (locally and on SWA previews) - SiteFooter container widened to max-w-7xl to match navbar and sections Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): preview page — prominent approval-pending badge, honest status copy - Amber "Approval pending" status pill above the headline - Next-steps wording per review ("sign out and wait a bit or contact me…", capacity clause dropped) - Corrected the false "approval status on your dashboard" claim: a blocked tenant never reaches a dashboard — signing in again lands back on this page while pending, and goes straight into the portal once approved (verified against AuthContext PrivatePreview handling) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): about/terms/privacy cards flattened to the landing pinstripe style All raised cards (shadow / shadow-sm) become flat lp-surface panels with the thin lp-line-soft border; inner plan boxes and quick-link tiles switch from gray-200 borders to the same pinstripe line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): portal primary actions green — buttons, avatar, Progress Portal icon - Primary buttons (bg-blue-600/indigo-600 + hover pairs) switch to green-600/700: admin backups, customs archive, vulnerability data, session reports, and the settings SaveResetBar - Navbar user avatar circles green instead of blue (all four variants) - Progress Portal sidebar MonitorIcon gets a deliberate green accent (light + dark) instead of inheriting gray - Semantic blues (status pills, info banners, KPI tiles) untouched Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): portal links and focus rings on brand green - All focus states unified: focus:ring/border blue-500 and indigo-500 (207 occurrences) -> green-500 across inputs, selects, buttons - Link patterns text-blue-600/indigo-600 with hover 800, hover:underline variants (36) -> text-green-700 hover:text-green-800; icon hovers hover:text-blue-600 (12) -> green - New dark override .dark .text-green-700 -> green-400, mirroring the existing blue-700 convention - Semantic blues untouched: info banners, status pills, KPI tiles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): local dev sign-in repaired; progress portal brand accents green Local sign-in appeared broken (always landed on /progress as a role-less user) — two stacked regressions in the dev API proxy: 1. trailingSlash:true (static-export change) made next dev 308-redirect /api/* to /api/*/ BEFORE the proxy rewrite; the Functions backend rejects trailing-slash routes, so /me silently failed. -> skipTrailingSlashRedirect: true (no effect on the static export; SWA handles trailing slashes itself). 2. .env.local DEV_API_PROXY_TARGET still pointed at the pre-EU-cutover autopilotmonitor-api (dead since 07-18) -> now -api-eu; proxy verified healthy (v1.5.1027). Progress portal: header icon, icon circle, "Change device" link and "Check Status" button on brand green; the blue activity indicators (downloading/installing) stay blue — that is the portal-wide in-progress semantic, not branding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): loading screens and remaining primary buttons on brand green - All four loading spinners (AuthGate, ProtectedRoute, CalculatingCard, UnmappedSoftwareTab) green instead of blue - Blue-indigo gradient loading/error backgrounds (AuthGate overlay, ProtectedRoute states, error page, CalculatingCard, SectionPlatformUsage) replaced with the neutral lp background (theme-aware in the portal) - 19 non-contiguous primary buttons (bg-blue-600 text-white ... hover:bg-blue-700) the first sweep missed -> green, incl. ProtectedRoute retry/back buttons, error page, audit, diagnosis, session table Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): last blue interactive states green; monitoring headers wrap on mobile - Time-range toggles (fleet health, software, usage, dashboard, platform usage), software lens tabs, MCP-usage segmented controls, SLA report button, gather/analyze rule save buttons, and the page-header icons on fleet health / geographic performance -> brand green. Zero remaining bg-blue-600/indigo-600 interactive patterns. - Monitoring page headers (fleet health, software, SLA, usage metrics, geographic performance, audit): title row gets flex-wrap + gap-y-3 and the control group (tenant scope + range buttons) wraps as a compact second line on narrow screens instead of pushing off the right edge; range buttons slightly compacted (px-3 py-1.5). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): geo map dev crash — per-mount container key for Leaflet "Map container is already initialized": dev-mode remounts (StrictMode re-runs, Fast Refresh over the dynamic() wrapper) hand Leaflet the same container div twice and it refuses. A per-mount key on MapContainer forces a virgin div for every (re)mount; MapCleanup keeps handling instance teardown. No production behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): form control accents (radios/checkboxes) green portal-wide Checked-state colors for radios and checkboxes (indigo/blue/amber/teal/ sky variants, 19 controls incl. the diagnostics destination radios and subfolder checkboxes) unified on green, plus the diagnostics "Add" button. Deliberately kept: red danger-confirmation checkboxes and the purple global-admin control. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): pin native form-control accent-color to brand green Root cause of the "red" radios/checkboxes: there is no @tailwindcss/forms plugin, so text-* utilities never styled native controls — their check color silently followed the user's OS accent color. A global accent-color rule (green-600, green-500 in dark) pins all radios/checkboxes to the brand; the danger-red and global-admin-purple controls override via accent-* utilities. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): progress portal icon inherits item color again Permanent green stood out against the gray siblings — the sidebar icon now follows the item state like all others: gray at rest, green when active. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): geo map root fix (react-leaflet 5) + readable dark muted text - Root cause of "Map container is already initialized": Next 15 app router runs its bundled React 19 regardless of the installed react@18, and react-leaflet 4.x is incompatible with React 19 StrictMode double-invoked effects. Upgraded react-leaflet 4.2.1 -> 5.0.0 (@react-leaflet/core 3) and removed our now-counterproductive workarounds (MapCleanup manual remove, per-mount container key) — v5 owns the map lifecycle including unmount teardown. Verified via a throwaway repro page: map renders, remount toggle survives, console clean (repro page deleted). - Dark mode: .dark .text-gray-400/500 lightened (#64748b -> #7b8ba3 / #8896ad) — notification timestamps and other muted text were nearly invisible on raised dark surfaces (unread bg, hover rows). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): subtler dark hover surfaces — hovered rows stay readable .dark hover:bg-gray-50/100 dropped from #334155 to #283548/#2e3c52 (one step above the card surface): the bright hover wash made notification timestamps and other muted text illegible on highlighted rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): GA/tenant notification rows dark-aware — opacity variants escaped the overrides The global-admin and tenant notification rows use bg-purple-50/30 and bg-blue-50/30 (+ /50 hovers) — opacity-variant class tokens the global .dark .bg-*-50 overrides never match, leaving light rows with light text on dark mode. Explicit dark: variants (purple/blue-900 at low opacity) fix the readability. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): notification row hover clearly distinguishable in dark mode GA (purple) and tenant (blue) rows: dark hover raised from /30 to /45 opacity so the highlighted row visibly differs from its resting tint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web): one SegmentedControl for all range/group toggles New shared components/SegmentedControl.tsx (the geo-style connected pills on a gray track) replaces the three mixed patterns: loose button trios on fleet health and software, the months <select> on SLA, and geo's two inline toggles (time range + group-by) now all render the same control with shared 7/30/90-day labels. Dark mode: active pill uses arbitrary-value colors because the global .dark .bg-white override would blend it into the track. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web): MCP usage sections use the shared SegmentedControl too The last remaining variant (joined bordered buttons with raw 7d/30d/90d labels in admin metrics + reporting MCP usage) replaced by the shared control with the unified "7/30/90 Days" labels. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: clickable notification deep links in the bell dropdown GA notifications now carry hrefs (session/diag-files report -> report modal, preview signup -> tenant management filtered), GA rows render the View link, and every bell row with an href navigates on row click and closes the dropdown. Target sections accept ?reportId= / ?tenantId= via the proven Suspense+useSearchParams pattern from DeviceBlockSection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(web): npm overrides pin react-leaflet peer deps to installed react react-leaflet 5 peer-depends on react 19; the app router runs Next's bundled React 19 at runtime while the installed package stays react 18. Without the overrides a plain npm ci (SWA/Oryx build) fails on the peer conflict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9c50d98792 |
fix(web): sign-in landed back on www — HostRoutingGuard bounced before MSAL settled
Two defects of the same shape in the portal→www direction: - Deterministic: a signed-in user opening portal root was always bounced to www (guard effect fires at hydration, before MSAL restores the session, so AuthGate never got to route "/" to /dashboard). - Race: MSAL's module-level init strips the #code hash before hydration, so the auth-response exception saw nothing and the bounce destroyed the in-flight token redemption — the user ended up unauthenticated on www. Portal→www now waits for settled, unauthenticated auth state; www→portal stays auth-free. Decision extracted as pure decideHostBounce with the safety rules pinned in hostRouting.guard.test.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
20b43e5349 |
feat(agent): gather debug log — phase transitions + per-match regex traces
- New "phase" stage: OnPhaseChanged writes a transition marker line (phase change: Old -> New) so rule-level lines read against the current phase; only on real changes, written outside _scopeLock. - LogParser traces each regex match: line number, matched text (<=200 chars) and capture groups (<=100 chars, same selection as event data), capped at 10 lines per file per run; the per-file summary keeps the full count. - Invalid-regex error line now includes the offending pattern string. Tests: 4 new (transition dedup, match trace, cap, pattern in error); agent suite 2137/2137 green. OKF doc + docs/log.md updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>v2.0.1386 |
||
|
|
d0d1d28371 |
perf(backend): column projection for unmatched-software scan
GetUnmatchedSoftwareFunction runs a full cross-tenant scan of SoftwareInventory on every request (including every pagination click) but only reads 9 properties. GetAllSoftwareInventoryAsync now accepts an optional $select projection and the function passes the consumed columns, cutting transfer and entity materialization. Server-side filtering on CpeUri is documented as impossible: rows re-created by the cascade-delete 404 path carry no CpeUri property and Table Storage cannot express "property missing" — those rows must still count as unmatched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5191751ed2 |
perf(web): lazy admin context + instant shell for software mapping
AdminConfigProvider no longer eagerly fetches global/config, config/all
(46 KB) and preview/whitelist on every /admin/* entry. Sections that
consume adminConfig/tenants trigger the idempotent ensureAdminConfigLoaded/
ensureTenantsLoaded on mount; fetchTenants stays as the explicit post-save
refresh. Pages that only need the token (Software Mapping, Backups, ...)
now pay nothing.
/admin/software gets a route-level loading.tsx and all three software tabs
render a TableSkeleton instead of a centered spinner on initial load. The
Mapped/Ignored mount guards ("!loaded && !loading") could refetch endlessly
after a failed fetch; now fetch only on !loaded.
Also gitignore .playwright-mcp/ (Playwright MCP browser artifacts).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
907118649b | added playwright mcp | ||
|
|
26ecbf33d6 |
feat(web): instant-shell loading UX — table skeletons + route loading fallbacks
Perceived hangs on navigation (e.g. Distress Reports) had three causes: missing loading.tsx fallbacks froze the old page until the new chunk rendered, loading state initialised false flashed empty states for one paint, and several pages blocked their entire render on the first fetch. - New TableSkeleton/SectionSkeleton primitives (server-component-safe), loading-UX convention documented in TableSkeleton.tsx - 20 new route-level loading.tsx fallbacks (admin areas + top-level pages) - Distress/Session Reports + Feedback: loading init true, skeleton in the table slot; refreshes keep the populated table - Dashboard: skeleton in the sessions slot instead of a blank gap - Converted full-page loading early returns on audit (also fixes a latent auth hang: the spinner sat outside ProtectedRoute so a fresh tab never triggered the MSAL redirect), usage-metrics, fleet-health, sla and admin Usage Plans; deliberate gates (diagnosis, preview, CalculatingCard pages) left untouched Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
adb103fc9e |
fix(web): always render the diagnostics opt-in in the report modal
Hidden-when-unavailable read as missing. The block now always shows: active checkbox when the session has an uploaded archive, disabled with an explanatory hint when it does not. Submit semantics unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
26c8b80efa | updated commands | ||
|
|
89c5c7da03 |
feat: richer, self-describing session report content
The report ZIP now captures everything an operator needs even after the session is gone, and states its own completeness: - session.csv: append the 15 newer Sessions fields that had drifted out of the export (failureSource, reconcileReason, adminMarkedAction, validatedBy, rebootCount, isHybridJoin, isSelfDeployingProfile, connectionType, geo*, avgApiLatencyMs, apiRequestCount, stalledAt, failureSnapshotJson) - completeness signal: exportedEventCount / sessionEventCount / eventStreamActive in the request and report-metadata.json; the modal warns while event pages are still streaming - includedDiagnostics records sourceBlobName/sourceDestination so a failed copy still points at the original archive - tenantContext snapshot (fail-soft): upload mode/destination, gather- rule debug flag, custom gather/analyze rule IDs + UpdatedAt, IME pattern IDs — no secrets, no definitions - robustness: 8 MB client-side screenshot cap (20 MB body cap headroom), attachments status in metadata instead of silently dropping invalid base64, SanitizeZipEntryName strips path components from customer- supplied file names (both report types) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
48308df4ff |
feat: opt-in preservation of session diagnostics archive with session reports
Session reports persist while sessions and their diagnostics blobs are
removed by user deletion or retention. A new checkbox in the Report
Session modal (shown only when a diagnostics ZIP was uploaded, default
checked) lets the submitter include the archive: the backend streams a
server-side copy into the durable session-reports container.
- SessionReportDiagnosticsArchiveCopier: fail-soft copy (never blocks the
report), reuses the Hosted/CustomerSas read paths, re-verifies the
hosted tenant prefix via ClassifyBlobName, honours the admin size cap
and download timeout; outcome persisted as DiagnosticsCopyStatus
- Report row/API gain DiagnosticsBlobName + DiagnosticsCopyStatus
(written only when requested, omitted for legacy rows)
- report-metadata.json records includedDiagnostics {requested, blobName, status}
- Admin reports UI: second Download Diagnostics button + failure status line
- Privacy page: explicit exception for the opt-in report copy (Last updated bump)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
cefa57fde5 |
feat(web): track negotiated SignalR transport in telemetry
Long-poll 404s in dependency telemetry come from clients whose corporate network blocks WebSockets. Emit the negotiated transport on connect and reconnect so fallback sessions are attributable instead of mysterious. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
946d503be0 |
feat(mcp): emit RFC 9207 iss parameter in OAuth authorization response
MCP spec 2026-07-28 requires clients to validate the iss parameter against the recorded issuer before redeeming the authorization code (AS mix-up defense). The /oauth/callback redirect now carries iss=<public base URL> — the same value the RFC 8414 metadata reports as issuer — and the metadata advertises authorization_response_iss_parameter_supported. Pre-9207 clients ignore unknown response parameters (RFC 6749 s4.1.2), so nothing regresses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f744ad8406 | fix code review findings | ||
|
|
5e9876bc5f |
fix: hard page reloads on family routes — RSC .txt payloads were wildcard-rewritten
Client navigation to an exported route fetches its RSC payload (<route>/index.txt). The legacy-path wildcards (/admin/backups/* etc.) matched those .txt paths and served the detail HTML instead — the Next router cannot parse HTML as a flight payload and falls back to a hard full-page navigation, resetting all client state (visible as the sidebar collapsing on every Backups/Customs-Archive navigation). Exact self-rewrites for all nine exported index.txt files now precede the wildcards; the swaConfig guard pins txt-before-wildcard for every family. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ffe88a47c6 |
fix: /sla dead-ended on www — it is a portal page, not a public one
/sla sat in PUBLIC_PATH_PREFIXES only because SLA notifications historically linked www…/sla. It is a fully authenticated page (ProtectedRoute + token fetches), and since ProtectedRoute stands down on the public host, www/sla waited forever on "Loading SLA metrics...". Removed from the public prefixes: the nav stays on portal (no bounce), old www…/sla notification links bounce to portal/sla where auth works, and SlaBreachEvaluationService.DashboardUrl now links the portal host directly for future notifications. /sla added to robots disallow. Web tsc + 603/603, backend build clean + SLA suite 38/38. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1f22b65d57 |
fix: /apps, /admin/backups, /admin/customs-archive list pages shadowed by detail rewrites
SWA's trailingSlash:auto normalizes /apps to /apps/, which the /apps/* legacy wildcard matches — so the LIST pages were rewritten to the DETAIL html (prod: /apps rendered the app-detail view with an empty name and hung on "Aggregating app analytics"). Exact list-route rewrites now precede each family wildcard; the swaConfig guard pins list-before-wildcard order for all families. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
03afbf6ac6 |
fix: double sign-in on www→portal handover — silent SSO for app-triggered logins
Two causes of the reported "sign in on www, then sign in again on portal":
- Race: on www/<portal-path> ProtectedRoute fired loginRedirect BEFORE
HostRoutingGuard's replace, signing the user in on the wrong origin.
ProtectedRoute now stands down on the public host and lets the guard move
the page.
- prompt "select_account" forced the account picker on EVERY loginRedirect,
including the automatic portal-side re-auth where Entra's session cookie
would complete silently. login({auto:true}) (ProtectedRoute) drops the
prompt; user-clicked logins (LoginButton/PublicSiteNavbar, manual retry)
keep the picker.
Net UX: sign in once — the portal-side re-auth is a silent Entra roundtrip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
36172b8cc5 |
fix: www↔portal redirect loop — MSAL auth is per-origin, guards must not fight it
Production incident after the static-export deploy: authenticated-on-www users hit a hard ping-pong. Chain: AuthGate (www) pushed /dashboard → HostRoutingGuard bounced to portal (no MSAL session there — auth state is per-origin) → ProtectedRoute's login-failure fallback navigated to "/" → guard bounced back to www → AuthGate pushed again. Three-part fix: - HostRoutingGuard: never bounce while the URL carries an MSAL auth response (#code=/#state= land on the ORIGIN ROOT because redirectUri has no path — a bounce destroys the sign-in), plus a sessionStorage bounce counter that stands down after 3 hops/15s as a generic loop breaker. - ProtectedRoute: login failure no longer navigates to "/" (the loop's return edge) — fails in place with a manual "Sign in" retry UI. - AuthGate: on the public host, hand over to the portal origin in ONE window.location.href navigation (getPortalLoginUrl pattern) instead of router.push + guard cascade. tsc clean, vitest 603/603, export build 87 pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8849a68b1c |
fix: og/twitter/apple-icon as static PNGs — extension-less exports served as octet-stream
The ImageResponse metadata routes export extension-less files and SWA route-level Content-Type headers do not override the file server's type (verified in prod: application/octet-stream + nosniff, which breaks social preview crawlers). Replaced with the one-time-rendered PNGs as static app/ metadata files — Next now emits /opengraph-image.png URLs and SWA serves image/png by extension. Content-Type route entries dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cc195990ff |
fix: SWA deploy — drop invalid is_static_export input, dedupe trailing-slash routes
The pinned Azure/static-web-apps-deploy@v1 has no is_static_export input (warning only — Oryx detected the export and validated out/ regardless). The real failure was config validation: with "trailingSlash": "auto" SWA normalizes /x and /x/ to the same route key, so the defensive trailing-slash duplicates were rejected as duplicate rules. One entry per route covers both forms under auto. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
18b28260a7 |
feat: portal static export — eliminate the SWA SSR runtime (cold-start root fix)
output:'export' + trailingSlash: the SWA now serves plain static files; the
managed SSR runtime — the portal's last cold-start source ("Backend call
failure" when a cold start exceeded SWA's 45s limit) — no longer exists.
- Detail routes are query-string based (/sessions?id=, /sessions/inspector?id=,
/diagnosis?id=, /apps/detail?name=, /admin/backups/detail?id=,
/admin/customs-archive/detail?tenantId=&rowKey=). Single-definition builders
in lib/routes.ts; all 21 link sites migrated, duplicate sessionHref helpers
consolidated. Suspense boundaries added everywhere useSearchParams drives
page identity (hard export-build errors otherwise).
- 9 [section] families prerender via server shells (generateStaticParams from
the *NavSections registries, dynamicParams=false, client bodies extracted to
SectionClient) — 37 pages; server redirect() index pages became
ClientRedirect shells. robots/sitemap/og-image routes force-static.
- Legacy PATH-shaped deep links are preserved FOREVER (sent Teams/Slack/
webhook notifications cannot be edited): SWA wildcard rewrites serve the
family page and the globally mounted LegacyPathRedirect rewrites to the
canonical query URL, preserving query + #event hashes (mapping pinned by
lib/__tests__/legacyPathRedirect.test.ts).
- middleware.ts deleted → client-side HostRoutingGuard (prefix list moved to
lib/hostRouting.ts). Legacy /go/[code] route + bootstrapValidation deleted —
BootstrapSessions table verified free of active pre-cutover sessions.
- staticwebapp.config.json now carries production routing: 19 docs/marketing
redirects (order-sensitive), legacy rewrites (exact before wildcard), 404
override, all security headers incl. literalized prod CSP (no unsafe-eval),
Content-Type routes for the extension-less og images. Guarded by the new
utils/__tests__/swaConfig.guard.test.ts. Maintenance mode is now a
first-routes-entry insert, never a whole-file swap (README rewritten).
- Backend notification deep links go through Constants.PortalSessionUrl
(query shape, escaped, pinned); 3 producers migrated.
- deploy-web.yml uploads out/ with is_static_export.
Export build: 79 static pages, no go/ output. Web: tsc clean, vitest 53
files green (618 incl. new guards; 2 deleted legacy suites). Backend:
solution build clean, 3622/3622 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
6cbd507b4f |
fix: client-IP extraction was dead on Flex — RemoteIpAddress fallback + AFD-aware rate-limit key
Post-deploy verification of the go endpoint showed every anonymous caller keyed as "unknown": Flex Consumption does not surface X-Forwarded-For to the isolated worker, so ALL per-IP rate-limit buckets (bootstrap-validate, distress, diag-ticket-download, stats/platform) had silently collapsed into one shared bucket — confirmed by DistressReports.SourceIp being "unknown" across the board. - GetTrustedClientIp falls back to HttpContext.Connection.RemoteIpAddress (transport-derived, unspoofable; IPv4-mapped addresses normalized). Heals every existing caller without signature changes. - New GetRateLimitClientIp for AFD-fronted endpoints: honors X-Azure-ClientIP only when X-Azure-FDID carries exactly our profile ID (FrontDoorProfileId app setting, set on autopilotmonitor-api-eu); fail-closed to GetTrustedClientIp on unset/mismatch/multi-value. Used by GetBootstrapScriptFunction (traffic arrives via go.autopilotmonitor.com, where the trusted hop is AFD egress). - FdidMatches table tests incl. append/spoof cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
26bcab9054 |
feat: move OOBE bootstrap script endpoint into the backend (go.autopilotmonitor.com)
The portal's Next.js /go/[code] route was the web app's only request-time
server code and blocked the planned static export. The script generation
now lives in the Function App as GET /api/bootstrap/go/{code}
(GetBootstrapScriptFunction), reached by customers as
irm https://go.autopilotmonitor.com/{code} | iex via a Front Door custom
domain whose /* route rewrites onto the API path.
- New Constants.BootstrapGoBaseUrl; CreateBootstrapSessionFunction now
hands out the go URL (previously unguarded — CreateBootstrapSessionUrlShapeTests
pins it now). go host added to both URL-guard enforced lists.
- BootstrapScriptValueValidator ports the bootstrapValidation.ts injection
invariants (exact GUIDs, https + host allowlist + /agent/*.zip path,
expiry <= 14d) as defense-in-depth for in-process values.
- OobeBootstrapScriptGenerator ports the PS template as a Replace-token
raw string; golden test pins byte parity with the TS template
(TestData/oobe-bootstrap-golden.ps1, captured from route.ts).
- Always-HTTP-200 text/plain contract incl. rate limit (own bucket
bootstrap-script:{ip} 20/min); disabled tenant indistinguishable from
unknown code. Catalog: PublicAnonymous; NoStoreCacheMiddleware covers
/api/bootstrap/go/ (script inlines the bearer token).
- Web: BOOTSTRAP_GO_URL registry entry, sessions list shows the go host,
legacy route marked LEGACY (alive for pre-migration URLs <= 168h,
removal in the static-export PR).
- Docs: new OKF backend/oobe-bootstrap-go-flow.md, url-registry go
section, index/log entries.
Backend tests 3611/3611, web vitest 651/651, tsc clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
10308664dd | small UI fix in session list entry for started | ||
|
|
21b8e054df |
docs: correct portal-prefetch root cause (h2, server-side queue) + measured outcome
The HTTP/1.1 / six-connection-cap claim was wrong — it came from the
Schannel curl build that cannot speak HTTP/2. ALPN (openssl s_client)
and .NET HttpClient confirm the portal negotiates h2; the queue is
server-side in the SWA SSR runtime. Doc, index, log and the
GlobalSidebar comment now describe the real mechanism.
Adds the 2026-07-28 outcome section: Standard plan changed nothing
(stratified p50s match, Mann-Whitney p=0.81); prefetch={false} removed
the freezes (worst totalDuration 225s -> 4.9s) and also removed an
accidental keep-alive (~150ms added to cold navigations).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v2.0.1385
|
||
|
|
249c20e15a |
feat: mark backfilled pre-agent events as history in timeline + agent summary event
Customer feedback: backfilled modern_deployment_error events (pre-agent Autopilot event-log errors replayed at agent start) rendered as plain Error rows with one identical emission timestamp — indistinguishable from a live failure. Web (EventTimeline.tsx, effective for already-stored events): - "⟲ Backfilled" badge (slate, explanatory tooltip) on data.backfilled - row time now shows the original event-log timeCreated with a "(reported …)" suffix; metadata block gains a "Recorded" line; raw mode gets a compact ⟲ marker - severity deliberately NOT downgraded — unresolved pre-agent errors must stay visible in the Error filter Agent (ModernDeploymentTracker): - backfill scan buffers the capped record set, emits an Info summary event first (modern_deployment_log, backfillSummary: true + replayedCount/droppedOverCap/lookbackMinutes) announcing the block, then replays oldest-first so timeline sequence follows event-log chronology (cap semantics unchanged: newest 25 win) No new EventType, no ConfigVersion bump. Tests: summary emission x2, tracker subset 12/12; tsc + vitest 651/651 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4ebd60fb1e |
feat: EnableGatherRuleDebugLog — per-tenant gather-rule debug trace (ConfigVersion 36)
Customers cannot tell why a gather rule produces nothing in the timeline (e.g. a logparser rule with emit_mode on_change that never emits). New per-tenant boolean EnableGatherRuleDebugLog (EnableImeMatchLog-style plumbing end to end): when enabled, the agent writes %ProgramData%\AutopilotMonitor\Logs\gather_rules_debug.log with one pipe-delimited line per evaluation decision across all previously silent "produced nothing" exits — disabled rules, interval without intervalSeconds, first-tick-after-one-full-interval, phase-scope skips incl. currentPhase=Unknown, per-(rule, phase) dedup, empty collector results (emitOnlyIfExists), on_change hash suppression (hash prefix + streak), guard blocks, logparser per-file outcomes (position skips, format=text hint when every line fails CMTrace parsing, maxLines and wildcard caps), and full exception stacks the agent log drops. - Backend: ConfigVersion 35 -> 36; TenantConfiguration/AgentConfigResponse field + TableConfigRepository Store+Map - Agent: GatherRuleDebugLog writer (10 MB cap, one .old generation, failures swallowed, optional console echo), GatherRuleContext.DebugLog (null-safe no-op), trace points in executor + collectors; never writes under _scopeLock (reasons captured inside, written after release); merger bool->path projection with --gather-debug-log CLI override tolerance; deliberately NOT in the offline-cache strip lists; --run-gather-rules always traces; zero-rules delivery leaves a one-line file instead of nothing - Side fix: GatherRuleExecutorHost now passes the logger into InformationalEventPost so gather-event emissions hit the DEBUG log - Web: tenant settings toggle (Settings -> Agent) + admin tenant-config report rows - Tests: merger on/off/override, writer format+rotation, executor debug lines, logparser debug lines (agent 2131, backend 3561, tsc green) - Docs: docs/agent/gather-rule-debug-log.md + index/log entries Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
22093d0824 |
feat(agent): diagnostics upload at WhiteGlove Part-1 seal (preprov, V1 parity)
Upload the diagnostics package as a pre-provisioning intermediate when Part 1 seals (suffix "preprov"). No new config field: the existing mode semantics carry the desired behaviour — Always uploads at the seal, OnFailure skips (the seal terminates as Succeeded; a Part-1 failure goes through the regular Failed terminal path, which already uploads), Off stays off. The upload runs before the whiteglove_part1_complete emit so diagnostics_* events keep a lower sequence than the Part-1/Part-2 boundary marker. Best-effort: a fast reseal-reboot just loses the intermediate package. Backend needs nothing — ingest already stamps diagnostics_uploaded independent of terminal state (proven live by the on-demand collection path). Also make the on-demand path honest: CreateAndUploadAsync now takes bool? enrollmentSucceeded — null means "no outcome yet" (mid-session), so the OnFailure gate only skips a KNOWN success and sessioninfo.txt records "In Progress" instead of the former enrollmentSucceeded:false workaround that stamped in-flight packages "Failed". 10 new tests (WG seal mode matrix, preprov suffix, event ordering vs the Part-1 marker, unchanged Part-1 exit contract; nullable-outcome gate matrix via HTTP-free sentinel, sessioninfo labels). Agent suite 2112/2112 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dff1532be5 |
feat: project ConnectionType (WiFi/Ethernet) onto SessionsIndex
The agent already reports connectionType in every network_interface_info event, but it was only stored as a JSON blob in DeviceSnapshot — answering "how many machines enroll over Ethernet vs WiFi?" required a slow full-table scan via deviceProperties. Project it as a typed column instead (template: the AvgApiLatencyMs chain), making it a cheap indexed one-call query. - Ingest: TryExtractConnectionType — last network_interface_info emission of the batch wins (a device switching media mid-enrollment is relabeled); only exact "WiFi"/"Ethernet" accepted, fire-and-forget fail-soft writer - Storage: UpdateSessionConnectionTypeAsync (Merge on Sessions + index mirror, tombstone-safe), BuildSessionIndexEntity superset entry, MapToSessionSummary, re-registration preserve in StoreSessionAsync - Search: connectionType= filter with OData eq push-down on the index, client-side backstop on the non-OData paths, fields=connectionType projection, pagination fingerprint entry, raw endpoint parity - MCP: connectionType z.enum param on search_sessions/query_raw_sessions, fields doc updated (server redeploy required) - Web: optional sortable/filterable "Connection" column in the session list - No backfill: legacy sessions lack the column and are excluded by the filter (intended, same semantics as RebootCount); the deviceProperties scan path still covers historical data Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8608ee6272 |
feat(agent): netsh WiFi fallback labels for all enrollment-geography languages
Derived the language set from actual enrollment geography (get_geographic_metrics, 365d, 10k sessions across ~50 countries) and documented the country->language mapping on the key arrays. Newly covered: Finnish, Hungarian, Lithuanian, Latvian, Romanian, Croatian/ Bosnian, Slovak, Indonesian/Malay, Vietnamese, Thai, Arabic, Bulgarian radio-type variant and Swedish "Radiotyp". New fi/id parser tests; the unknown-language tests now use Greek (outside the geography set). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>v2.0.1384 |
||
|
|
f762b932a2 |
feat(agent): CJK + Russian labels in netsh WiFi fallback parser
Extend the localized label sets with the remaining languages seen in enrollments: Russian, Japanese, Chinese (Simplified + Traditional, incl. the 信道/通道 channel variants) and Korean (incl. the two radio- type spellings). SSID stays untranslated everywhere. New zh-CN and ko parser tests; suite green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
173bfc40a9 |
feat(agent): multi-language label support in netsh WiFi fallback
The netsh fallback previously matched English labels only, silently dropping fields on localized OS images. The parser now matches the label variants of the languages commonly seen in enrollments (EN/DE/FR/ES/PT/IT/NL/PL/CS/TR; Scandinavian locales reuse the EN/DE spellings) and is split into a pure Parse(output) function covered by unit tests (EN/DE/ES samples, "94 %" spacing, AP-BSSID collision, unknown-language degradation - SSID itself is untranslated in all locales). Unknown labels still fall through harmlessly; the native WLAN API remains the primary path. V2.Core suite 2095/2095 green; fallback live-verified against netsh on dev hardware (identical values to the API path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
609d9c47af |
fix(agent): netsh fallback + step diagnostics for WiFi info collection
Field regression on 2.0.1383 (session f7c56065, HP ProBook 450 G7, build 26200.8037): the native WLAN API path returned nothing in the OOBE/SYSTEM context and the null path was silent, so wifi_signal_info disappeared entirely - while netsh-based 2.0.1380/1382 sessions on the same build kept reporting SSID/signal the same morning. Suspected (not yet proven) cause: Wi-Fi location gating of WlanQueryInterface on 24H2+; inbox netsh is unaffected. - WifiInfoReader: new TryGetCurrentConnection overload reporting WHY the read yielded nothing (native rc per step, interface state); callers log it at Debug so the agent log explains missing WiFi info. - NetshWifiFallback (new): parses netsh wlan show interfaces ONLY when the WLAN API yields nothing on a WiFi-connected device; SystemPaths. Netsh restored for it. - WifiConnectionInfo.SignalPercent is now int? (netsh fallback cannot always parse localized "Signal"); event payload emits the field only when present - same shape as before. V2.Core suite 2085/2085 green; interactive positive path re-verified (SSID/94%/802.11ax/ch116, empty diagnostics). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |