Commit Graph

2640 Commits

Author SHA1 Message Date
Oliver Kieselbach 729ee56bde security: assume-breach alarm for the Global-Admin-only surface (x29 phase 0)
The 2026-09-04 audit found no bypass of the GA boundary, but a failed attempt left
only a Warning trace without identity, and a non-GA tools/call for a GA-only MCP tool
never reached the backend at all. This makes the boundary observable without adding
a second alarm writer (D-185):

Backend
- PrivilegedRouteDenied (Security): raised from the policy middleware's deny path for
  every 403 by an authenticated caller on a GlobalAdminOnly route. Critical for a
  caller without a platform role, Warning for a Global Reader. Details carry upn, oid,
  tid, client source, MCP tool name, correlation id, reason and policy. Throttled one
  hour per caller+path per instance (PrivilegedDenialReporter, IPrivilegedDenialReporter
  seam). PolicyResult now surfaces the denied tier; the DENIED log line gains oid, tid,
  clientSource and mcpTool.
- GET global/raw/access-probe: no-op GlobalAdminOnly route whose denial IS the signal
  (AccessProbeResponse DTO, catalog entry, parity test).
- Defense in depth: AppInsightsQueryFunction and both TableQueryFunction triggers re-check
  IsGlobalAdmin in the body (RawGlobalAdminGate); an empty RequestContext fails closed.
- Dual-register: OpsEventTypes.All, OpsAlertRulesSection.tsx, regenerated manifests.

MCP
- ga-tool-probe.ts: before dispatch, a caller without the GlobalAdmin role naming a
  strictGa tool leaves an unconditional [mcp-security] line (tool, upn, scope) and fires
  the access probe with X-MCP-Tool-Name; the SDK answer stays "Tool not found".
- GA_STRICT_TOOL_NAMES exported and pinned equal to the registered GA-minus-Reader set.
- debug-session prompt names query_backend_logs/query_table only for strictGa (Readers
  were told to call tools they cannot see); tool_call_rejected lines carry scope + upn;
  config-tool comment corrected against the actual catalog tiers.

Tests: reporter (severity, details, throttle, fail-soft), middleware policy surfacing and
IsPrivilegedDenial/BuildPrivilegedDenial, Program.cs middleware order source scan, raw
gate 403, probe catalog + JWT pins, wire parity; MCP probe unit tests, Global Reader over
HTTP on both eras, runtime refusal fires the probe, prompt gate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 13:29:06 +02:00
Oliver Kieselbach f0a3f04398 fix(agent,rules): AccountSetup activity means observed progress; same-event filter for event_data conditions (q8n)
Agent: ProvisioningStatusTracker.HasAccountSetupActivity now requires at least one
AccountSetup subcategory past notStarted. Windows writes AccountSetupCategory.Status with
every subcategory notStarted BEFORE the Device-ESP page exits, so the presence check
confirmed the Device->Account handoff (Shell-Core 62407) as the final ESP exit; the
user-apps-settled synthesis then completed AccountSetup and released the User-ESP
keep-awake minutes before the real exit (fleet: 77 % of keep-awake sessions, median 3 min;
on battery that is enough for Modern Standby). D-183.

Rule engine: filterField/filterOperator/filterValue now apply to event_data and
event_data_array as a same-event filter (shared ApplyValueFilter with event_count).
ANALYZE-DEV-012 v1.1.0 excludes backfilled pre-session episodes (backfilled equals false);
a second independent condition could not, since each condition scans all events on its
own. D-184. Schema, Shared model, MCP authoring guide and generated types follow.

Tests: ProvisioningStatusTrackerAccountSetupActivityTests (the verbatim all-notStarted
registry JSON), SleepEpisodeRuleTests (+3 backfill cases), RuleEngineEventDataFilterTests.
Solution build 0 warnings; agent suite 2820 and backend suite 5791 green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 13:11:28 +02:00
Oliver Kieselbach 49013c3cc4 chore: bump internal gitlink (cockpit path links)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 12:59:27 +02:00
Oliver Kieselbach 32cbc3620e chore: bump internal gitlink (cockpit reader view)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 12:53:30 +02:00
Oliver Kieselbach ae9c1b0744 chore: bump internal gitlink (cockpit edit)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 12:42:17 +02:00
Oliver Kieselbach 9eb8cac507 ci: skip CI and CodeQL on gitlink bumps and prose-only commits
Every "chore: bump internal gitlink" commit ran the full CI matrix and
both CodeQL analyses, because a gitlink bump reports the bare path
'internal' (no trailing slash) and the existing 'internal/**' filter
does not match it. Both workflows now ignore the bare and nested forms
of the three private submodule paths plus markdown, LICENSE and the
.github metadata files. CodeQL's weekly unfiltered schedule remains
the backstop.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
v2.0.1447
2026-09-04 05:00:25 +02:00
Oliver Kieselbach 69ed763640 chore: bump internal gitlink (cockpit stats tiles) 2026-09-04 04:51:53 +02:00
Oliver Kieselbach 369650aeeb chore: bump internal gitlink (cockpit chrome) 2026-09-04 04:47:15 +02:00
Oliver Kieselbach 0280366cc3 chore: bump internal gitlink (lesson) 2026-09-04 04:36:38 +02:00
Oliver Kieselbach ac2b52cc4d chore: bump internal gitlink (todo cleared) 2026-09-04 04:36:09 +02:00
Oliver Kieselbach 8faa304c94 chore: trim public CLAUDE.md to the public half; bump internal gitlink 2026-09-04 04:32:57 +02:00
Oliver Kieselbach 95817f1296 chore: bump internal gitlink (cockpit live file view) 2026-09-04 04:30:24 +02:00
Oliver Kieselbach e6981bad99 chore: bump internal gitlink (cockpit browse) 2026-09-04 04:26:14 +02:00
Oliver Kieselbach 99717f03e2 chore: bump internal gitlink (cockpit file view) 2026-09-04 04:18:06 +02:00
Oliver Kieselbach 04f53b882c chore: bump internal gitlink (cockpit date view) 2026-09-04 04:10:38 +02:00
Oliver Kieselbach 558153f29c chore: bump internal gitlink (cockpit decisions view) 2026-09-04 04:07:50 +02:00
Oliver Kieselbach 91ba9d24a1 chore: bump internal gitlink 2026-09-04 03:54:12 +02:00
Oliver Kieselbach e8ecb5537c chore: bump internal gitlink (backlog ids) 2026-09-04 03:54:02 +02:00
Oliver Kieselbach 6e4563b2a0 chore: bump internal gitlink (backlog cockpit) 2026-09-04 03:31:47 +02:00
Oliver Kieselbach 435aef867c CLAUDE: public half keeps directives, knowledge map, hard rules and code quality; process, working files, OKF, customer-claims detail live in the private half
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 03:15:26 +02:00
Oliver Kieselbach e76f312adb chore: bump internal gitlink (rules moved into the private instruction half) 2026-09-04 03:13:44 +02:00
Oliver Kieselbach eac470e781 chore: bump internal gitlink (lesson) 2026-09-04 03:12:51 +02:00
Oliver Kieselbach 685a86215b chore: bump skills and internal gitlinks (skills rework 20 -> 16, MCP-first) 2026-09-04 03:12:28 +02:00
Oliver Kieselbach 8a259241b5 chore: operator scripts leave the public tree (versioned in the private infra submodule); scripts/ keeps only Bootstrap and CustomerSetup 2026-09-04 02:43:23 +02:00
Oliver Kieselbach fd41dded3d chore: bump internal gitlink (private instructions: operations facts) 2026-09-04 02:23:47 +02:00
Oliver Kieselbach db020789e1 chore: private submodule docs/ becomes internal/ (knowledge + decisions + work files + private instructions)
- .gitmodules: docs -> internal, repo renamed to Autopilot-Monitor-internal; the OKF bundle now lives at internal/docs/
- .claude/CLAUDE.md rewritten: where knowledge lives, working-file rules, hard rules; imports the private half from internal/CLAUDE.md (silently absent without submodule access)
- .claude/architecture-principles.md moved into the internal bundle (reconciled against the code)
- tasks/ (gitignored) retired; working files live in internal/work/
- doc path references docs/<bundle> -> internal/docs/<bundle> in code comments, skills gitlink, codeql paths, build script
- rule descriptions no longer cite an internal doc path (customer-visible text); rules/dist regenerated
- public tree no longer names private operator tooling
- .claude/commands gitlink: skill docs point at the new paths
2026-09-04 02:19:00 +02:00
Oliver Kieselbach 186c151046 fix(web): tenant filter resolves a typed domain name, session search matches tenant domain and ID
A domain typed into the cross-tenant filter only became a tenant ID when a suggestion was
picked; submitted raw it was dropped by the fetch (not a GUID) while the client scope compared
it against session tenant IDs, leaving zero rows and "No sessions found for this tenant yet".
Submit now resolves GUID / exact domain / unique substring, updates the box to the ID, fetches
with the resolved value, and the empty state says "No tenant matches" for unresolvable input.

The search box never matched the tenant column: tenant ID and domain are now part of the
searchable text and the suggestion dropdown (cross-tenant only), sourced from one
tenantId-to-domain map shared by table and filter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 18:20:43 +02:00
Oliver Kieselbach c4c253a03a feat(app-homing): name the add-on roles that block the self-service switch and hand the admin the fix
Two tenants clicked the migration banner, consented the new app, and were refused by the
role-superset probe without ever learning why: the previous app's service principal holds a
tenant-side add-on grant (DeviceManagementScripts.Read.All) that no re-consent can produce, and
the grant command on the Optional Graph capabilities page pre-fills the homed (previous) app.

Backend: the probe now separates manifest roles (still propagating after consent ⇒ transient,
the retry converges) from add-on roles (permanent ⇒ MissingRoles names them). consent-status and
access-check carry appHomingPending + appHomingMissingRoles; the manual flip's probe payload
carries missingRoles. The access-check invalidates the Graph caches BEFORE the homing probe so
"Detect existing access" judges a fresh primary token after the admin's grant.

Web: TenantConfigContext learns the blocking roles from either response and the post-consent
poll keeps going while the switch is still converging (previously it stopped on the first
legacy-side reconcile and left the tenant un-switched). Settings → Autopilot Validation shows a
"One more step" banner with the roles, the grant command pre-filled with the NEW app's client id
and -Permissions, and the re-check button; the admin flip error names the roles too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 17:08:03 +02:00
Oliver Kieselbach f833882e07 fix(ops): AgentEmergencyBreak ops event severity follows session state at break time
The agent's 48h emergency break is wall-clock, so devices powered on after a shelf
period fire it days after the sweep already terminalized the session (2026-09-03
audit: 224 of 269 breaks in three days were such late cleanups from one kiosk tenant,
all Warning, burying the real ones).

- ReportAgentErrorFunction.ClassifyEmergencyBreak: session already terminal
  (Succeeded/Failed/Incomplete) => Info + lateCleanup=true; open, unknown or
  unreadable => Warning (historical semantics). Row read is best-effort.
- OpsEventService.RecordAgentEmergencyBreakAsync takes the severity and stamps
  sessionStatusAtBreak/lateCleanup into message and details.
- Timeline event agent_emergency_break stays Warning (classifier rule 3 keys on type).
- SessionOwnerBindingObserver: document that the ops-event throttle is per Function
  instance (IMemoryCache) and deliberately so.
- Tests for both branches, the fail-safe path and the pure rule; docs submodule bumped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 16:03:47 +02:00
Oliver Kieselbach 0505e5d5bd feat(web): switchable color modes and legend on the geographic performance map
The Performance Map gets a "Color by" selector (enrollment duration, success rate,
API latency, DO peer efficiency, App-Load-Score) and a legend under the map. One
bucket definition per mode (mapColorModes.ts) feeds both the Leaflet markers and
the Location Performance table badges, so map, legend and table share thresholds.
The map's duration scale moves to the table's 0.8/1.0/1.2/1.5 ladder.

Also: page.tsx uses the generated wire types instead of drifted local interfaces,
formatThroughput comes from lib/formatting, marker tooltips show latency and DO share.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 15:56:07 +02:00
Oliver Kieselbach a4140c4e4c docs(readme): refresh screenshots, add social preview source
The four README screenshots were still from the private preview (old blue
logo, preview banner). Retaken in the light theme with the current logo,
tenant view only, no banner. Fleet Health uses the 90-day window; the geo
map has too few locations in the own tenant to read as a world map.

Also stores the GitHub social preview as source (HTML composition plus the
anonymized dashboard capture it embeds) and the rendered 1280x640 PNG.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 14:50:19 +02:00
Oliver Kieselbach 0e928b5506 Merge branch 'main' of https://github.com/okieselbach/AutopilotMonitor 2026-09-03 14:29:28 +02:00
Oliver Kieselbach ce9efb6f6c docs(repo): AGPL-3.0 root license, MCP section in README, security policy and issue forms
- Root LICENSE is now the verbatim AGPL-3.0 text so GitHub detects the license
  instead of showing "Other"; the split model (MIT for Agent + Shared) is
  explained in the README and each component keeps its own license file.
- README: MCP server in the component list, an "AI Integration (MCP)" section
  with example prompts and the server URL, and a neutral Feedback section.
- SECURITY.md routes reports to private GitHub Security Advisories.
- CONTRIBUTING.md is a feedback routing page (issues, discussions, security,
  Report Session button), CODE_OF_CONDUCT.md is short and neutral.
- Issue forms for bug reports and feature requests; blank issues disabled.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 14:29:14 +02:00
dependabot[bot] 73961f67f2 build(deps-dev): bump @humanfs/node (#146)
Bumps the web-security group with 1 update in the /src/Web/autopilot-monitor-web directory: [@humanfs/node](https://github.com/humanwhocodes/humanfs/tree/HEAD/packages/node).


Updates `@humanfs/node` from 0.16.7 to 0.16.8
- [Release notes](https://github.com/humanwhocodes/humanfs/releases)
- [Changelog](https://github.com/humanwhocodes/humanfs/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/humanwhocodes/humanfs/commits/node-v0.16.8/packages/node)

---
updated-dependencies:
- dependency-name: "@humanfs/node"
  dependency-version: 0.16.8
  dependency-type: indirect
  dependency-group: web-security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 13:27:42 +02:00
dependabot[bot] 48fe68b87c build(deps): bump fast-uri in /src/McpServer/autopilot-monitor-mcp (#143)
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.5 to 3.1.7.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.5...v3.1.7)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 13:15:00 +02:00
dependabot[bot] f1820b2b43 build(deps): bump qs in /src/McpServer/autopilot-monitor-mcp (#144)
Bumps [qs](https://github.com/ljharb/qs) from 6.15.2 to 6.16.0.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.15.2...v6.16.0)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.16.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 13:14:46 +02:00
dependabot[bot] 78f810a326 build(deps): bump the mcp group (#145)
Bumps the mcp group in /src/McpServer/autopilot-monitor-mcp with 2 updates: [zod](https://github.com/colinhacks/zod) and [tsx](https://github.com/privatenumber/tsx).


Updates `zod` from 4.5.2 to 4.5.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.2...v4.5.4)

Updates `tsx` from 4.23.12 to 4.23.13
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.12...v4.23.13)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mcp
- dependency-name: tsx
  dependency-version: 4.23.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: mcp
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 13:14:29 +02:00
Oliver Kieselbach 9207b066db changed dependabot to batching 2026-09-03 13:06:38 +02:00
Oliver Kieselbach 0d41f50ace feat(web): answer-engine optimization — problem-framed FAQ on /about, entity graph JSON-LD, llms.txt, AI crawlers in robots
- /about: "Common Questions" section (15 user problems → feature answers) from one source array, mirrored as FAQPage JSON-LD
- root layout: SoftwareApplication JSON-LD becomes an @graph with Person (author/publisher), Organization glueckkanja AG (provider), WebSite, featureList, softwareHelp
- public/llms.txt with portal, docs, and source links
- robots: explicit allow block for GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Google-Extended
- drop the unverified "policy conflicts" claim from the analyze-rules feature card

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 00:41:06 +02:00
Oliver Kieselbach b294d2025d feat(plans): tenant-wide MCP usage-plan override + Community upgrade hint on quota 429
- TenantConfiguration.McpUsagePlanOverride: a SectionUsagePlans plan name pinned to a whole
  tenant (members' default user plan + organization windows); precedence per-user override >
  tenant override > edition default; never changes the edition
- PATCH config/{tenantId}/plan accepts mcpUsagePlan (validated against the definitions,
  400 on unknown names; null/blank clears), audited, echoed as mcpUsagePlan/mcpUsagePlanOverride
- Tenant Management: "MCP usage plan (override)" select fed by global/config/plan-tiers
- Quota 429: every Community block now says Community is sized for occasional use and Pro
  lifts the window; managed-tenant block names whose plan governs it
- Field in BaseDeniedFields/RevertProtectedFields/projection, Store/Map, manifests regenerated

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 23:51:48 +02:00
Oliver Kieselbach 12a75ef13d feat(delegation): MCP quota follows the managed tenant, delegated tenant slots, self-service delegation
Phase A — the budget follows the data
- A delegated (MSP) read is charged to the MANAGED tenant's organization MCP budget and checked against
  its plan; the caller's own budget still follows the home tenant. 429 names the managed tenant (domain)
  and the Pro upgrade path; new McpQuotaExceededResponse.targetTenantId.
- Bounded fleet aggregate (global/sessions, global/stats/sessions): exhausted managed tenants are excluded
  softly — RequestContext.AllowedTenantIds is narrowed (never widened, never null; pinned) and echoed as
  quotaExcludedTenants. Global Admins stay exempt and are tracked on their home tenant only.
- Tenant Group flag ChargeHomeTenantQuota (PATCH global/tenant-groups/{id}, audited per tenant): reads
  through the group are charged to the assignee's home tenant (operator-managed service groups).
- McpTenantUsage rows carry UserPrincipalName / HomeTenantId / LastRequestAt; new
  GET metrics/mcp-usage/organization (budget by account incl. delegated readers) rendered on MCP Usage.
- MCP: get_fleet_overview (delegated + platform) over the two subset-tier routes; error handler names
  the managed tenant. Other delegated tools stay tenant-mandatory.

Phase B — delegated tenant slots
- EditionEntitlements.MaxDelegatedTenants (Community 0 / Pro 2), TenantConfiguration.MaxDelegatedTenantsOverride
  (GA-only via PATCH config/{tenantId}/plan { maxDelegatedTenants }), DelegatedSlotService counting distinct
  managed tenants per home tenant through identity bindings (+ pending invitations + 24 h holds), enforced
  fresh on grant / group assign / add-tenant with 409 DelegatedSlotLimitReached; GA UI offers
  "raise to N & retry"; GET global/delegated-slots/{tenantId}; feature-flags entitlements.maxDelegatedTenants.

Phase C — self-service delegation
- One implicit Tenant Group msp-{tenantId} per managing tenant (OwnerTenantId on the meta row);
  DelegationInvitations table (invitations + slot release holds); DelegationInviteTicket (HMAC, 7 days,
  locates the row only — the accepting tenant is always the JWT tenant; one-shot via ETag).
- DelegationSelfService: ordered accept chain (distinct codes), remove/revoke with 24-hour slot hold, dual
  audit and live-stream disconnect, assignees bound to tenant membership, "who manages my tenant" view;
  GA release-hold escape hatch. No delegations/* route carries {tenantId} (pinned).
- Web: Settings → Tenant → Delegated Access, /delegations/accept?token=, AuthGate keeps the deep link
  across the activation detour, Tenant Management holds + release, owned groups read-only in GA UI.
- Offboarding wipes both sides (documented two-bucket membership); 90-day sweep of terminal rows.
- Trust pages (terms, privacy) and generated wire types updated; docs submodule bumped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 21:37:36 +02:00
Oliver Kieselbach 5494fd1862 fix(web): re-home hop peeks the post-login deep link instead of re-saving it
CodeQL flagged the re-save in AuthGate (js/clear-text-storage-of-sensitive-data,
alert #82): the value flowed back into sessionStorage from the consumed
return URL. The link only has to survive one more sign-in round trip, so the
re-home path now reads it without clearing and never writes it again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 17:26:28 +02:00
Oliver Kieselbach 6c6d746532 feat(web): small "Docs" link in every portal page header
Session details, diagnosis, annotations, fleet health, geographic performance,
SLA, software, usage metrics, audit log, system health, IME log patterns and the
rules pages link to their guide from the end of the header control group.
Navbar help links use the DOCS_PATHS catalog (service announcements no longer
goes through a redirect).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 16:34:37 +02:00
Oliver Kieselbach 7563ec9158 feat(web): DocsLink shows a one-word label on narrow screens
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 16:18:45 +02:00
Oliver Kieselbach 4e1a3c6312 feat(auth): prepare the new app registration as the default sign-in app
- Backend: the app-homing consent probe now requires the primary app's Graph
  roles to be a superset of the legacy app's, so a mere sign-in consent
  (User.Read, no application role) can never auto-flip a tenant onto an app
  without the device-validation permission.
- Web: dashboard banner for tenants still on the previous app (per-tab
  dismiss, docs link); docs links on the settings funnel banner, the flipped
  banner and the failed-sign-in screen; immediate re-homing after a redirect
  sign-in on the other app (one hop per tab, never after a cross-app
  fallback); the www -> portal handover passes the learned app as ?authapp=.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 16:18:45 +02:00
Oliver Kieselbach 3eb1f39c4b feat(web): bulk actions capped at 100 sessions, deletes above 10 require typing DELETE
The dashboard selection could grow across pages without limit and a bulk delete was one click on the red button. The effective selection is now cut to BULK_MAX_TARGETS (100, the largest page size) and every pick path (row, Shift range, page checkbox) fills only up to the remaining room. The delete dialog lists every target and, above BULK_TYPED_CONFIRM_THRESHOLD (10), asks for the word DELETE before the button arms. Block stays one click because it expires after 24 hours.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 15:26:14 +02:00
Oliver Kieselbach 7e82decc01 feat(plan): company name in the tenant contact profile, required with the contact address at the Pro trial entry point
CompanyName joins ContactEmail as the tenant contact profile: optional on Community,
both required by the self-service trial start (one 409 ContactProfileRequired verdict
naming every missing part). GA plan assignment stays soft (admin warning). Feature
flags expose companyNameSet as a boolean only; TenantTrialStarted carries companyName.
Web: Contact section field, Plan gate and dashboard banner name what is missing,
admin panel/report/search know the company. Privacy page lists the new datum.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 12:52:08 +02:00
Oliver Kieselbach af7850d786 Revert "fix(web): MCP Usage page tolerates a backend without the tenant quota fields"
This reverts commit 62c7a29b46.
2026-09-02 12:00:56 +02:00
Oliver Kieselbach 62c7a29b46 fix(web): MCP Usage page tolerates a backend without the tenant quota fields
The organization-quota card called toLocaleString on undefined when the
backend predates the tenant quota (deploy window, local dev against prod),
tripping the error boundary. Tenant fields are optional on the client now;
the card renders only when they are present.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 11:58:33 +02:00
Oliver Kieselbach 78a043db08 feat(web): shared SectionCardHeader with docs links in every settings section
- SectionCardHeader replaces 33 hand-copied card headers across settings, rules and admin pages
- DocsLink + lib/docsPaths: every tenant-facing section links its documentation page (new tab)
- docsPaths guard test encodes the GitBook URL rule (group slug, not folder name)
- delete dead MaintenanceSection duplicate

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 11:02:45 +02:00