refactor: replace "seam" terminology across codebase

Replace "seam" with clearer terms throughout:
- "surface" for public API/extension boundaries
- "boundary" for plugin/module interfaces
- "interface" for runtime connection points
- "hook" for test injection points
- "palette" for the lobster palette reference

Also delete experiments/acp-pluginification-architecture-plan.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vincent Koc 2026-03-18 00:19:56 -07:00
parent 6802a768cf
commit 466510b6d8
19 changed files with 30 additions and 549 deletions

View File

@ -281,7 +281,7 @@
- If staged+unstaged diffs are formatting-only, auto-resolve without asking.
- If commit/push already requested, auto-stage and include formatting-only follow-ups in the same commit (or a tiny follow-up commit if needed), no extra confirmation.
- Only ask when changes are semantic (logic/data/behavior).
- Lobster seam: use the shared CLI palette in `src/terminal/palette.ts` (no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed.
- Lobster palette: use the shared CLI palette in `src/terminal/palette.ts` (no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed.
- **Multi-agent safety:** focus reports on your edits; avoid guard-rail disclaimers unless truly blocked; when multiple agents touch the same file, continue if safe; end with a brief “other files present” note only if relevant.
- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
- Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).

View File

@ -35,7 +35,7 @@ Docs: https://docs.openclaw.ai
- Models/OpenAI: add native forward-compat support for `gpt-5.4-mini` and `gpt-5.4-nano` in the OpenAI provider catalog, runtime resolution, and reasoning capability gates. Thanks @vincentkoc.
- Plugins/bundles: make enabled bundle MCP servers expose runnable tools in embedded Pi, and default relative bundle MCP launches to the bundle root so marketplace bundles like Context7 work through Pi instead of stopping at config import.
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` seam for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
- Plugins/Chutes: add a bundled Chutes provider with plugin-owned OAuth/API-key auth, dynamic model discovery, and default-on extension wiring. (#41416) Thanks @Veightor.
- Plugins/binding: add `onConversationBindingResolved(...)` so plugins can react immediately after bind approvals or denies without blocking channel interaction acknowledgements. (#48678) Thanks @huntharo.
- CLI/config: expand `config set` with SecretRef and provider builder modes, JSON/batch assignment support, and `--dry-run` validation with structured JSON output. (#49296) Thanks @joshavant.
@ -141,7 +141,7 @@ Docs: https://docs.openclaw.ai
- Telegram/network: unify API and media fetches under the same sticky IPv4 and pinned-IP fallback chain, and re-validate pinned override addresses against SSRF policy. (#49148) Thanks @obviyus.
- Agents/prompt composition: append bootstrap truncation warnings to the current-turn prompt and add regression coverage for stable system-prompt cache invariants. (#49237) Thanks @scoootscooob.
- Gateway/auth: add regression coverage that keeps device-less trusted-proxy Control UI sessions off privileged pairing approval RPCs. Thanks @vincentkoc.
- Plugins/runtime-api: pin extension runtime-api export seams with explicit guardrail coverage so future surface creep becomes a deliberate diff. Thanks @vincentkoc.
- Plugins/runtime-api: pin extension runtime-api export surfaces with explicit guardrail coverage so future surface creep becomes a deliberate diff. Thanks @vincentkoc.
- Telegram/security: add regression coverage proving pinned fallback host overrides stay bound to Telegram and delegate non-matching hostnames back to the original lookup path. Thanks @vincentkoc.
- Secrets/exec refs: require explicit `--allow-exec` for `secrets apply` write plans that contain exec SecretRefs/providers, and align audit/configure/apply dry-run behavior to skip exec checks unless opted in to prevent unexpected command side effects. (#49417) Thanks @restriction and @joshavant.
- Tools/image generation: add bundled fal image generation support so `image_generate` can target `fal/*` models with `FAL_KEY`, including single-image edit flows via FLUX image-to-image. Thanks @vincentkoc.

View File

@ -88,7 +88,7 @@ OpenClaw uses a lobster palette for CLI output.
- `error` (#E23D2D): errors, failures.
- `muted` (#8B7F77): de-emphasis, metadata.
Palette source of truth: `src/terminal/palette.ts` (aka “lobster seam”).
Palette source of truth: `src/terminal/palette.ts` (the “lobster palette”).
## Command tree

View File

@ -55,14 +55,14 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
- Embedded runner note:
- When you change message-tool discovery inputs or compaction runtime context,
keep both levels of coverage.
- Add focused helper regressions for pure routing/normalization seams.
- Add focused helper regressions for pure routing/normalization boundaries.
- Also keep the embedded runner integration suites healthy:
`src/agents/pi-embedded-runner/compact.hooks.test.ts`,
`src/agents/pi-embedded-runner/run.overflow-compaction.test.ts`, and
`src/agents/pi-embedded-runner/run.overflow-compaction.loop.test.ts`.
- Those suites verify that scoped ids and compaction behavior still flow
through the real `run.ts` / `compact.ts` paths; helper-only tests are not a
sufficient substitute for those seams.
sufficient substitute for those integration paths.
- Pool note:
- OpenClaw uses Vitest `vmForks` on Node 22, 23, and 24 for faster unit shards.
- On Node 25+, OpenClaw automatically falls back to regular `forks` until the repo is re-validated there.

View File

@ -1,519 +0,0 @@
# Bindings Capability Architecture Plan
Status: in progress
## Summary
The goal is not to move all ACP code out of core.
The goal is to make `bindings` a small core capability, keep the ACP session kernel in core, and move ACP-specific binding policy plus codex app server policy out of core.
That gives us a lightweight core without hiding core semantics behind plugin indirection.
## Current Conclusion
The current architecture should converge on this split:
- Core owns the generic binding capability.
- Core owns the generic ACP session kernel.
- Channel plugins own channel-specific binding semantics.
- ACP backend plugins own runtime protocol details.
- Product-level consumers like ACP configured bindings and the codex app server sit on top of the binding capability instead of hardcoding their own binding plumbing.
This is different from "everything becomes a plugin".
## Why This Changed
The current codebase already shows that there are really three different layers:
- binding and conversation ownership
- long-lived session and runtime-handle orchestration
- product-specific turn logic
Those layers should not all be forced into one runtime engine.
Today the duplication is mostly in the execution/control-plane shape, not in storage or binding plumbing:
- the main harness has its own turn engine
- ACP has its own session control plane
- the codex app server plugin path likely owns its own app-level turn engine outside this repo
The right move is to share the stable control-plane contracts, not to force all three into one giant executor.
## Verified Current State
### Generic binding pieces already exist
- `src/infra/outbound/session-binding-service.ts` already provides a generic binding store and adapter model.
- `src/plugins/conversation-binding.ts` already lets plugins request a conversation binding and stores plugin-owned binding metadata.
- `src/plugins/types.ts` already exposes plugin-facing binding APIs.
- `src/plugins/types.ts` already exposes the generic `inbound_claim` hook.
### ACP is only partially pluginified
- `src/channels/plugins/configured-binding-registry.ts` now owns generic configured binding compilation and lookup.
- `src/channels/plugins/binding-routing.ts` and `src/channels/plugins/binding-targets.ts` now own the generic route and target lifecycle seams.
- ACP now plugs into that seam through `src/channels/plugins/acp-configured-binding-consumer.ts` and `src/channels/plugins/acp-stateful-target-driver.ts`.
- `src/acp/persistent-bindings.lifecycle.ts` still owns configured ACP ensure and reset behavior.
- runtime-created plugin conversation bindings still use a separate path in `src/plugins/conversation-binding.ts`.
### Codex app server is already closer to the desired shape
From this repo's side, the codex app server path is much thinner:
- a plugin binds a conversation
- core stores that binding
- inbound dispatch targets the plugin's `inbound_claim` hook
What core does not provide for the codex app server path is an ACP-like shared session kernel. If the app server needs retries, long-lived runtime handles, cancellation, or session health logic, it must own that itself today.
## The Durable Split
### 1. Core Binding Capability
This should become the primary shared seam.
Responsibilities:
- canonical `ConversationRef`
- binding record storage
- configured binding compilation
- runtime-created binding storage
- fast binding lookup on inbound
- binding touch/unbind lifecycle
- generic dispatch handoff to the binding target
What core binding capability must not own:
- Discord thread rules
- Telegram topic rules
- Feishu chat rules
- ACP session orchestration
- codex app server business logic
### 2. Core Stateful Target Kernel
This is the small generic kernel for long-lived bound targets.
Responsibilities:
- ensure target ready
- run turn
- cancel turn
- close target
- reset target
- status and health
- persistence of target metadata
- retries and runtime-handle safety
- per-target serialization and concurrency
ACP is the first real implementation of this shape.
This kernel should stay in core because it is mandatory infrastructure and has strict startup, reset, and recovery semantics.
### 3. Channel Binding Providers
Each channel plugin should own the meaning of "this channel conversation maps to this binding rule".
Responsibilities:
- normalize configured binding targets
- normalize inbound conversations
- match inbound conversations against compiled bindings
- define channel-specific matching priority
- optionally provide binding description text for status and logs
This is where Discord channel vs thread logic, Telegram topic rules, and Feishu conversation rules belong.
### 4. Product Consumers
Bindings are a shared capability. Different products should consume it differently.
ACP configured bindings:
- compile config rules
- resolve a target session
- ensure the ACP session is ready through the ACP kernel
Codex app server:
- create runtime-requested bindings
- claim inbound messages through plugin hooks
- optionally adopt the shared stateful target contract later if it really needs long-lived session orchestration
Main harness:
- does not need to become "a binding product"
- may eventually share small lifecycle contracts, but it should not be forced into the same engine as ACP
## The Key Architectural Decision
The shared abstraction should be:
- `bindings` as the capability
- `stateful target drivers` as an optional lower-level contract
The shared abstraction should not be:
- "one runtime engine for main harness, ACP, and codex app server"
That would overfit very different systems into one executor.
## Stable Nouns
Core should understand only stable nouns.
The stable nouns are:
- `ConversationRef`
- `BindingRule`
- `CompiledBinding`
- `BindingResolution`
- `BindingTargetDescriptor`
- `StatefulTargetDriver`
- `StatefulTargetHandle`
ACP, codex app server, and future products should compile down to those nouns instead of leaking product-specific routing rules through core.
## Proposed Capability Model
### Binding capability
The binding capability should support both configured bindings and runtime-created bindings.
Required operations:
- compile configured bindings at startup or reload
- resolve a binding from an inbound `ConversationRef`
- create a runtime binding
- touch and unbind an existing binding
- dispatch a resolved binding to its target
### Binding target descriptor
A resolved binding should point to a typed target descriptor rather than ad hoc ACP- or plugin-specific metadata blobs.
The descriptor should be able to represent at least:
- plugin-owned inbound claim targets
- stateful target drivers
That means the same binding capability can support both:
- codex app server plugin-bound conversations
- ACP configured bindings
without pretending they are the same product.
### Stateful target driver
This is the reusable control-plane contract for long-lived bound targets.
Required operations:
- `ensureReady`
- `runTurn`
- `cancel`
- `close`
- `reset`
- `status`
- `health`
ACP should remain the first built-in driver.
If the codex app server later proves that it also needs durable session handles, it can either:
- use a driver that consumes this contract, or
- keep its own product-owned runtime if that remains simpler
That should be a product decision, not something forced by the binding capability.
## Why ACP Kernel Stays In Core
ACP's kernel should remain in core because session lifecycle, persistence, retries, cancellation, and runtime-handle safety are generic platform machinery.
Those concerns are not channel-specific, and they are not codex-app-server-specific.
If we move that machinery into an ordinary plugin, we create circular bootstrapping:
- channels need it during startup and inbound routing
- reset and recovery need it when plugins may already be degraded
- failure semantics become special-case core logic anyway
If we later wrap it in a "built-in capability module", that is still effectively core.
## What Should Move Out Of Core
The following should move out of ACP-shaped core code:
- channel-specific configured binding matching
- channel-specific binding target normalization
- channel-specific recovery UX
- ACP-specific route wrapping helpers as named ACP seams
- codex app server fallback policy beyond generic plugin-bound dispatch behavior
The following should stay:
- generic binding storage and dispatch
- generic ACP control plane
- generic stateful target driver contract
## Current Problems To Remove
### Residual cleanup is now small
Most ACP-era compatibility names are gone from the generic seam.
The remaining cleanup is smaller:
- `src/acp/persistent-bindings.ts` compatibility barrel can be deleted once tests stop importing it
- ACP-named tests and mocks can be renamed over time for consistency
- docs should stop describing already-removed ACP wrappers as if they still exist
### Configured binding implementation is still too monolithic
`src/channels/plugins/configured-binding-registry.ts` still mixes:
- registry compilation
- cache invalidation
- inbound matching
- materialization of binding targets
- session-key reverse lookup
That file is now generic, but still too large and too coupled.
### Runtime-created plugin bindings still use a separate stack
`src/plugins/conversation-binding.ts` is still a separate implementation path for plugin-created bindings.
That means configured bindings and runtime-created bindings share storage, but not one consistent capability layer.
### Generic registries still hardcode ACP as a built-in
`src/channels/plugins/configured-binding-consumers.ts` and `src/channels/plugins/stateful-target-drivers.ts` still import ACP directly.
That is acceptable for now, but the clean final shape is to keep ACP built in while registering it from a dedicated bootstrap point instead of wiring it inside the generic registry files.
## Target Contracts
### Channel binding provider contract
Conceptually, each channel plugin should support:
- `compileConfiguredBinding(binding, cfg) -> CompiledBinding | null`
- `resolveInboundConversation(event) -> ConversationRef | null`
- `matchInboundConversation(compiledBinding, conversation) -> BindingMatch | null`
- `describeBinding(compiledBinding) -> string | undefined`
### Binding capability contract
Core should support:
- `compileConfiguredBindings(cfg, plugins) -> CompiledBindingRegistry`
- `resolveBinding(conversationRef) -> BindingResolution | null`
- `createRuntimeBinding(target, conversationRef, metadata) -> BindingRecord`
- `touchBinding(bindingId)`
- `unbindBinding(bindingId | target)`
- `dispatchResolvedBinding(bindingResolution, inboundEvent)`
### Stateful target driver contract
Core should support:
- `ensureReady(targetRef, cfg)`
- `runTurn(targetRef, input)`
- `cancel(targetRef, reason)`
- `close(targetRef, reason)`
- `reset(targetRef, reason)`
- `status(targetRef)`
- `health(targetRef)`
## File-Level Transition Plan
### Keep
- `src/infra/outbound/session-binding-service.ts`
- `src/acp/control-plane/*`
- `extensions/acpx/*`
### Generalize
- `src/plugins/conversation-binding.ts`
- fold runtime-created plugin bindings into the same generic binding capability instead of keeping a separate implementation stack
- `src/channels/plugins/configured-binding-registry.ts`
- split into compiler, matcher, and session-key resolution modules with a thin facade
- `src/channels/plugins/types.adapters.ts`
- finish removing ACP-era aliases after the deprecation window
- `src/plugin-sdk/conversation-runtime.ts`
- export only the generic binding capability surfaces
- `src/acp/persistent-bindings.lifecycle.ts`
- either become a generic stateful target driver consumer or be renamed to ACP driver-specific lifecycle code
### Shrink Or Delete
- `src/acp/persistent-bindings.ts`
- delete the compatibility barrel once tests import the real modules directly
- `src/acp/persistent-bindings.resolve.ts`
- keep only while ACP-specific compatibility helpers are still useful to internal callers
- ACP-named test files
- rename over time once the behavior is stable and there is no risk of mixing behavioral and naming churn
## Recommended Refactor Order
### Completed groundwork
The current branch has already completed most of the first migration wave:
- stable generic binding nouns exist
- configured bindings compile through a generic registry
- inbound routing goes through generic binding resolution
- configured binding lookup no longer performs fallback plugin discovery
- ACP is expressed as a configured-binding consumer plus a built-in stateful target driver
The remaining work is cleanup and unification, not first-principles redesign.
### Phase 1: Freeze the nouns
Introduce and document the stable binding and target types:
- `ConversationRef`
- `CompiledBinding`
- `BindingResolution`
- `BindingTargetDescriptor`
- `StatefulTargetDriver`
Do this before more movement so the rest of the refactor has firm vocabulary.
### Phase 2: Promote bindings to a first-class core capability
Refactor the existing generic binding store into an explicit capability layer.
Requirements:
- runtime-created bindings stay supported
- configured bindings become first-class
- lookup becomes channel-agnostic
### Phase 3: Compile configured bindings at startup and reload
Move configured binding compilation off the inbound hot path.
Requirements:
- load enabled channel plugins once
- compile configured bindings once
- rebuild on config or plugin reload
- inbound path becomes pure registry lookup
### Phase 4: Expand the channel provider seam
Replace the ACP-specific adapter shape with a generic channel binding provider contract.
Requirements:
- channel plugins own normalization and matching
- core no longer knows channel-specific configured binding rules
### Phase 5: Re-express ACP as a binding consumer plus built-in stateful target driver
Move ACP configured binding policy to the new binding capability while keeping ACP runtime orchestration in core.
Requirements:
- ACP configured bindings resolve through the generic binding registry
- ACP target readiness uses the ACP driver contract
- ACP-specific naming disappears from generic binding code
### Phase 6: Finish residual ACP cleanup
Remove the last compatibility leftovers and stale naming.
Requirements:
- delete `src/acp/persistent-bindings.ts`
- rename ACP-named tests where that improves clarity without changing behavior
- keep docs synchronized with the actual generic seam instead of the earlier transition state
### Phase 7: Split the configured binding registry by responsibility
Refactor `src/channels/plugins/configured-binding-registry.ts` into smaller modules.
Suggested split:
- compiler module
- inbound matcher module
- session-key reverse lookup module
- thin public facade
Requirements:
- caching behavior remains unchanged
- matching behavior remains unchanged
- session-key resolution behavior remains unchanged
### Phase 8: Keep codex app server on the same binding capability
Do not force the codex app server into ACP semantics.
Requirements:
- codex app server keeps runtime-created bindings through the same binding capability
- inbound claim remains the default delivery path
- only adopt the stateful target driver seam if the app server truly needs long-lived target orchestration
- `src/plugins/conversation-binding.ts` stops being a separate binding stack and becomes a consumer of the generic binding capability
### Phase 9: Decouple built-in ACP registration from generic registry files
Keep ACP built in, but stop importing it directly from the generic registry modules.
Requirements:
- `src/channels/plugins/configured-binding-consumers.ts` no longer hardcodes ACP imports
- `src/channels/plugins/stateful-target-drivers.ts` no longer hardcodes ACP imports
- ACP still registers by default during normal startup
- generic registry files remain product-agnostic
### Phase 10: Remove ACP-shaped compatibility facades
Once all call sites are on the generic capability:
- delete ACP-shaped routing helpers
- delete hot-path plugin bootstrapping logic
- keep only thin compatibility exports if external plugins still need a deprecation window
## Success Criteria
The architecture is done when all of these are true:
- no inbound configured-binding resolution performs plugin discovery
- no channel-specific binding semantics remain in generic core binding code
- ACP still uses a core session kernel
- codex app server and ACP both sit on top of the same binding capability
- the binding capability can represent both configured and runtime-created bindings
- runtime-created plugin bindings do not use a separate implementation stack
- long-lived target orchestration is shared through a small core driver contract
- generic registry files do not import ACP directly
- ACP-era alias names are gone from the generic/plugin SDK surface
- the main harness is not forced into the ACP engine
- external plugins can use the same capability without internal imports
## Non-Goals
These are not goals of the remaining refactor:
- moving the ACP session kernel into an ordinary plugin
- forcing the main harness, ACP, and codex app server into one executor
- making every channel implement its own retry and session-safety logic
- keeping ACP-shaped naming in the long-term generic binding layer
## Bottom Line
The right 20-year split is:
- bindings are the shared core capability
- ACP session orchestration remains a small built-in core kernel
- channel plugins own binding semantics
- backend plugins own runtime protocol details
- product consumers like ACP configured bindings and codex app server build on the same binding capability without being forced into one runtime engine
That is the leanest core that still has honest boundaries.

View File

@ -1,4 +1,4 @@
// Private runtime barrel for the bundled Google Chat extension.
// Keep this seam thin and aligned with the curated plugin-sdk/googlechat surface.
// Keep this barrel thin and aligned with the curated plugin-sdk/googlechat surface.
export * from "openclaw/plugin-sdk/googlechat";

View File

@ -85,7 +85,7 @@ export function resolveMatrixVoiceDecision(opts: {
function isMatrixVoiceCompatibleAudio(opts: { contentType?: string; fileName?: string }): boolean {
// Matrix currently shares the core voice compatibility policy.
// Keep this wrapper as the seam if Matrix policy diverges later.
// Keep this wrapper as the boundary if Matrix policy diverges later.
return getCore().media.isVoiceCompatibleAudio({
contentType: opts.contentType,
fileName: opts.fileName,

View File

@ -75,7 +75,7 @@ function main() {
console.error(`- ${relative}`);
}
console.error(
"Publish a focused openclaw/plugin-sdk/<subpath> seam or use the extension's own public barrel instead.",
"Publish a focused openclaw/plugin-sdk/<subpath> surface or use the extension's own public barrel instead.",
);
process.exit(1);
}

View File

@ -8,7 +8,7 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
},
{
pattern: /["']openclaw\/plugin-sdk\/test-utils["']/,
hint: "Use openclaw/plugin-sdk/testing for the public extension test seam.",
hint: "Use openclaw/plugin-sdk/testing for the public extension test surface.",
},
{
pattern: /["']openclaw\/plugin-sdk\/compat["']/,
@ -20,7 +20,7 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
},
{
pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/,
hint: "Use test/helpers/extensions/* for repo-only helpers, or openclaw/plugin-sdk/testing for public seams.",
hint: "Use test/helpers/extensions/* for repo-only helpers, or openclaw/plugin-sdk/testing for public surfaces.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/,
@ -81,7 +81,7 @@ function main() {
if (offenders.length > 0) {
console.error(
"Extension test files must stay on extension test bridges or public plugin-sdk seams.",
"Extension test files must stay on extension test bridges or public plugin-sdk surfaces.",
);
for (const offender of offenders.toSorted((a, b) => a.file.localeCompare(b.file))) {
const relative = path.relative(process.cwd(), offender.file) || offender.file;

View File

@ -711,7 +711,7 @@ describe("telegramMessageActions", () => {
}
});
it("forwards telegram action aliases into the runtime seam", async () => {
it("forwards telegram action aliases into the runtime interface", async () => {
const cases = [
{
name: "media-only send preserves asVoice",

View File

@ -923,7 +923,7 @@ export const FIELD_HELP: Record<string, string> = {
"Forces a session memory-search reindex after compaction-triggered transcript updates (default: true). Keep enabled when compacted summaries must be immediately searchable, or disable to reduce write-time indexing pressure.",
ui: "UI presentation settings for accenting and assistant identity shown in control surfaces. Use this for branding and readability customization without changing runtime behavior.",
"ui.seamColor":
"Primary accent/seam color used by UI surfaces for emphasis, badges, and visual identity cues. Use high-contrast values that remain readable across light/dark themes.",
"Primary accent color used by UI surfaces for emphasis, badges, and visual identity cues. Use high-contrast values that remain readable across light/dark themes.",
"ui.assistant":
"Assistant display identity settings for name and avatar shown in UI surfaces. Keep these values aligned with your operator-facing persona and support expectations.",
"ui.assistant.name":

View File

@ -9,7 +9,7 @@ vi.mock("../plugins/provider-runtime.js", () => ({
let resolveProviderAuths: typeof import("./provider-usage.auth.js").resolveProviderAuths;
describe("resolveProviderAuths plugin seam", () => {
describe("resolveProviderAuths plugin boundary", () => {
beforeEach(async () => {
vi.resetModules();
resolveProviderUsageAuthWithPluginMock.mockReset();

View File

@ -16,7 +16,7 @@ let loadProviderUsageSummary: typeof import("./provider-usage.load.js").loadProv
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
describe("provider-usage.load plugin seam", () => {
describe("provider-usage.load plugin boundary", () => {
beforeEach(async () => {
vi.resetModules();
resolveProviderUsageSnapshotWithPluginMock.mockReset();

View File

@ -64,7 +64,7 @@ export async function mergeHybridResults(params: {
mmr?: Partial<MMRConfig>;
/** Temporal decay configuration for recency-aware scoring */
temporalDecay?: Partial<TemporalDecayConfig>;
/** Test seam for deterministic time-dependent behavior */
/** Test hook for deterministic time-dependent behavior */
nowMs?: number;
}): Promise<
Array<{

View File

@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const ALLOWED_EXTENSION_PUBLIC_SEAMS = new Set([
const ALLOWED_EXTENSION_PUBLIC_SURFACES = new Set([
"action-runtime.runtime.js",
"api.js",
"index.js",
@ -320,8 +320,8 @@ function expectOnlyApprovedExtensionSeams(file: string, imports: string[]): void
}
const basename = normalized.split("/").at(-1) ?? "";
expect(
ALLOWED_EXTENSION_PUBLIC_SEAMS.has(basename),
`${file} should only import approved extension seams, got ${specifier}`,
ALLOWED_EXTENSION_PUBLIC_SURFACES.has(basename),
`${file} should only import approved extension surfaces, got ${specifier}`,
).toBe(true);
}
}
@ -386,19 +386,19 @@ describe("channel import guardrails", () => {
}
});
it("keeps core extension imports limited to approved public seams", () => {
it("keeps core extension imports limited to approved public surfaces", () => {
for (const file of collectCoreSourceFiles()) {
expectOnlyApprovedExtensionSeams(file, collectExtensionImports(readFileSync(file, "utf8")));
}
});
it("keeps extension-to-extension imports limited to approved public seams", () => {
it("keeps extension-to-extension imports limited to approved public surfaces", () => {
for (const file of collectExtensionSourceFiles()) {
expectOnlyApprovedExtensionSeams(file, collectExtensionImports(readFileSync(file, "utf8")));
}
});
it("keeps internalized extension helper seams behind local api barrels", () => {
it("keeps internalized extension helper surfaces behind local api barrels", () => {
for (const extensionId of LOCAL_EXTENSION_API_BARREL_GUARDS) {
for (const file of collectExtensionFiles(extensionId)) {
const normalized = file.replaceAll("\\", "/");

View File

@ -152,7 +152,7 @@ function readExportStatements(path: string): string[] {
}
describe("runtime api guardrails", () => {
it("keeps runtime api seams on an explicit export allowlist", () => {
it("keeps runtime api surfaces on an explicit export allowlist", () => {
const runtimeApiFiles = collectRuntimeApiFiles();
expect(runtimeApiFiles).toEqual(
expect.arrayContaining(Object.keys(RUNTIME_API_EXPORT_GUARDS).toSorted()),

View File

@ -185,7 +185,7 @@ describe("plugin-sdk subpath exports", () => {
expectTypeOf<CoreChannelMessageActionContext>().toMatchTypeOf<ChannelMessageActionContext>();
});
it("exports the public testing seam", () => {
it("exports the public testing surface", () => {
expect(typeof testingSdk.removeAckReactionAfterReply).toBe("function");
expect(typeof testingSdk.shouldAckReaction).toBe("function");
});
@ -284,7 +284,7 @@ describe("plugin-sdk subpath exports", () => {
expect(typeof googlechatSdk.resolveGoogleChatGroupRequireMention).toBe("function");
});
it("keeps the Google Chat runtime seam aligned with the public SDK subpath", async () => {
it("keeps the Google Chat runtime surface aligned with the public SDK subpath", async () => {
const googlechatRuntimeApi = await import("../../extensions/googlechat/runtime-api.js");
expect(typeof googlechatRuntimeApi.buildChannelConfigSchema).toBe("function");
@ -338,7 +338,7 @@ describe("plugin-sdk subpath exports", () => {
}
});
it("does not advertise trimmed legacy extension helper seams", () => {
it("does not advertise trimmed legacy extension helper surfaces", () => {
for (const id of trimmedLegacyExtensionSubpaths) {
expect(pluginSdkSubpaths).not.toContain(id);
}

View File

@ -122,7 +122,7 @@ describe("buildPluginStatusReport", () => {
configSchema: false,
},
],
diagnostics: [{ level: "warn", pluginId: "google", message: "watch this seam" }],
diagnostics: [{ level: "warn", pluginId: "google", message: "watch this surface" }],
channels: [],
channelSetups: [],
providers: [],
@ -175,7 +175,7 @@ describe("buildPluginStatusReport", () => {
hasAllowedModelsConfig: true,
});
expect(inspect?.diagnostics).toEqual([
{ level: "warn", pluginId: "google", message: "watch this seam" },
{ level: "warn", pluginId: "google", message: "watch this surface" },
]);
});

View File

@ -1,4 +1,4 @@
// Lobster palette tokens for CLI/UI theming. "lobster seam" == use this palette.
// Lobster palette tokens for CLI/UI theming. Use this palette for all CLI color output.
// Keep in sync with docs/cli/index.md (CLI palette section).
export const LOBSTER_PALETTE = {
accent: "#FF5A2D",