refactor(prism-facet): strip content into ClawPrompts; expose registration API #4

Merged
hzhang merged 3 commits from feat/registration-api-strip-content into main 2026-05-25 09:52:54 +00:00
Contributor

Makes PrismFacet a pure framework. All Hangman-Lab-specific content (always router, pcexec-convention prompt, fabric-chat-injector hook) moves to a new sibling plugin ClawPrompts (private, at hzhang/ClawPrompts) which registers them via the new globalThis.__prismFacet.addRouter / addRule cross-plugin API.

New core/cross-plugin-api.ts is installed at module-import time so a consumer plugin loaded before PrismFacet still finds a working API.

rule-store now tiers rules into persistent (rules.json, mutated by the prompt-rules tool) and external (in-memory, registered by other plugins via the API). Persistent overrides external on conflict.

Nothing in this PR is plumbed to TODO(phase-2) fabric xType DM-only narrowing yet — that work still belongs in Fabric.OpenclawPlugin first.

Makes PrismFacet a pure framework. All Hangman-Lab-specific content (always router, pcexec-convention prompt, fabric-chat-injector hook) moves to a new sibling plugin **ClawPrompts** (private, at hzhang/ClawPrompts) which registers them via the new `globalThis.__prismFacet.addRouter / addRule` cross-plugin API. New core/cross-plugin-api.ts is installed at module-import time so a consumer plugin loaded **before** PrismFacet still finds a working API. rule-store now tiers rules into `persistent` (rules.json, mutated by the prompt-rules tool) and `external` (in-memory, registered by other plugins via the API). Persistent overrides external on conflict. Nothing in this PR is plumbed to TODO(phase-2) fabric xType DM-only narrowing yet — that work still belongs in Fabric.OpenclawPlugin first.
hzhang added 1 commit 2026-05-25 09:22:33 +00:00
Strip all Hangman-Lab-specific content out of PrismFacet so it can be
reused by any project. Content (always router, pcexec-convention prompt,
fabric-chat-injector hook) moves to the new sibling plugin ClawPrompts.

Mechanism additions:
- `globalThis.__prismFacet` cross-plugin API installed at module-import
  time (so consumers loaded before PrismFacet can still register):
    .addRouter(name, resolveFn)
    .addRule(router, key, { file })
- core/rule-store: tier rules into `persistent` (rules.json, mutated by
  the prompt-rules admin tool) and `external` (in-memory, registered by
  other plugins via the API). Persistent overrides external on conflict.
- core/router-loader: addExternalRouter() for programmatic registration
  into the same map the file-based loader uses.
- index.ts: drops registerFabricChatInjector wiring, registerBeforePromptBuild
  remains.

Removed (now shipped from ClawPrompts):
- plugin/routers/always.ts
- plugin/hooks/fabric-chat-injector.ts
- plugin/prompts/pcexec-convention.md
- plugin/rules.json: now `{}`; ClawPrompts registers its rule externally

What still lives in PrismFacet:
- before_prompt_build hook (the wiring between routers/rules and the
  agent's system prompt)
- prompt-rules admin tool (lists + mutates persistent rules)
- file-based routersDir / rulesFile scanning (kept for operator ad-hoc
  use; ClawPrompts uses the API instead)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hzhang added 1 commit 2026-05-25 09:25:42 +00:00
Plugin load order is undefined. If a consumer (ClawPrompts) loads
before PrismFacet, it can't call __prismFacet.addRouter directly
(slot is undefined). Now it pushes ops onto the well-known
__prismFacetPending array; PrismFacet's installCrossPluginApi()
drains the queue when it runs. Marker __real: true on the installed
API so consumers can tell stub from real.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hzhang added 1 commit 2026-05-25 09:31:19 +00:00
Two related fixes:

1. core/router-loader.loadRouters() previously called map.clear() before
   scanning routersDir, which wiped routers registered via
   __prismFacet.addRouter (the cross-plugin API). Now: track which
   entries in the map came from a file vs API (filePath sentinel),
   only delete file-based ones that disappeared between loads. External
   routers are never touched.

2. scripts/install.mjs: chown installed plugin files to root when the
   installer is running as root. openclaw 2026.5+ blocks plugins whose
   files are owned by non-root; rsync/tar from a developer laptop
   silently broke prism-facet on the next gateway restart. Matches the
   Meridian + ClawPrompts install.mjs fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Contributor

Sim e2e on dind-t2 — 7 plugins listening, drain verified

Gateway start log (load order is claw-prompts first, prism-facet later):

[claw-prompts] __prismFacet not yet installed — queued router+rule for drain when PrismFacet loads
[claw-prompts] plugin registered
[prism-facet] plugin registered (framework only — content via ClawPrompts)
http server listening (7 plugins: claw-prompts, dialectic, fabric, harbor-forge, meridian, padded-cell, prism-facet)

Runtime sanity after restart confirms ClawPrompts's registrations actually landed:

routers: [ 'always' ]
rules: {"always:always":"/root/.openclaw/plugins/claw-prompts/prompts/pcexec-convention.md"}
tiered: { persistent: {}, external: { "always:always": "..." } }

Tiered view shows the rule came in via the external tier (the cross-plugin API), not persistent (rules.json) — i.e. ClawPrompts owns it, not PrismFacet.

Two fixes piggybacked in this PR after the first restart attempt surfaced bugs:

  • loadRouters no longer map.clear()s blindly — that wiped externally-registered routers added before PrismFacet's register(). Now it only deletes file-based routers that disappeared between loads; API-registered routers (filePath sentinel <external: ...>) survive.
  • scripts/install.mjs chowns installed dir to root (same hardening Meridian + ClawPrompts have) — fixes the openclaw 2026.5 "suspicious ownership" block when source was tar-pushed from a uid-1000 host.

ClawPrompts repo: https://git.hangman-lab.top/hzhang/ClawPrompts (private, initial commit on main: 2d685c6).

TODO(phase-2) fabric xType DM-only narrowing — still NOT done. The fabric-chat-injector hook today fires for any fabric channel triggered turn; Fabric.OpenclawPlugin would need to expose per-channel type info via __fabric cross-plugin API first. Tracked in the hook's comment.

**Sim e2e on dind-t2 — 7 plugins listening, drain verified** Gateway start log (load order is claw-prompts first, prism-facet later): ``` [claw-prompts] __prismFacet not yet installed — queued router+rule for drain when PrismFacet loads [claw-prompts] plugin registered [prism-facet] plugin registered (framework only — content via ClawPrompts) http server listening (7 plugins: claw-prompts, dialectic, fabric, harbor-forge, meridian, padded-cell, prism-facet) ``` Runtime sanity after restart confirms ClawPrompts's registrations actually landed: ``` routers: [ 'always' ] rules: {"always:always":"/root/.openclaw/plugins/claw-prompts/prompts/pcexec-convention.md"} tiered: { persistent: {}, external: { "always:always": "..." } } ``` Tiered view shows the rule came in via the **external** tier (the cross-plugin API), not persistent (rules.json) — i.e. ClawPrompts owns it, not PrismFacet. Two fixes piggybacked in this PR after the first restart attempt surfaced bugs: - `loadRouters` no longer `map.clear()`s blindly — that wiped externally-registered routers added before PrismFacet's `register()`. Now it only deletes file-based routers that disappeared between loads; API-registered routers (filePath sentinel `<external: ...>`) survive. - `scripts/install.mjs` chowns installed dir to root (same hardening Meridian + ClawPrompts have) — fixes the openclaw 2026.5 "suspicious ownership" block when source was tar-pushed from a uid-1000 host. ClawPrompts repo: https://git.hangman-lab.top/hzhang/ClawPrompts (private, initial commit on main: `2d685c6`). TODO(phase-2) fabric xType DM-only narrowing — still **NOT done**. The fabric-chat-injector hook today fires for any fabric channel triggered turn; Fabric.OpenclawPlugin would need to expose per-channel type info via `__fabric` cross-plugin API first. Tracked in the hook's comment.
hzhang merged commit cf03e218af into main 2026-05-25 09:52:54 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: zhi/PrismFacet#4