mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
v2.0.1389
2196 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
eb94c612e7 |
fix(agent): gather phase/event triggers follow the engine-reduced timeline
phase_change/phase_exit/on_event gather triggers (and the phase-scope gates) were driven by the pre-reduce SignalIngress.SignalPosted stream, so any raw signal carrying an EspPhase payload fired them before the decision engine had applied its gates. In session 32312a32 (RealmJoin tenant) a Phase Start rule on FinalizingSetup fired at the raw ESP-exit EspPhaseChanged(FinalizingSetup) signal, 7 minutes before the engine's RealmJoin-gated phase_transition(FinalizingSetup) - and read HKLM\SOFTWARE\RealmJoin\Custom\BIOS before the RealmJoin package that creates it had run (exists=false). GatherRuleExecutorHost now subscribes to the new TimelineEventStream: EventTimelineEmitter publishes every emitted event's (eventType, phase) post-reduce, so phase triggers fire exactly when the UI timeline shows the phase, and on_event sees engine-emitted event types (enrollment_complete, phase_transition, realmjoin_resolved, ...) that never existed on the raw signal stream - restoring V1 semantics and the documented "On Event with enrollment_complete" pattern. Raw-signal consumers with OS-state semantics (DeviceInfoHost, UserEspKeepAwakeHost, ProvisioningPackageHost) stay on the raw feed deliberately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>v2.0.1389 |
||
|
|
ed5fcf3dc3 |
chore(deps): bump transitive npm deps to resolve Dependabot alerts
- mcp: hono 4.12.31->4.13.0 (ReDoS in CORS middleware), fast-uri 3.1.4->3.1.5 (host confusion), ip-address 10.1.0->10.4.0 (SSRF bypasses), postcss 8.5.21->8.5.25 (sourceMappingURL file read) - web: postcss 8.5.22->8.5.25, brace-expansion (DoS, npm audit) Lockfile-only changes within existing semver ranges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9ba67162aa |
fix: session duration inflated by inherited tz-skewed IME log timestamps (#123)
Session df1fcf47 (WG Part 1, real ~1h16m) reported 8h15m: one do_telemetry
event inherited an unrelated, 8h-shifted IME log line timestamp via
LastMatchedLogTimestamp, and the backend re-anchored StartedAt on it.
Three layers:
- Agent: OnDoTelemetryReceived now carries the source-line timestamp; the
DO-collector poll path passes null and the adapter stamps the clock
instead of inheriting whatever line matched last.
- Backend: StartedAt alignment and duration-start selection reject
earliest-event timestamps more than 2h before the current anchor
(MaxStartedAtBackwardShift), on all four mutation sites incl. the
SessionsIndex dual-write decision.
- Parser: CMTrace time fields with a UTC-bias suffix ("+480") now parse
and honor the writer-declared offset instead of failing to match.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
v2.0.1388
|
||
|
|
efc96258d6 |
chore(deps-dev): bump the web group (#122)
Bumps the web group in /src/Web/autopilot-monitor-web with 2 updates: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom). Updates `@types/react` from 19.2.17 to 19.2.18 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@types/react-dom` from 19.2.3 to 19.2.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.2.18 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: web - dependency-name: "@types/react-dom" dependency-version: 19.2.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: web ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
9d8b0fc254 |
feat: tenant-config field schema - MCP writes get types right on the first attempt (#121)
Follow-up to the E2E finding (95 vs 95.0): a model driving update_tenant_config had to guess field names and JSON types from a config read. New reflection- generated schema surface makes guessing unnecessary: - Backend: GET config/fields-schema (GlobalAdminOnly) - built from the TenantConfiguration model + the patch service deny-lists at runtime, so it can NEVER drift from what the patch endpoint enforces. Per field: camelCase name, JSON type (string/boolean/integer/number), nullability (null clears), format hints (json / date-time), writability + deny reason, GA-only marker (phase 2), revert-protected marker. Literal route; FindPolicy literal-over-template precedence pinned by test (same mechanism as config/all). - MCP: new strictGa READ_ONLY tool get_tenant_config_schema; update_tenant_config description + GA instructions headline steer the model to call it before composing a patch. - Tests: drift canary (every model property appears exactly once), deny-list consistency, type exemplars incl. the decimal SLA column class, policy + route-precedence pins, MCP catalog/registration/request-shape. Backend 3854 green, vitest 465 green, tsc clean. Deploy order: backend first, then MCP redeploy. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f3028041af |
feat(web): tenant list shows plan badge (Community/Pro) instead of Preview
The green "Preview" badge was the activation-gate whitelist marker from the preview era. With public availability + auto-approve live it carried no information. Rows now show the plan tier (legacy "enterprise" resolves to Pro, everything else Community); the amber "Waitlist" badge still appears for unapproved tenants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3628c7bc79 |
fix(backend): revert rehydration - whole-valued double columns must not become Int32
Prod finding during the first live E2E (gktatooine): revert_tenant_config failed fail-closed with "could not be rehydrated". Root cause: JSON cannot distinguish 95.0 from 95, so the snapshot rehydrator turned the whole-valued SLA rate columns (decimal? in the model, double in storage) into Int32 - and TableEntity.GetDouble throws on the type mismatch. The rehydrator now forces GetDouble for columns the model declares as decimal/double/float, mirroring the existing DateTime handling. The roundtrip canary test is hardened into a reflection filler that sets EVERY writable property non-default (with a deliberately whole-numbered decimal), so no column type can escape the fidelity check again; plus a focused pin on the exact prod failure. Suite 3849 green. No data was harmed: the fail-closed guard refused the write, the snapshot itself is intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
395fdd49df |
Transactional tenant-config writes via MCP + ConfigurationBackups safety net (#120)
* feat(backend): pre-write config backup net - every tenant/admin config save snapshots first New ConfigurationBackups table (PK=tenantId or GlobalConfig, RK=reverse-ticks+guid, newest-first) holds the raw stored row as EntityJson before any overwriting save of TenantConfiguration or AdminConfiguration. Hook lives in TableConfigRepository, the single choke point all writers funnel through - fail-soft (a backup hiccup never breaks a save), skips row creations and noise-only changes (LastAuthClientId flips), prunes to the newest 2 per partition. Source/reason tagging via a new SaveTenantConfigurationAsync overload (not optional params - Moq expression trees, CS0854); portal-put/plan/auth/app-homing/offboard call sites tagged. Offboarding: backup partition wiped in the bulk phase (TenantPartitionTables) and by the marker-cleanup tombstone sweep; the GlobalConfig partition never matches a tenant GUID and survives. Table registered in TableNames.All + CriticalBackupTables.All. Groundwork for the transactional MCP config-write flow (patch/revert endpoints follow). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(backend): transactional tenant-config field patch + revert with verified writes New GA-only surface (policy catalog: GlobalAdminOnly + RouteParam): - PATCH config/{tenantId}/fields - field-level patch through TenantConfigPatchService: fresh ETag read -> field gate (deny-list incl. plan/trial, HomedAppClientId, auth provenance; unknown fields 400 with the name) -> patch onto a clone -> shared validation -> fail-CLOSED backup -> CAS replace -> fresh re-read -> verify that EXACTLY the intended fields changed -> automatic ETag-conditional rollback on drift (catches Store/Map serialization asymmetry). - POST config/{tenantId}/revert - restore from a ConfigurationBackups snapshot (latest by default); snapshots current state first (revert is revertible), preserves plan/homing/provenance fields unless includeProtectedFields. - GET config/{tenantId}/backups - metadata + masked diff only, never EntityJson. Supporting changes: IConfigRepository gains GetTenantConfigurationWithEtagAsync + TryReplaceTenantConfigurationAsync (412 -> false, no implicit backup hook); validators moved from the PUT into shared TenantConfigValidation (PUT re-pointed, forwarding shims keep test call sites); GET config/{tenantId} honors ?view=redacted so a GA's MCP session never pulls clear-text secrets into model context. Caller-tier deny-lists (TenantAdmin/DelegatedAdmin) ship now for the phase-2 flip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(mcp): tenant-config write tools - update/revert with snapshot-backed verified writes First mutating surface on the MCP server, strictGa (real Global Admin) only: - get_tenant_config: full config read, ALWAYS ?view=redacted (secrets never reach model context; pinned by config-write-tools tests) - update_tenant_config: field-level patch via PATCH config/{tenantId}/fields; required reason; response = applied fields + masked diff + backupId - list_tenant_config_backups: snapshot metadata + masked diff only - revert_tenant_config: restore latest/named snapshot; protected fields stay current unless includeProtectedFields; revert is itself revertible New MUTATING annotation + TenantGuidSchema (path-traversal guard) in shared.ts; role-aware instructions headline (non-GA keeps the exact READ-ONLY wording); catalog snapshot test extends GA_FULL + new CONFIG_WRITE_GA_STRICT difference list (Global Reader/tenant/delegated see none of the four). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(backend): de-flake session-deletion heartbeat test - condition-gated handler instead of wall-clock window The heartbeat test held the handler for a FIXED 350ms and expected the 50ms heartbeat timer to tick >=3 times inside that window. On loaded CI runners the heartbeat Task.Delay stretches disproportionately to the handler delay, the handler completes with <3 heartbeats fired, and the awaited condition can never become true afterwards (CI failure on PR #120). The handler now holds the message in-flight UNTIL the test observed 3 heartbeats (with ct + own 15s deadline as termination guards), so the assertion is reached deterministically regardless of scheduler jitter. Verified: 12/12 local runs green, full suite green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5edb64f3c9 |
fix(web): confirm-dialog errors surface in-dialog + dark-mode sheet gaps
The GA offboard and app-homing confirm dialogs reported failures via the
admin page-level error banner, which sits behind the tenant editor modal
(z-50) and the dialogs themselves (z-[60]) - the user saw nothing. Both
dialogs now take an error prop and render the failure inline; the section
routes homing/offboard failures into dedicated states cleared on open.
Dark mode: class-by-class audit of the offboard UI against the global
override sheet. The dialogs were already covered; the real gaps were
missing family members in globals.css - border-{red,amber,green,purple}-100,
border-{blue,sky,yellow,rose,emerald}-300, and text-red-600/700 (mapped to
red-400, mirroring the cyan/rose 600-to-400 convention).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
46965af290 |
feat(backend): finalize + arm the offboarding farewell email
Replace the [DRAFT] placeholder with the final copy (thank-you, gentle feedback pointer to GitHub/LinkedIn, welcome-back note, last-email notice) and remove the OffboardFarewellEmailArmed scaffolding gate. Tests move from the disarmed-by-default contract to the armed contract (gate ordering, never-throw, template pins). GitHub links in both email templates now point to the renamed repo okieselbach/AutopilotMonitor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1bfdb68ff6 | Merge branch 'main' of https://github.com/okieselbach/Autopilot-Monitor | ||
|
|
5ee5114d5c | updated modification datetimes page | ||
|
|
a7bb7ebf15 |
feat: Pro requires a tenant contact address - gated at plan entry, never as a runtime lockout
Backend: POST config/{id}/trial now denies with 409 ContactEmailRequired
when no contact address is stored (extracted EvaluateTrialStart verdict,
order test-pinned: consumed/already-Pro win over the contact prompt). The
gate is live at the API even while the trial CTA stays behind its
"coming soon" flag; GA plan assignment stays unblocked by design. The
feature-flags payload gains contactEmailSet (bool only, never the
address - contract test updated).
Web: trial CTA shows a "set a contact address first" note instead of the
start button when the flag flips on; dashboard gets an amber non-blocking
banner for Pro tenants without a contact (admins only, fail-safe: only an
explicit contactEmailSet:false nags, so loading/error/older-backend states
stay quiet); GA tenant editor warns when assigning Pro to a tenant
without a contact address.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
a7a1cc43c1 |
fix(backend): welcome mail survives the auto-approve race - send-on-save + once-only marker
First production auto-approve (mueller-steinag.ch) activated 64s after signup, 5s before the user saved their notification address on the activation page - the approval path found no address and silently skipped the welcome mail. Both racing paths now write-then-read: the notification-email save path checks approval fresh (bypassing the 30s negative cache) and triggers the send when activation already happened. A conditional-insert welcome-email-sent marker in PreviewWhitelist makes the send once-only across both paths; it is consumed strictly after the address check so it can never be burned without an address. Revoke clears the marker, the GA resend endpoint sends unconditionally and consumes it best-effort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2a5ad65dd7 |
fix(web): explicit JSX spaces where Turbopack eats them - "onlyto" class of typos
Turbopack drops the ambient space between an inline element and adjacent
prose in certain multi-line JSX text layouts (Babel/SWC preserve it), so
the live portal rendered "onlyto reach you", "IsEnabledflag",
"...ps1script", "5 minutesfor GitHub's", "Unavailableand the cascade".
User-reported on the Contact section; the other five sites were found by
scanning the compiled bundle for space-less text nodes after elements.
Fix: explicit {" "} at each affected boundary - it compiles to its own
text-node and cannot be trimmed. Verified in a fresh build: all six sites
now emit ..."})," ","to reach... Sites whose gap comes from ml-*/mx-*
margins or flex gap were left alone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
960b3d8ed9 |
chore(deps): bump github/codeql-action from 4 to 4.37.3 (#119)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4...v4.37.3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
86bb255b11 |
feat(web): GA offboard button in tenant management - with type-to-confirm and ban-semantics note
The admin tenant editor gains a danger zone that queues the existing
offboarding cascade (DELETE tenants/{id}/offboard, policy already admits
GA). Guarded by a type-OFFBOARD confirmation dialog so a stray click can
never trigger it, Global Admin included.
Both the suspension block and the danger zone now spell out the
semantics: suspension is the durable lock-out lever (blocks sign-in and
auto-activation, data stays); offboarding is NOT a ban - the cascade
deletes the config row including any suspension, so a completed offboard
lets the tenant re-onboard and auto-activate on the next sign-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
d3a10dfb96 |
fix(backend): tenant activation is race-proof - conditional insert dedupes welcome mail and ops event
Double signup on two instances enqueued two auto-approve envelopes; both workers could pass the approved-check (stale 30s negative cache) and both ran the activation side effects - duplicate welcome mail plus duplicate TenantAutoApproved ops event/Telegram ping. Same duplicate on queue redelivery after a failed message delete. The whitelist add is now a conditional INSERT (AddEntity, 409 = already approved) so storage arbitrates concurrent activations: exactly one caller sees "newly added" and runs mail/promote/ops event; losers drop quietly. The GA approve endpoint stays idempotent (200 + alreadyApproved instead of a duplicate mail on re-approve). Also fixes a silent-failure path: the repo swallowed storage errors and returned false, which ApproveAsync ignored - a failed whitelist write still sent the welcome mail and reported success. Storage errors now throw (queue path retries, HTTP path 500s). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
80d0b30cba |
fix(backend): activation-gate review findings - OnboardedBy immutable via PUT, suspension gate reads fresh
Two findings from the re-run public-availability review: - UpdateTenantConfigurationFunction preserved HomedAppClientId, LastAuth* and plan fields against round-trip clobber but not OnboardedBy, despite AuthFunction documenting it as write-once: any own-tenant admin PUT could overwrite or null it, corrupting audit provenance and letting a revoke -> re-approve cycle auto-promote an attacker-chosen UPN. Now preserved like the other system-owned fields. - TenantAutoApproveHandler checked the suspension gate through the cached TryGetConfigurationAsync (5-min per-instance TTL): a tenant suspended right after signup could still be auto-approved from a stale view. Now reads via GetConfigurationFreshAsync; a storage error propagates so the message retries (flag re-checked per attempt) instead of silently downgrading the tenant to manual approval. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
aee723e497 |
fix(backend): app-homing flip reads config fresh - stale cache could rewind concurrent writes
FlipAsync read the tenant config through the cached GetConfigurationAsync (5-min per-instance TTL, fail-open to CreateDefault) and saved the whole entity back: a flip could silently rewind fields another writer changed within the TTL, and a transient storage read error would have persisted a default config over the real row. Now reads via GetConfigurationFreshAsync (cache-bypassing, errors propagate) and aborts loudly when no config row exists instead of materializing a default one. Daily-review finding 2026-07-31. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
427c923530 |
feat(web): mobile menu for landing navbar - sign in was unreachable on phones
Below md the landing navbar only showed the Get started CTA: Sign in was hidden sm:block and the nav links (Product/Capabilities/Compare/Docs) had no mobile counterpart at all. Returning users on a phone had no visible way to log in. - burger toggle (<md) opens a solid panel under the bar: nav links, GitHub, and a prominent Sign in button; closes on link click, backdrop tap, and Escape - Get started stays always visible in the top bar; GitHub icon and Sign in text button move into the menu below md - panel uses opaque --lp-surface: nested backdrop-blur inside the nav does not blur page content, a translucent panel let the hero bleed through - 320px fit: px-4 below sm and the wordmark hides below 360px (mark only) Verified via Playwright at 320/390/1280px: menu open/close paths, anchor scroll to #features, desktop layout unchanged; tsc and ESLint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b46a8f1571 |
fix(rules): merge collision guard - stale tenant rule copies no longer crash rule notifications
Tenants seeded in the pre-global-partition era can still carry per-tenant copies of built-in rules (IsBuiltIn=false debris, e.g. ANALYZE-ID-002 on one prod tenant). They pass the custom-rule filter and put the same RuleId twice into the merged list, blowing up ToDictionary(r => r.RuleId) in AnalyzeOnEnrollmentEndHandler.SafeNotifyRuleChannelsAsync and silently dropping rule channel notifications for that tenant. Guard the merge in AnalyzeRuleService + GatherRuleService: a tenant row whose RuleId collides with a merged global rule is skipped (global wins) with a warning log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6e294c010e |
fix(web): validation-gate toggles persist - disable was local state only
Prod report 2026-08-01 (tenant b54dc1af): corporate-identifier validation "came back on" after every disable. Audit log shows the truth: not a single True->False entry ever - the disable NEVER reached the server. The validation section has no save bar, and only the consent-driven ENABLE paths persisted; the toggle setters (disable via confirm dialog, and enabling the second gate while the first already carries the consent) wrote plain React state that silently reverted on the next config load. New TenantConfigContext.saveValidationGate routes those setter calls through the shared config PUT with explicit override values; local gate state re-syncs from the server response (or snaps back on failure), and the section save indicator covers the write. tsc clean, vitest 631/631, ESLint ratchet holds (115). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc54c9555c |
fix(web): correct Entra roles for admin consent - Application Administrator is not sufficient
The validation info box and the detect-access error still claimed Application Administrator could grant the consent. Per MS Learn (grant-admin-consent), Application/Cloud Application Administrator are excluded for Microsoft Graph app roles (application permissions) - our consent includes the app-only DeviceManagementServiceConfig.Read.All. Correct roles: Global Administrator or Privileged Role Administrator, matching the funnel banner and docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
784fd99240 |
fix(web): consent verify rides AAD propagation; funnel banner actions + dark mode
- Post-consent verification now POLLS the access-check (up to 5 attempts,
15s apart) instead of a single shot: right after a successful admin consent
the app-only token often mints with an EMPTY roles claim for 30-90s
(observed live: tenant 5ca2b350, GrantedRoleCount=0 at T+43s), which the
single-shot probe misreported as "permission not granted" while the homing
flip and consent were actually fine. The access-check invalidates and
re-mints fresh per call, so the poll converges as soon as Microsoft has
propagated the role; the consent spinner stays up meanwhile. Final "absent"
message now names propagation as the likely cause instead of sounding fatal.
- Funnel banner gains always-visible actions ("Grant consent for the new app"
+ "Detect existing access" on the autopilot gate, idempotent when already
enabled): tenants whose validations are already ON had no consent button to
click, and the per-toggle detect link under Corporate Identifier would
enable that validation as a side effect.
- Dark mode: funnel/flipped/legacy banners and the admins-only notice were
light-palette-only and unreadable on dark.
tsc clean, vitest 631/631, ESLint ratchet holds (115).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
607d6615b0 |
fix(auth): app-homing flip survives stale full-model config writers
Prod incident 2026-07-31 18:03Z (tenant b54dc1af): the consent-driven homing
flip landed and 600ms later the frontend persisted the validation toggle via
the generic config PUT - round-tripping a config loaded BEFORE the flip. The
GA branch treated the stale null HomedAppClientId as an intentional revert to
legacy, silently undoing the flip (App Insights: AppHomingFlipped 18:03:00.44,
PUT revert warning 18:03:01.03).
- HomedAppClientId is now IMMUTABLE via the generic PUT for ALL callers, GA
included: a full-model round-trip can never carry flip intent. The only
writers are the consent auto-flip and POST config/{tenantId}/app-homing
(which the GA tenant-management panel already uses).
- HandleAuthClientIdTrackingAsync (same failure class, cross-instance): the
last-seen tracking write now mutates a cache-BYPASSING fresh read via new
TenantConfigurationService.GetConfigurationFreshAsync instead of blind-saving
the up-to-5-min-stale cached view - the login right after a flip is exactly
the one that carries a changed audience and triggers this write.
3 regression tests (fresh-entity mutation preserves a concurrent flip,
fresh-already-current suppresses the write, steady state stays zero-I/O).
Backend xUnit 3767/3767.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
8d4f7ea0f9 |
fix(web): app-homing funnel banner - prominent card with permission hint + docs link
The plain info box was easy to overlook. Upgrade to a bordered gradient card with icon and heading, state that admin consent needs a tenant-wide-consent role (Global Administrator / Privileged Role Administrator), and link the portal-setup docs article describing the required permissions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0e2d620ad4 |
feat(auth): self-service app-homing flip via consent flow + GA lever
Re-consent, verify and flip collapse into the existing consent flow: the
consent URL funnels legacy-homed tenants to the primary app registration,
and consent-status/access-check verify admin consent via an app-only token
probe under explicit primary credentials, then auto-flip HomedAppClientId.
- AdminConfiguration.SelfServiceAppHomingEnabled: kill switch (default off,
cache-bypassing fail-closed read); gates funnel + auto-flip + tenant-admin
manual flip. GA path deliberately survives the kill switch.
- POST config/{tenantId}/app-homing (TenantAdminOrGA): tenant admins flip to
primary only, probe-gated; GA gets force + revert. Pure decision helper
EvaluateManualFlip shared by function and tests.
- GraphTokenService.GetAccessTokenForAppAsync: probe seam with explicit app
credentials, cache-safe via app-scoped token keys.
- EntraAppRolesEnabled does not block a flip; it emits a Warning ops event
(AppHomingFlippedWithEntraRoles) reminding to re-assign enterprise-app roles.
- Web: homing badge + "App Registration Homing" panel with confirm dialog in
GA tenant management (typed homedAppClientId hardens the PUT round-trip),
funnel/success banners in tenant settings with switchAuthApp re-login,
admin-config kill-switch round-trip, ops event types dual-registered.
- Browser telemetry (App Insights): pre-init event buffer so module-init auth
events survive; strategic events across the whole flow (app_homing_flipped,
consent_flow_*, consent_verify_failed, auth_app_fallback_*, auth_app_switched,
auth_app_homing_learned, app_homing_manual_flip*).
Backend must deploy before web (additive JSON fields); everything is inert
while EntraId:LegacyClientId is unset. Enable the kill switch in the admin
portal after the dual app-reg config swap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
69d61c6c26 |
feat(backend): dynamic poison-queue enumeration for health + ops monitoring
The static MonitoredPoisonQueues watch-list had already drifted twice (tenant-auto-approve-poison, session-deletion-maintenance-poison missing). Health check and maintenance watcher now enumerate every existing *-poison queue in the storage account at runtime; an absent queue has never had a poison-move and is healthy by definition, so enumeration covers everything a list would. Enumeration failure surfaces as warning (never a silent all-clear) on the health card and skips the maintenance tick. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cfb28d83b6 | added png logo | ||
|
|
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> |