Skip to content

build(plugins): enforce extension package root boundaries#61498

Open
huntharo wants to merge 12 commits intomainfrom
codex/boundaries-p3-restart
Open

build(plugins): enforce extension package root boundaries#61498
huntharo wants to merge 12 commits intomainfrom
codex/boundaries-p3-restart

Conversation

@huntharo
Copy link
Copy Markdown
Member

@huntharo huntharo commented Apr 5, 2026

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: bundled plugins could still reach into src/** by relative path, and package-local tsconfig.json files alone did not make TypeScript fail closed.
  • Why it matters: contributors and AI agents were still able to import forbidden core surfaces without normal tsc feedback, so boundary enforcement depended too heavily on custom scripts and convention.
  • What changed: I added package-local extension TS projects with rootDir: ".", pointed extension TS resolution at emitted Plugin SDK declaration surfaces instead of raw src/plugin-sdk/*.ts, added explicit devDependencies.openclaw = "workspace:*" for bundled plugins, and taught postinstall to build the declaration surface for source checkouts.
  • What did NOT change (scope boundary): I did not split the whole repo into separate packages, move core directories, or wire the entire extensions/ tree into the root lint pipeline.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: TypeScript was resolving extension-allowed imports through raw repo source paths, and extension projects had no rootDir boundary to reject relative escapes.
  • Missing detection / guardrail: package-local tsconfig.json files existed only as topology on the earlier prototype branch; they did not yet combine declaration-based package resolution with rootDir enforcement.
  • Contributing context (if known): root lint intentionally excludes most of extensions/, so the default local loop did not surface these boundary breaks early enough.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/plugins/contracts/extension-package-project-boundaries.test.ts
    • test/extension-package-tsc-boundary.test.ts
    • src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts
    • test/scripts/postinstall-bundled-plugins.test.ts
  • Scenario the test should lock in: a bundled plugin like extensions/xai typechecks cleanly when it only imports openclaw/plugin-sdk/* and same-package files, but fails with TS6059 when it imports ../../src/cli/acp-cli.ts.
  • Why this is the smallest reliable guardrail: it proves both sides of the contract without requiring a full repo package split or editor automation in CI.
  • Existing test that already covers this (if any): the existing boundary script tests still cover the policy-script layer.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • Extension developers now get normal TypeScript TS6059 failures when a bundled plugin reaches outside its own package root by relative import.
  • Source checkouts now regenerate the Plugin SDK declaration surface during postinstall so extension-local editor/typecheck flows have the declaration contract available.

Diagram (if applicable)

Before:
extension file -> ../../src/cli/... -> TypeScript accepts import -> custom boundary script fails later

After:
extension file -> ../../src/cli/... -> extension rootDir rejects import with TS6059
extension file -> openclaw/plugin-sdk/... -> emitted SDK declarations resolve cleanly

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local source checkout
  • Model/provider: N/A
  • Integration/channel (if any): bundled plugin packages, verified with extensions/xai
  • Relevant config (redacted): default repo checkout with generated dist/plugin-sdk declarations

Steps

  1. Run ./node_modules/.bin/tsc -p tsconfig.plugin-sdk.dts.json.
  2. Run ./node_modules/.bin/tsc -p extensions/xai/tsconfig.json --noEmit.
  3. Add a temporary canary file in extensions/xai importing ../../src/cli/acp-cli.ts, rerun the xAI typecheck, and run node scripts/check-extension-plugin-sdk-boundary.mjs --mode=relative-outside-package.

Expected

  • Clean extension imports typecheck.
  • Illegal relative imports outside the extension package fail under normal tsc and the boundary script.

Actual

  • Clean extensions/xai typecheck passed.
  • Canary import failed with TS6059.
  • Boundary script reported the src/cli/acp-cli.ts escape.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • ./node_modules/.bin/tsc -p tsconfig.plugin-sdk.dts.json
    • ./node_modules/.bin/tsc -p extensions/xai/tsconfig.json --noEmit
    • ./node_modules/.bin/vitest run src/plugins/contracts/extension-package-project-boundaries.test.ts test/extension-package-tsc-boundary.test.ts test/scripts/postinstall-bundled-plugins.test.ts src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts
    • node scripts/check-extension-plugin-sdk-boundary.mjs --mode=relative-outside-package
    • deleted dist/plugin-sdk, ran node scripts/postinstall-bundled-plugins.mjs, then reran ./node_modules/.bin/tsc -p extensions/xai/tsconfig.json --noEmit
  • Edge cases checked:
    • allowed openclaw/plugin-sdk/* imports still typecheck in extensions/xai
    • temporary canary import from ../../src/cli/acp-cli.ts fails with TS6059
    • source-checkout postinstall rebuilds the declaration surface needed by extension-local TS projects
  • What you did not verify:
    • full repo pnpm check
    • full repo pnpm test
    • VS Code UI itself, though the same TS project settings now drive tsc

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: some Plugin SDK declaration surfaces still depend on emitted declaration generation being available in source checkouts.
    • Mitigation: postinstall now builds the declaration surface automatically, and the new postinstall tests lock that in.
  • Risk: a few bundled plugin package manifests gained devDependencies.openclaw, which could drift over time.
    • Mitigation: src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts now enforces that contract.

Post-Deploy Monitoring & Validation

No additional operational monitoring required. This PR changes local/compiler boundary enforcement and source-checkout bootstrap behavior, not runtime gateway behavior.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 5, 2026

Too many files changed for review. (167 files found, 100 file limit)

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser extensions: copilot-proxy Extension: copilot-proxy extensions: diagnostics-otel Extension: diagnostics-otel extensions: llm-task Extension: llm-task extensions: lobster Extension: lobster extensions: memory-core Extension: memory-core extensions: memory-lancedb Extension: memory-lancedb extensions: open-prose Extension: open-prose scripts Repository scripts channel: feishu Channel integration: feishu channel: twitch Channel integration: twitch labels Apr 5, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle openclaw-barnacle Bot closed this Apr 5, 2026
@huntharo huntharo added the bad-barnacle Suppress Barnacle automation on this issue or PR. label Apr 5, 2026
@huntharo huntharo reopened this Apr 5, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 692dea7fae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"compilerOptions": {
"rootDir": "."
},
"include": ["./*.ts", "./*.mts", "./*.cts", "./src/**/*.ts", "./src/**/*.tsx"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude test-support files from extension TS project includes

The new project glob includes every src/**/*.ts file, which pulls in non-runtime helpers like extensions/telegram/src/test-support/write-skill.ts and extensions/telegram/src/test-support/inbound-context-contract.ts; those files import ../../../../src/... and immediately violate the new rootDir: "." boundary with TS6059. As a result, tsc -p extensions/telegram/tsconfig.json --noEmit now fails even when production plugin code is clean, so the boundary project is unusable for affected extensions.

Useful? React with 👍 / 👎.

Comment on lines +145 to +146
if (env?.[DISABLE_PLUGIN_SDK_DTS_ENV]?.trim()) {
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the existing postinstall kill switch for DTS build

The new declaration-build step only checks OPENCLAW_DISABLE_PLUGIN_SDK_DTS_POSTINSTALL, but the script entrypoint now always calls it before runBundledPluginPostinstall, so setting the existing OPENCLAW_DISABLE_BUNDLED_PLUGIN_POSTINSTALL flag no longer disables all postinstall work. In source checkouts that rely on the legacy flag to keep installs side-effect-free, this unexpectedly runs tsc anyway (or emits warnings if TypeScript is unavailable).

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui agents Agent runtime and tooling labels Apr 5, 2026
@huntharo huntharo force-pushed the codex/boundaries-p3-restart branch from 4889f15 to 952e35c Compare April 5, 2026 20:52
@openclaw-barnacle openclaw-barnacle Bot removed the agents Agent runtime and tooling label Apr 5, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 952e35c1d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/config/types.agents.ts Outdated
Comment on lines +74 to +75
/** Optional per-agent default verbose level. */
verboseDefault?: "off" | "on" | "full";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove duplicated verboseDefault field from AgentConfig

This commit adds a second verboseDefault property to AgentConfig, which causes TypeScript to emit TS2300: Duplicate identifier 'verboseDefault' when this type is compiled. Because src/config/types.agents.ts is part of the main source tree, normal typecheck/build workflows that include this file will fail until one of the duplicate declarations is removed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02e54a759c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

description: "Matrix channel plugin (matrix-js-sdk)",
register: registerMatrixCliMetadata,
});
export { registerMatrixCliMetadata } from "./src/cli-metadata.js";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Export a plugin entry from matrix cli-metadata module

In bundled cli-metadata mode, the loader always prefers cli-metadata.ts and resolves it through resolvePluginModuleExport, which only accepts a default function/exported definition with register/activate. This file now exports only registerMatrixCliMetadata, so the loader gets no register function and records the Matrix plugin as a load error (missing register/activate export), which prevents Matrix CLI metadata registration in that mode.

Useful? React with 👍 / 👎.

"compilerOptions": {
"rootDir": "."
},
"include": ["./*.ts", "./*.mts", "./*.cts", "./src/**/*.ts", "./src/**/*.tsx"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude Discord test-support files from tsconfig includes

This new include glob pulls src/test-support/component-runtime.ts into the package project, and that helper imports ../../../../src/plugins/conversation-binding.js and ../../../../src/security/dm-policy-shared.js outside the package root. With rootDir: ".", tsc -p extensions/discord/tsconfig.json --noEmit now hits TS6059 even when production plugin code is clean, so the boundary project is unusable for Discord until test-support files are excluded (fresh evidence beyond the existing Telegram comment: this same regression now occurs in Discord).

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui bad-barnacle Suppress Barnacle automation on this issue or PR. channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: irc channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: qqbot channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: twitch Channel integration: twitch channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser docs Improvements or additions to documentation extensions: acpx extensions: anthropic extensions: byteplus extensions: cloudflare-ai-gateway extensions: copilot-proxy Extension: copilot-proxy extensions: deepseek extensions: diagnostics-otel Extension: diagnostics-otel extensions: duckduckgo extensions: fal extensions: huggingface extensions: kilocode extensions: kimi-coding extensions: llm-task Extension: llm-task extensions: lobster Extension: lobster extensions: memory-core Extension: memory-core extensions: memory-lancedb Extension: memory-lancedb extensions: minimax extensions: moonshot extensions: nvidia extensions: open-prose Extension: open-prose extensions: openai extensions: qianfan extensions: stepfun extensions: synthetic extensions: tavily extensions: together extensions: venice extensions: vercel-ai-gateway extensions: volcengine extensions: xiaomi maintainer Maintainer-authored PR scripts Repository scripts size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant