feat(reporting-governance): add profile artifact binding slice
This commit is contained in:
@@ -5,3 +5,4 @@ export { runSenderBindingAdapter } from './sender-binding.mjs';
|
||||
export { runOrchestratorAdapter } from './orchestrator.mjs';
|
||||
|
||||
export { createRuntimeBinding } from './runtime-binding.mjs';
|
||||
export { loadDeploymentProfileArtifact, createDeploymentBindingContract } from '../storage/profile-artifact.mjs';
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# OpenClaw Watchdog Reference Runtime Composition
|
||||
|
||||
This file places the existing watchdog auto-notify chain inside the reporting-governance package skeleton as a **reference implementation**.
|
||||
|
||||
## Placement decision
|
||||
|
||||
The watchdog chain is **not** part of `src/core/`.
|
||||
It is categorized under `src/reference/` and represented through package adapter boundaries in `src/adapters/`.
|
||||
|
||||
## Why
|
||||
|
||||
The chain is runtime-specific and deployment-shaped:
|
||||
|
||||
- it depends on OpenClaw execution patterns
|
||||
- it depends on queue / spool / bridge / sender boundaries
|
||||
- it reflects truthful delivery-state handling across runtime hops
|
||||
|
||||
Those are exactly the properties of a reference adapter composition, not runtime-agnostic governance core.
|
||||
|
||||
## Reference chain
|
||||
|
||||
```text
|
||||
scripts/long_task_watchdog.mjs
|
||||
-> scripts/operator_notify_dispatcher.mjs
|
||||
-> scripts/operator_notify_bridge_supervisor.mjs
|
||||
-> scripts/operator_notify_sender_binding.mjs
|
||||
-> acked | blocked | pending_external_send
|
||||
```
|
||||
|
||||
## Package mapping
|
||||
|
||||
- `src/adapters/watchdog-adapter.mjs` → watchdog trigger + canonical event seeding
|
||||
- `src/adapters/dispatcher-adapter.mjs` → queue to spool handoff
|
||||
- `src/adapters/bridge-adapter.mjs` → spool consumption + receipt writeback
|
||||
- `src/adapters/sender-binding-adapter.mjs` → sender contract boundary
|
||||
- `src/adapters/orchestrator-adapter.mjs` → deterministic composition entrypoint
|
||||
|
||||
## Runtime artifact classes
|
||||
|
||||
The current reference implementation still writes runtime artifacts in repo-level state directories such as:
|
||||
|
||||
- `state/long-task-watchdog/`
|
||||
- `state/long-task-watchdog-events/`
|
||||
- `state/operator-notify-queue/`
|
||||
- `state/operator-notify-dispatch-spool/`
|
||||
- `state/operator-notify-bridge-receipts/`
|
||||
- `state/operator-notify-sender-attempts/`
|
||||
|
||||
Future package extraction should preserve these artifact classes through `src/storage/` contracts rather than raw script coupling.
|
||||
1
plugins/reporting-governance/src/storage/index.mjs
Normal file
1
plugins/reporting-governance/src/storage/index.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { loadDeploymentProfileArtifact, createDeploymentBindingContract } from './profile-artifact.mjs';
|
||||
@@ -0,0 +1,54 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const packageRoot = path.resolve(import.meta.dirname, '..', '..');
|
||||
const repoRoot = path.resolve(packageRoot, '..', '..');
|
||||
|
||||
function readJsonFile(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
export function resolvePackageArtifactPath(...segments) {
|
||||
return path.resolve(packageRoot, ...segments);
|
||||
}
|
||||
|
||||
export function loadDeploymentProfileArtifact({ artifactPath, profileId } = {}) {
|
||||
const resolvedPath = path.resolve(
|
||||
artifactPath
|
||||
?? resolvePackageArtifactPath('profiles', `${profileId ?? 'strict-manager-mode'}.profile.json`)
|
||||
);
|
||||
const artifact = readJsonFile(resolvedPath);
|
||||
return {
|
||||
artifactPath: resolvedPath,
|
||||
artifact,
|
||||
};
|
||||
}
|
||||
|
||||
export function createDeploymentBindingContract({ artifact, repoRootOverride } = {}) {
|
||||
if (!artifact?.spec?.bindings) {
|
||||
throw new Error('deployment profile artifact bindings are required');
|
||||
}
|
||||
|
||||
const root = path.resolve(repoRootOverride ?? repoRoot);
|
||||
const scripts = Object.fromEntries(
|
||||
Object.entries(artifact.spec.bindings.scripts ?? {}).map(([key, relativePath]) => [key, path.resolve(root, relativePath)])
|
||||
);
|
||||
const artifactRoots = Object.fromEntries(
|
||||
Object.entries(artifact.spec.bindings.artifact_roots ?? {}).map(([key, relativePath]) => [key, path.resolve(root, relativePath)])
|
||||
);
|
||||
|
||||
return {
|
||||
runtime: artifact.spec.bindings.runtime ?? artifact.metadata?.runtime ?? 'unknown-runtime',
|
||||
entrypoint: path.resolve(root, artifact.spec.bindings.entrypoint),
|
||||
pluginVersion: artifact.spec?.package?.pluginVersion ?? null,
|
||||
compatibilityMode: artifact.metadata?.compatibility_mode ?? 'strict_envelope',
|
||||
scripts,
|
||||
artifactRoots,
|
||||
};
|
||||
}
|
||||
|
||||
export const __testables = {
|
||||
packageRoot,
|
||||
repoRoot,
|
||||
readJsonFile,
|
||||
};
|
||||
Reference in New Issue
Block a user