Compare commits

...
3 changed files with 316 additions and 2 deletions

No files matched your search

@@ -0,0 +1,98 @@
# Batch 1 aggregate failure comparison
Frozen base: `e5bd294cddbf58dfe04c646b6291933d980498fd`. Current:
`9af123e5d7bdb15771d69fb427faacf32c46dfb3`. Same Node 24.19.0 executable,
same installed dependency tree, same sandbox and environment. Tests use existing
synthetic fixtures. No home/cache permission or native-build bypass was added.
The 39 entries below were observed before stopping the local full suite.
Later runner failures caused by that explicit termination are excluded. This
is not a successful aggregate run and is not proof that every full-suite test
was executed. The full suite was stopped after existing/environmental failures
to avoid presenting an incomplete run as green.
## Bounded comparison result
Targeted test-name patterns selected the original 39 failures across 15 files.
Thirty-eight fail on both frozen base and current with matching failure titles
and key error signatures. The persistence-volume assertion passes on both.
Three additional paired repetitions of that assertion also pass on both: four
passing runs per side including the initial comparison. Its code is unchanged;
the aggregate-only result remains a timing-sensitive limitation, not an
established current-only regression.
The first baseline rerun reproduced 11 entries: rows 1-2 and 4-12. It did not
reproduce row 3. No new source change fixes unrelated baseline failures.
| Rows | Signature and classification |
| --- | --- |
| 1-2, 14-16, 27-33, 35-39 | Host temp-dir creation fails outside writable roots (`ENOENT` from `tempDirBridge`). Both revisions fail. Environment constraint |
| 4-9 | Markdown comparison falls back to source; expected rich-preview elements are absent. Both revisions fail. Existing baseline/dependency behavior; no source repair in this scope |
| 10-12 | Native `pty.node` missing from normal build/prebuild lookup. Both revisions fail. Dependency/native setup constraint |
| 13 | UI algorithm list includes `ssh-ed25519-cert-v01@openssh.com` but installed ssh2 rejects it. Both revisions fail. Existing baseline/dependency behavior |
| 17-26 | Cursor discovery/SDK expectations differ (`false` vs `true`, then expected installation/auth DTO). Both revisions fail with matching assertion signatures. Existing environment/dependency behavior; no real account accessed |
| 34 | Electron bootstrap fetch fails and the binary is not installed. Both revisions fail. Dependency/network setup constraint |
| 3 | Persistence writes exceed aggregate threshold; isolated baseline/current and three repetitions each pass. Aggregate-only timing-sensitive assertion; no current-only failure reproduced |
## Original titles and first error
| # | Test file | Original title | First key error | Paired result |
| --- | --- | --- | --- | --- |
| 1 | `application/state/sftp/dedicatedTransferResume.test.ts` | large-history replace recovery retransfers retained completed children into its empty stage: late=false | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 2 | `application/state/sftp/dedicatedTransferResume.test.ts` | large-history replace recovery retransfers retained completed children into its empty stage: late=true | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 3 | `application/state/sftpTransferCenterStore.test.ts` | large directory progress coalesces synchronous persistence work | AssertionError [ERR_ASSERTION]: expected bounded persistence volume, wrote 2638624 characters for a 588644-character snapshot | base/current pass; aggregate-only |
| 4 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | literal Markdown comparisons render without changing the note source | AssertionError [ERR_ASSERTION]: a comparison must render as ordinary text | base/current same failure |
| 5 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | comparison variants render in paragraphs, tables and alongside HTML | assert.ok(!querySourceFallback(rootNode)) is false | base/current same failure |
| 6 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | issue 3205 author note survives source, preview and edit without rewriting | AssertionError [ERR_ASSERTION]: preview must render the author's note | base/current same failure |
| 7 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | editing a comparison emits Markdown that can be reopened | assert.ok(editable): actual null | base/current same failure |
| 8 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | preview task checkboxes still save explicit changes without rewriting comparisons | assert.ok(task): actual null | base/current same failure |
| 9 | `components/notes/InlineMarkdownEditor.unrenderableMarkdown.test.tsx` | switching between a comparison note and an unsupported note keeps the fallback scoped | assert.ok(!querySourceFallback(rootNode)) is false | base/current same failure |
| 10 | `components/terminal/runtime/serialInputSequences.test.ts` | components/terminal/runtime/serialInputSequences.test.ts | Failed to load native module: pty.node | base/current same failure |
| 11 | `components/terminal/runtime/terminalLinePaste.test.ts` | components/terminal/runtime/terminalLinePaste.test.ts | Failed to load native module: pty.node | base/current same failure |
| 12 | `components/terminal/runtime/terminalPasteCancellation.test.ts` | components/terminal/runtime/terminalPasteCancellation.test.ts | Failed to load native module: pty.node | base/current same failure |
| 13 | `domain/sshAlgorithmList.test.ts` | SUPPORTED_ALGORITHMS_BY_CATEGORY only lists algorithms ssh2 will actually accept | AssertionError [ERR_ASSERTION]: ssh-ed25519-cert-v01@openssh.com (serverHostKey) is in the UI list but ssh2 would reject it | base/current same failure |
| 14 | `electron/bridges/ai/liveShellProbe.test.cjs` | Bash invoked as sh cleans probe history | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 15 | `electron/bridges/ai/liveShellProbe.test.cjs` | Bash invoked as renamed-bash cleans probe history | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 16 | `electron/bridges/aiBridge.externalContext.test.cjs` | electron/bridges/aiBridge.externalContext.test.cjs | ENOENT creating host temp directory from sshBridge import | base/current same failure |
| 17 | `electron/bridges/aiBridge.test.cjs` | resolve-cli reports Cursor Agent CLI on PATH as installed without CLI login | AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: | base/current same failure |
| 18 | `electron/bridges/aiBridge.test.cjs` | resolve-cli reports Cursor SDK installed but unavailable without an API key | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: | base/current same failure |
| 19 | `electron/bridges/aiBridge.test.cjs` | resolve-cli separates Cursor SDK installation from API key availability | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: | base/current same failure |
| 20 | `electron/bridges/aiBridge.test.cjs` | resolve-cli ignores custom Cursor paths and stores the SDK sentinel path | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: | base/current same failure |
| 21 | `electron/bridges/aiBridge.test.cjs` | resolve-cli exposes Cursor SDK support when installed and authenticated | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: | base/current same failure |
| 22 | `electron/bridges/aiBridge.test.cjs` | resolve-cli exposes Cursor SDK support when API key is saved in settings | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: | base/current same failure |
| 23 | `electron/bridges/aiBridge.test.cjs` | resolve-cli reports settings as Cursor auth source when settings and env keys both exist | AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: | base/current same failure |
| 24 | `electron/bridges/aiBridge.test.cjs` | resolve-cli can refresh shell env before resolving Cursor | AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: | base/current same failure |
| 25 | `electron/bridges/aiBridge.test.cjs` | discover exposes Cursor SDK support when API key is saved in settings | AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: | base/current same failure |
| 26 | `electron/bridges/aiBridge.test.cjs` | discover can refresh shell env before scanning Cursor | AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: | base/current same failure |
| 27 | `electron/bridges/aiBridge/sdk/copilotDriver.test.cjs` | listCopilotModels resolves a Windows .cmd shim to the copilot JS entry before the SDK sees it | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 28 | `electron/bridges/aiBridge/sdk/copilotDriver.test.cjs` | listCopilotModels retains the official loader when its platform runtime is unavailable | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 29 | `electron/bridges/aiBridge/sdk/copilotDriver.test.cjs` | resolveCopilotPlatformExecutable resolves the platform package exe from the loader root | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 30 | `electron/bridges/aiBridge/sdk/copilotDriver.test.cjs` | resolveCopilotExecutableForSdk resolves npm's copilot.ps1 wrapper to the JS entry on Windows | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 31 | `electron/bridges/aiBridge/sdk/copilotDriver.test.cjs` | connectCopilotStdioPath directly selects the official loader's native runtime | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 32 | `electron/bridges/aiBridge/sdk/opencodeDriver.startup.test.cjs` | OpenCode releases the process-env gate after spawning separate catalog profiles | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 33 | `electron/bridges/aiBridge/sdk/opencodeDriver.test.cjs` | createOpenCodeProcessEnv prefers an existing opencode binary | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 34 | `electron/bridges/aiEchoSuppressionOrdering.test.cjs` | electron/bridges/aiEchoSuppressionOrdering.test.cjs | fetch failed; Electron failed to install correctly | base/current same failure |
| 35 | `electron/bridges/autoUpdateBridge.test.cjs` | recognizes packaged Linux FPM formats as auto-update capable | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 36 | `electron/bridges/autoUpdateBridge.test.cjs` | allows the update check to reach electron-updater for a packaged Linux deb | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 37 | `electron/bridges/autoUpdateBridge.test.cjs` | keeps the manual-update fallback for an unmarked Linux package | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 38 | `electron/bridges/autoUpdateBridge.test.cjs` | keeps AppImage auto-update support on Linux | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
| 39 | `electron/bridges/autoUpdateBridge.test.cjs` | install handler reports package-manager failures and restores the app state | ENOENT creating host temp directory from tempDirBridge | base/current same failure |
## Reproduction method
For each file above, run the existing test with the same title pattern in each
checkout. Pass the title pattern as an argument, without interpolating it as
shell code. Native/import failures occur before the title filter can execute.
```sh
node --test --import tsx --test-reporter=tap --test-name-pattern "<escaped original title>" <test file>
```
The comparison used bounded 90-second subprocess deadlines. None reached that
deadline. The persistence repeat used the same existing test and a 60-second
deadline. It added no load generator or attack payload.
All 15 test files and the business modules responsible for these signatures
are unchanged by batch 1. This classification does not resolve the two
independently reported grant-management review findings; those receive
separate source fixes and tests.
+3 -2
View File
@@ -27,7 +27,7 @@ mean complete. Existing APIs remain available.
| Vault and keychain | Credential references and import preview/commit | Metadata/write enums do not grant a generic Vault SDK | Only user-selected whitelist DTOs and drafts | Notes, environmentVariables, startupCommand, keys and credentials | Selected object plus field/direction | Explicit preview, identity checks, redacted audit | Full Vault export and secret reads deferred indefinitely. No agent field-exclusion DTO reuse |
| System manager and Docker | No plugin system API | Agent/MCP adapters are separate authority | Typed read-only summaries excluding args/env/inspect | Process command lines and Docker inspect can expose secrets | Selected live session and summary kind | Bounded result, endpoint identity, cancellation | Planned evaluation. Arbitrary process/inspect forwarding deferred |
| Port forwarding | No generic plugin API | Existing agent start is write-sensitive | Selected rule status and open host form | Rule endpoints and local exposure | Selected rule plus host action | Bounded DTO, session close and revoke | Planned evaluation. Direct start deferred until binding/listener approval design |
| Themes, settings, appearance, i18n | Contributions, scoped setting API, environment updates | No arbitrary application settings write | Existing namespaced contributions. Candidate: declarative global ThemePack using the 19 semantic tokens in `uiThemes.ts`, with preview/apply/reset and uninstall fallback | Settings can contain sensitive plugin values. A theme must preserve contrast and security UI | Plugin namespace, declared scope | Existing persistence and lifecycle, explicit sync flag | Existing tests. ThemePack design pending parent review of allowed tokens, accessibility and fallback. No arbitrary host DOM/CSS, remote CSS/fonts or security UI hiding. Arbitrary host settings mutation deferred |
| Themes, settings, appearance, i18n | Terminal theme/background Providers, isolated view CSS, scoped settings and environment updates | No host global theme contribution. Personal customCSS is not plugin authority | Accepted ThemePack stage: static palette registry, source-labelled selector, window-local preview, revision-safe apply/cancel/reset, static terminal linkage | Unreadable colors and misleading security UI. Nineteen UiThemeTokens fields are not independent: primary/accent/ring and foregrounds derive from the effective accent | User explicitly selects a theme. Separate static contribution enablement from code permissions; no generic appearance.write or runtime activation | Immutable version/hash, bounded JSON, preview owner/token/revision, disable/update/uninstall fallback, no implicit sync, fixed host security appearance | [ThemePack design](./theme-packs-design.md) accepted for implementation after current A work; unfinished until contract, security boundary, lifecycle, contrast, samples and native gates pass. No arbitrary DOM/CSS, remote assets, free layout or security UI customization |
| Transfer center | Host transfer queue exists, no plugin entry | No ownership-bound task API | Observe and cancel only plugin-created tasks | Paths and host endpoints reveal user data. Cancel may leave partial files | Own transfer handle plus source/destination authority | Shared backpressure, cancel/close cleanup, honest partial status | Planned with batch 2. Do not broadcast or cancel other tasks |
| AI, MCP, automation | Host agent catalog and harness exist separately | No generic plugin AI or tool forwarding | Selection-to-proposal workflow through the same controlled primitives | Content disclosure, account authority and model cost | Explicit content sharing plus service origin/budget | No inherited history, tools, account, or hidden cron | Controlled workflow planned. Generic host AI and visible schedules deferred to distinct product decision |
| Known hosts, app lock and global security | Host security management UI | No plugin management API | Future allowlisted security-page navigation | Host-key trust, unlock, keys and security policy affect all sessions | Host navigation action only | No mutation or sensitive DTO | Security policy mutation, private-key reads, auto-trust and unlocking deferred indefinitely |
@@ -41,7 +41,8 @@ mean complete. Existing APIs remain available.
2. Batch 1b: host action context, native interactions, selected context and terminal
sharing/drafts. A plugin-supplied gesture is never authority.
3. Batch 2: SFTP selected-file handles and explicit transfer destinations.
4. Reassess editor, Notes, snippets, menus, system summaries, and port forwarding
4. Deliver the separately reviewed [declarative ThemePack stage](./theme-packs-design.md), including protected security surfaces, contrast gates, revision-safe preview/apply/reset, and safe fallback.
5. Reassess editor, Notes, snippets, menus, system summaries, and port forwarding
using the tested host-action and resource primitives.
Stop only when every row is implemented and tested, reasonably deferred with a
+215
View File
@@ -0,0 +1,215 @@
# Declarative theme packs
Status: proposed implementation plan. No theme contribution or theme permission
is implemented by this document. The baseline is Draft #3951 at
`e5bd294cddbf58dfe04c646b6291933d980498fd`; batch 1 grants and network contracts
are reviewed separately in Draft #3955.
## Scope and existing behavior
A theme pack supplies validated color data. Users preview, apply, cancel, or
reset it through the host Appearance UI. Selecting a theme never starts a
plugin runtime or approves its function permissions. This extends whole-app
appearance alongside existing `terminal.theme` and `terminal.background`
Providers and isolated view styles.
Personal `customCSS` is a user-owned advanced setting, not a third-party plugin
surface. This plan introduces no stylesheet, selector, DOM, font URL, image URL,
script, layout, notification, or background permission.
`UiThemeTokens` has 19 fields. They are not independent controls:
`buildAppThemeCssVars()` assigns one effective accent to primary, accent, and
ring and computes both accent foregrounds. The initial theme contract preserves
this relationship. It must not accept independent primary, primaryForeground,
accentForeground, or ring overrides and then silently discard them.
## Contract and package admission
The proposed feature is `appearance.theme-packs`. Its schema definitions,
generated SDK exports, runtime/package compatibility, and documentation move
together. Plan an internal schema/API revision with legacy negotiation retained;
do not change the current batch 1 API just to publish this proposal. Old hosts
reject a pack that requires the unknown feature before registration.
A proposed `contributes.themes` entry contains:
| Field | Meaning and validation |
| --- | --- |
| `id` | Contribution ID under the owning plugin namespace |
| `label` | Existing bounded localized-text contract |
| `variant` | `light`, `dark`, `hc-light`, or `hc-dark` |
| `path` | Canonical package-relative JSON path inside the immutable installed version |
A theme JSON document contains a schema revision, a variant-matching built-in
base ID, bounded color overrides, and an optional static terminal palette.
It contains no cross-plugin includes, arbitrary file references, inheritance
chains, or remote resources. Resolve the validated base and overrides into one
immutable complete palette before registration. Never retain partially valid
entries from a failed theme document.
The initial allowed UI overrides are `background`, `foreground`, `card`,
`cardForeground`, `popover`, `popoverForeground`, `secondary`,
`secondaryForeground`, `muted`, `mutedForeground`, `accent`, `destructive`,
`destructiveForeground`, `border`, and `input`. Accept only opaque `#RRGGBB`
colors. The host converts them to its internal HSL representation. Reject
unknown keys, alpha, CSS expressions, `url()`, `var()`, `@import`, non-finite
values, and custom properties. Do not pass author strings to CSS parsing.
The optional terminal palette uses the existing `TerminalThemeColorName`
allowlist. It merges with a host default and is validated independently.
Static terminal colors require neither a session snapshot nor a Provider call.
Initial bounds for implementation and tests are 32 themes per package,
64 KiB per JSON file, 2 MiB total theme JSON, depth 8, and one active preview per
window. These are proposed defaults, not existing limits. The schema and host
must share the final constants. No font, image, or video assets enter v1.
### Static admission must remain distinct from code activation
The present manifest requires `main` and browser-only policy expects a browser
entrypoint. Removing either check without a separate package variant is unsafe.
For the first usable slice, a sample can retain a no-op browser entrypoint.
Static theme registration must not invoke it. Theme enablement has a separate
host-owned state from function/code enablement. Installing a pack keeps both
off until a user opts into the corresponding contribution. Enabling its themes
never sets the code-enabled flag, grants permissions, or runs activation events.
Disabling the package disables its theme contributions as well as its code.
A later exclusive data-only manifest variant can remove the no-op entrypoint.
It must reject `main`, activation events, permissions, companions, providers,
commands, and every non-theme contribution. Update canonical validation,
semantic admission, package inspection, catalog policy, manager behavior, and
compatibility tests together. The public catalog must explicitly recognize
this inert variant while retaining browser-only admission for executable packs.
A theme-only exception must not admit Node or an executable mixed package.
## Host registry and persistence
A host registry reads only installed, immutable package versions through the
existing package-store path boundary. Verify real paths, file kinds, byte caps,
and package ownership. Preserve the validated source version and content hash.
No plugin RPC can register, apply, or replace global palette data.
A committed selection references plugin ID, theme ID, variant, package version,
and validated theme hash. Store per-variant selections and a monotonic commit
revision through the settings/persistence layer. Do not persist plugin CSS or
turn this into implicit KV/cloud synchronization. Preserve legacy built-in theme
IDs and existing user settings when no plugin theme is selected.
Theme availability records and applied preferences are user state, separate
from runtime KV and function permissions. Define retained missing-selection
metadata and uninstall behavior explicitly. Missing code or theme files must
not trigger downloads, runtime activation, or new grants.
## Preview, apply, cancel, and reset
A single appearance resolver feeds existing chrome/terminal effects. Do not add
a second root effect that races `useAppThemeStyle`, `useThemeRuntime`, or active
terminal chrome selection.
| Host action | Required behavior |
| --- | --- |
| Preview | Create a host-owned token bound to window, preview owner, selected immutable theme, and current committed revision. Render locally without storage writes, sync events, runtime activation, or other-window changes |
| Apply | Verify owner, token, version/hash, availability, and expected commit revision. Commit the selected theme and explicitly chosen terminal linkage as one settings action. A stale preview produces a conflict, not an overwrite |
| Cancel or close | Clear only the matching local preview token. Render the latest committed state. Never restore a captured old settings snapshot over a newer commit |
| Reset | Clear the selected variant's plugin reference using the same revision check and apply the host default. Preserve font, accent, per-host terminal, and accessibility preferences |
| Another window applies | Advance the shared commit revision. End the old preview or mark it stale; later cancel cannot alter the new selection |
| Disable/uninstall/update | Invalidate matching previews and registry entries. Immediately resolve affected selections to a safe built-in variant, retain a bounded missing-reference notice, and never silently adopt unreviewed replacement bytes |
| Application exit or window destruction | Release local preview ownership. Only committed preferences survive startup |
The preview UI always identifies plugin source/version and has host-controlled
Apply, Cancel, and Restore default controls. Those controls live inside the
security appearance boundary. Theme data cannot control their labels, order,
visibility, click behavior, or layout.
Applying a theme changes only the selected palette and explicit terminal linkage.
It does not overwrite user font family/size, accent override, host terminal
override, reduced-motion choice, or OS accessibility preferences.
## Appearance precedence and terminal behavior
Resolve appearance in this order:
1. Host security surfaces and OS accessibility requirements.
2. Explicit user font, size, accent, and per-host/per-terminal overrides.
3. The selected and validated theme pack.
4. Built-in defaults.
Define one effective accent using the current host rule. Reject impossible
contrast combinations, including a user accent override against the effective
surface, or show an explicit host correction before apply. Do not report an
ignored author field as a successful override.
Follow App may use the pack's static terminal palette. Manual terminal choices
and per-host overrides retain precedence. Reuse the existing active-chrome
selection rather than resetting app appearance on tab changes. Test Vault,
SFTP, terminal, editor, Notes, logs, split panes, and detached windows.
High-contrast flags in plugin environments are not proof that the host already
has complete high-contrast themes. Missing HC variants require a host-owned HC
fallback. Do not downgrade a forced-colors/high-contrast preference to an
ordinary light/dark theme. Respect reduced motion and font scaling.
## Security appearance and contrast gates
Native Electron permission dialogs already sit outside renderer CSS. React
security surfaces need a new explicit boundary before a theme pack ships.
Cover AppLock, host-key trust, credential selection, grant/revoke management,
dangerous confirmations, and theme preview/apply controls. Use host-controlled
colors, font, icons, and layout there. Package colors never style those surfaces.
The existing code has no verified shared boundary covering all of them.
Color validation checks complete resolved/composited pairs, not only body text:
foreground/background, card text/card, popover text/popover, secondary/muted
text, accent/button text, destructive/button text, focus rings, borders, inputs,
and selected/disabled states. Ordinary text needs 4.5:1, large text 3:1, and
required non-text indicators 3:1 against adjacent surfaces. Reject a failing
palette with a named pair and ratio, or show an explicit host correction.
Tests must prove the effective UI, not only a helper's arithmetic.
These gates follow [WCAG text contrast](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)
and [non-text contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html).
Theme screenshot review supplements contract, lifecycle, and accessibility
checks. It does not establish safe ownership, persistence, or revocation.
## Delivery stages and acceptance
Current batch A grants and SDK consistency reaches review/CI first. Complete
the controlled native interaction/context work as planned; theme work remains
a separate small stage from marketplace card/layout changes.
| Stage | Implementation and evidence |
| --- | --- |
| Contract and pure validation | Canonical schema/generated SDK, feature/revision negotiation, strict color parser, derived-accent semantics, static terminal palette, bounds, named contrast diagnostics, old manifest compatibility |
| Registry and security boundary | Immutable package ownership, static registration without runtime, independent theme/code enablement, fixed security appearance, same-variant/HC fallback, unavailable/tampered version behavior |
| Product workflow | Appearance grouping/source, local preview ownership, revision-safe apply/cancel/reset, safe defaults, system theme changes, terminal linkage and overrides, persistence and window synchronization |
| Samples and final acceptance | Light/dark no-op-browser sample, normal settings import without advanced mode, no runtime/grants on theme apply, later data-only admission only if fully validated, exact-SHA tests/build/package/CI, Codex review/fix/review, parent-owned native recording |
Boundary tests include unknown fields/feature, invalid or excessive JSON,
unsupported variant/base, non-package and changed paths, unreadable colors,
preview cancellation and window close, two-window apply/cancel ordering,
update/disable/uninstall/failure fallback, startup restoration, host and accent
overrides, forced colors, reduced motion, Chinese fonts/fallback, and every
protected security surface. A pending gate keeps the theme row unfinished.
Later controlled candidates are known-font recommendations, radius/density
presets after component support exists, declarative file/folder icons, and
package-local static backgrounds on fixed surfaces with contrast limits. They
need separate contracts and acceptance. Global CSS, free layout, remote fonts,
HTML/video backgrounds, and security-UI customization remain excluded.
## Baseline source references
- `infrastructure/config/uiThemes.ts`: token fields and built-in registry.
- `application/state/settingsStateDefaults.ts`: accent derivation and built-in ID checks.
- `application/state/appearanceSync.ts`, `appearanceChromeStore.ts`, and
`useSettingsState.ts`: persistence and cross-window behavior.
- `application/app/useAppThemeStyle.ts`, `activeChromeTheme.ts`,
`application/state/useThemeRuntime.ts`, and `domain/terminalAppearance.ts`:
effective UI and terminal precedence.
- `packages/plugin-sdk/src/index.ts`: existing terminal theme/background types.
- `electron/plugins/pluginProtocol.cjs`: isolated view CSS/font/network CSP.
- `lib/customCss.ts`: personal user CSS, excluded from plugin authority.
- `domain/pluginRuntimePolicy.cjs` and the canonical manifest schema: executable admission.
- `electron/plugins/nativePermissionDecision.cjs`: native permission prompts.