mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 02:28:59 +00:00
docs(branding): complete subsystem governance evidence
This commit is contained in:
parent
672db9113f
commit
c700454b18
6 changed files with 65 additions and 0 deletions
|
|
@ -5614,3 +5614,13 @@ callback builder already applied, and they neither mutate nor persist
|
|||
update targeting, or fleet-lifecycle authority is touched, and the
|
||||
extension-point expectations on `internal/api/` and `internal/api/router.go`
|
||||
are otherwise unchanged.
|
||||
|
||||
### Runtime branding stays outside agent lifecycle authority
|
||||
|
||||
The authenticated `GET /api/runtime/branding` presentation endpoint is an
|
||||
application-shell read, not an agent registration, configuration, update, or
|
||||
command route. It may read the active `white_label` entitlement and the
|
||||
tenant-local `reportBranding` settings record, but it must not enumerate
|
||||
agents, resolve install tokens, mutate agent state, or reuse agent lifecycle
|
||||
authority. Agent setup and reporting remain independent of whether an
|
||||
operator has configured a custom application name or logo.
|
||||
|
|
|
|||
|
|
@ -6832,3 +6832,10 @@ that chrome. Assistant and Patrol shell state stays owned by
|
|||
`frontend-modern/src/stores/aiChat.ts` and the canonical Patrol path as before;
|
||||
this containment is a shell-level authority rule and must not be forked into a
|
||||
second AI-side scope check.
|
||||
|
||||
Application branding in `frontend-modern/src/AppLayout.tsx` is presentation
|
||||
only. A licensed custom name or logo may replace the default Pulse lockup and
|
||||
browser-title suffix, but it must not rename Pulse Assistant, alter Assistant
|
||||
page context, change tool/provider identity, or enter prompt, transcript, or
|
||||
Patrol evidence payloads. The AI runtime continues to consume canonical route
|
||||
and resource context independently of the shell's displayed brand.
|
||||
|
|
|
|||
|
|
@ -3233,3 +3233,14 @@ global banner block in `frontend-modern/src/App.tsx` (security, demo,
|
|||
commercial migration, update, what's-new, GitHub star) must be gated on the
|
||||
same predicate because those banners deep-link into settings destinations a
|
||||
scope-limited session cannot open.
|
||||
|
||||
The authenticated shell may project tenant-local application branding only
|
||||
through the narrow `GET /api/runtime/branding` response after normal session
|
||||
and `monitoring:read` admission. The runtime must return
|
||||
`enabled=false` with empty presentation values unless the active license grants
|
||||
`white_label`; persisted `reportBranding` values must never become an
|
||||
unlicensed browser-side feature flag. `frontend-modern/src/AppLayout.tsx` may
|
||||
consume that filtered response for the header and document title, while
|
||||
`frontend-modern/src/useAppRuntimeState.ts` keeps the fetch inside the existing
|
||||
authenticated bootstrap and does not add a commercial-posture, checkout, or
|
||||
organization probe.
|
||||
|
|
|
|||
|
|
@ -2102,3 +2102,15 @@ manipulation on the request already in hand. They add no inventory scan,
|
|||
persistence read, lock acquisition, metrics fan-out, or background work, and
|
||||
their cost is constant per SSO provider entry rendered in the admin settings
|
||||
response.
|
||||
|
||||
### Runtime branding remains a bounded bootstrap read
|
||||
|
||||
Authenticated application branding is loaded once alongside the existing
|
||||
system-settings and layout bootstrap work. `useAppRuntimeState.ts` must keep
|
||||
that request in the same parallel bootstrap group rather than serializing it
|
||||
ahead of state hydration, organization startup, or WebSocket connection.
|
||||
`GET /api/runtime/branding` performs one cached entitlement decision and at
|
||||
most one tenant-local system-settings read; it must not scan tenants, read
|
||||
external logo files, call a licensing network service, or add per-route and
|
||||
per-component refetches. The response remains bounded to an enabled flag, one
|
||||
120-character display name, and the already size-limited inline image.
|
||||
|
|
|
|||
|
|
@ -620,4 +620,17 @@ describe('App architecture', () => {
|
|||
expect(appLayoutSource).toContain("label: 'Patrol'");
|
||||
expect(appLayoutSource).toContain('countLabel: patrolAttentionCountLabel()');
|
||||
});
|
||||
|
||||
it('keeps licensed application branding inside the authenticated shell bootstrap', () => {
|
||||
expect(appRuntimeStateSource).toContain(
|
||||
'await Promise.all([loadSystemSettingsAndLayout(), loadRuntimeBranding()]);',
|
||||
);
|
||||
expect(appLayoutSource).toContain("import { runtimeBranding } from '@/stores/systemSettings';");
|
||||
expect(appLayoutSource).toContain(
|
||||
"const browserBrandName = createMemo(() => customBrandName() || 'Pulse');",
|
||||
);
|
||||
expect(appLayoutSource).toContain('data-testid="custom-brand-logo"');
|
||||
expect(appRuntimeStateSource).not.toContain('/api/license/commercial-posture');
|
||||
expect(appRuntimeStateSource).not.toContain('/api/license/entitlements');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import aiProviderConfigurationSectionSource from '../AIProviderConfigurationSect
|
|||
import aiRuntimeControlsSectionSource from '../AIRuntimeControlsSection.tsx?raw';
|
||||
import aiSettingsModelSource from '../aiSettingsModel.ts?raw';
|
||||
import generalSettingsPanelSource from '../GeneralSettingsPanel.tsx?raw';
|
||||
import brandingSettingsCardSource from '../BrandingSettingsCard.tsx?raw';
|
||||
import dockerRuntimeSettingsCardSource from '../DockerRuntimeSettingsCard.tsx?raw';
|
||||
import settingsHeaderMetaSource from '../settingsHeaderMeta.ts?raw';
|
||||
import settingsNavCatalogSource from '../settingsNavCatalog.ts?raw';
|
||||
|
|
@ -1781,6 +1782,17 @@ describe('settings architecture guardrails', () => {
|
|||
expect(diagnosticsResultsPanelSource).not.toContain('getStatusIndicatorBadgeToneClasses');
|
||||
});
|
||||
|
||||
it('keeps application branding in the canonical Appearance settings surface', () => {
|
||||
expect(generalSettingsPanelSource).toContain(
|
||||
"import { BrandingSettingsCard } from './BrandingSettingsCard';",
|
||||
);
|
||||
expect(generalSettingsPanelSource).toContain('<BrandingSettingsCard');
|
||||
expect(brandingSettingsCardSource).toContain("when={hasFeature('white_label')}");
|
||||
expect(brandingSettingsCardSource).toContain('<FeatureGateSection');
|
||||
expect(brandingSettingsCardSource).toContain('maxlength={120}');
|
||||
expect(brandingSettingsCardSource).toContain('BRAND_LOGO_MAX_BYTES = 36 * 1024');
|
||||
});
|
||||
|
||||
it('keeps every PBS diagnostic failure string inside the export redaction boundary', () => {
|
||||
// Behavioural guard, not a source scan: the redaction gap that shipped was
|
||||
// a field added to the payload after the sanitizer was written, which no
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue