feat(reporting-governance): add profile artifact binding slice

This commit is contained in:
Eve
2026-05-08 10:07:26 +08:00
parent 000f6b6a8b
commit 6366f70491
15 changed files with 695 additions and 6 deletions

View File

@@ -9,6 +9,7 @@ Current purpose:
- fix boundaries between `core/`, `adapters/`, `storage/`, and reference implementations
- prepare the next implementation round for evaluator / decision-runner extraction
- provide a minimal package-level policy evaluator and decision runner skeleton that can be verified in isolation
- add one minimal package-owned deployment profile artifact / loader / binding contract slice that is executable in tests
## Package skeleton
@@ -17,6 +18,7 @@ plugins/reporting-governance/
package.json
README.md
capabilities/
profiles/
docs/
examples/
src/
@@ -62,6 +64,7 @@ Durable I/O contracts for governance artifacts:
- queue items
- spool artifacts
- receipts
- decision/profile/package artifacts
- future decisions / audit manifests
### `src/reference/`
@@ -99,6 +102,23 @@ Compatibility posture for this slice:
- Adding a symbol to a file under `src/` does **not** mean it is public unless wired through package `exports`.
- Future tightening of root/adapters exports may still be a breaking change until a stable `1.0` surface is declared.
### Compatibility envelope vs legacy compatibility mode
This slice now makes the boundary explicit:
- **compatibility envelope present** = caller provides a deployment profile and/or package version pin, so `runCompatibilityPreflight(...)` must enforce canonical schema paths, declared plugin compatibility, required expectations, and action support **fail-closed**.
- **legacy compatibility mode** = caller omits profile + package version entirely, so preflight keeps old call sites alive, records the missing version pin as a note, and does **not** fail only because descriptor schema/version metadata drifted.
Hard rule:
- legacy mode is a caller-compatibility concession, **not** a relaxed truth model.
- once any profile/package compatibility envelope is supplied, schema mismatch becomes blocking again.
Practical migration rule:
- new integrations should always send a profile artifact or package version pin.
- old integrations may temporarily call without one, but should treat returned notes as migration debt.
Practical migration rule:
- depend on package root exports or declared adapter subpaths only
@@ -117,6 +137,7 @@ Package-home documentation:
- `src/reference/openclaw-watchdog-chain.md`
- `capabilities/openclaw-watchdog-reference.json`
- `profiles/strict-manager-mode.profile.json`
Mainline background specs remain in:
@@ -124,6 +145,38 @@ Mainline background specs remain in:
- `docs/specs/reporting-governance-adapter-interface.md`
- `docs/specs/reporting-governance-deployment-model.md`
## Minimal profile artifact / loader / binding contract slice
This round adds one small but real package artifact path:
- package artifact: `profiles/strict-manager-mode.profile.json`
- loader: `src/storage/profile-artifact.mjs#loadDeploymentProfileArtifact(...)`
- binding contract: `src/storage/profile-artifact.mjs#createDeploymentBindingContract(...)`
What this slice does:
1. package ships a profile artifact snapshot under package boundary
2. loader resolves that artifact from package-local path
3. binding contract translates profile-declared script/artifact roots into concrete repo/runtime paths
4. adapter runtime binding can be instantiated from that contract in tests
What this slice does **not** claim yet:
- full profile schema validation pipeline
- automatic YAML -> artifact generation
- generalized multi-profile packaging
- production deployment installer
It is intentionally the smallest verifiable step that proves package profile artifacts are executable inputs rather than documentation only.
## Current reference composition
The current reference composition is the OpenClaw watchdog chain:
```text
watchdog -> queue -> dispatcher -> bridge -> sender binding -> acked|blocked|pending_external_send
```
## Minimal evaluator / decision runner now included
The current package now includes a small but runnable `core/` implementation:
@@ -179,4 +232,4 @@ This package still does **not** claim full implementation of:
- complete rewrite / placeholder / review / status-downgrade adapter execution
- non-watchdog full runtime governance interception
It now provides the first package-mainline evaluator / decision-runner core, plus a minimal end-to-end contract proof, but the remaining enforcement surface is still intentionally honest about adapter gaps.
It now provides the first package-mainline evaluator / decision-runner core, a compatibility-envelope boundary, and a minimal package profile artifact/binding slice, but the remaining enforcement surface is still intentionally honest about adapter gaps.

View File

@@ -0,0 +1,258 @@
{
"$schema": "../../../schemas/reporting-governance/capability-descriptor.schema.json",
"apiVersion": "reporting-governance/v1alpha1",
"kind": "AdapterCapabilities",
"metadata": {
"id": "openclaw-watchdog-reference",
"title": "OpenClaw Watchdog Reference Runtime Composition",
"version": "1.0.0",
"owner": "openclaw",
"summary": "Reference capability descriptor for the OpenClaw watchdog -> queue -> dispatcher -> bridge -> sender-binding composition.",
"tags": [
"openclaw",
"watchdog",
"reference-runtime",
"queue-bridge-sender"
]
},
"runtime": {
"name": "openclaw",
"mode": "hybrid",
"surfaces": [
"watchdog",
"queue",
"dispatcher",
"bridge",
"sender_binding",
"orchestrator",
"scheduler",
"storage"
],
"entrypoint": "scripts/watchdog_auto_notify_orchestrator.mjs"
},
"compatibility": {
"plugin_spec_versions": [
"0.1.0-mainline"
],
"event_schema": "schemas/reporting-governance/event-envelope.schema.json",
"evidence_schema": "schemas/reporting-governance/evidence.schema.json",
"decision_schema": "schemas/reporting-governance/decision.schema.json",
"capabilities_schema": "schemas/reporting-governance/capability-descriptor.schema.json"
},
"capabilities": {
"observation": {
"task_lifecycle": {
"supported": true,
"level": "partial",
"notes": [
"Current reference path is strongest for overdue/watchdog-related task state rather than all inline lifecycle hooks."
]
},
"subagent_lifecycle": {
"supported": true,
"level": "partial",
"notes": [
"Indirectly supported through watchdog/governor state and runtime artifacts, not yet a full dedicated package module."
]
},
"checkpoint_obligations": {
"supported": true,
"level": "partial",
"notes": [
"Current reference composition enforces overdue visibility but not yet all checkpoint-shape checks inline."
]
},
"outgoing_report_attempts": {
"supported": true,
"level": "partial",
"notes": [
"Visible in notification path artifacts, but not yet generalized as a full package-wide hook surface."
]
},
"watchdog_state": {
"supported": true,
"level": "full"
},
"queue_spool_receipts": {
"supported": true,
"level": "full"
}
},
"normalization": {
"canonical_events": {
"supported": true,
"level": "full"
},
"canonical_evidence": {
"supported": true,
"level": "partial",
"notes": [
"Watchdog evidence and delivery receipts are strong; broader non-watchdog evidence shaping is still package-next work."
]
},
"decision_inputs": {
"supported": true,
"level": "partial",
"notes": [
"Enough for watchdog-driven enforcement; not yet a generalized evaluator package input pipeline."
]
},
"correlation_propagation": {
"supported": true,
"level": "partial",
"notes": [
"Correlation is preserved across the watchdog queue/bridge chain where available."
]
}
},
"enforcement": {
"block_transition": {
"supported": true,
"level": "partial",
"notes": [
"Blocking exists in queue/dispatch/send paths, but not yet as a universal inline transition gate."
]
},
"rewrite_message": {
"supported": false,
"level": "none",
"notes": [
"Not part of the reference watchdog composition yet."
]
},
"annotate_placeholder": {
"supported": false,
"level": "none"
},
"force_checkpoint": {
"supported": true,
"level": "partial",
"notes": [
"The composition can force operator-visible recovery/notice, but not yet a generalized checkpoint runner."
]
},
"request_review": {
"supported": false,
"level": "none"
},
"downgrade_status": {
"supported": false,
"level": "none"
},
"escalate": {
"supported": true,
"level": "full"
}
},
"notification_path": {
"queue_items": {
"supported": true,
"level": "full"
},
"spool_handoff": {
"supported": true,
"level": "full"
},
"sender_binding": {
"supported": true,
"level": "full"
},
"direct_send": {
"supported": true,
"level": "partial",
"notes": [
"Supported only when the deployment provides a working sender runtime such as openclaw-cli."
]
},
"receipts": {
"supported": true,
"level": "full"
},
"final_delivery_proof": {
"supported": true,
"level": "partial",
"notes": [
"Ack proof exists only when sender runtime returns proven sent state."
]
},
"truth_model": {
"delivery_states": [
"prepared",
"queued",
"dispatched",
"pending_external_send",
"acked",
"blocked"
],
"ack_requires_proven_send": true,
"pending_external_send_supported": true
}
},
"watchdog": {
"scheduler_installation": {
"supported": true,
"level": "partial",
"notes": [
"Cron wrapper exists, but installation remains environment-bound rather than package-core."
]
},
"watchdog_evaluation": {
"supported": true,
"level": "full"
},
"watchdog_event_emission": {
"supported": true,
"level": "full"
},
"operator_recovery_path": {
"supported": true,
"level": "full"
},
"watchdog_closure": {
"supported": true,
"level": "full"
}
},
"storage": {
"persist_events": {
"supported": true,
"level": "full"
},
"persist_evidence": {
"supported": true,
"level": "full"
},
"persist_decisions": {
"supported": false,
"level": "none",
"notes": [
"Decision-store extraction is still the next stage."
]
},
"persist_receipts": {
"supported": true,
"level": "full"
},
"preserve_original_attempted_message": {
"supported": true,
"level": "partial",
"notes": [
"Notification payloads and sender-attempt artifacts are retained for the reference path."
]
}
}
},
"defaults": {
"reporting_mode": "watchdog-auto-notify",
"channel": "telegram",
"policy_ids": [
"no-silence",
"mandatory-checkpoint-structure"
],
"watchdog_policy_id": "no-silence"
},
"notes": [
"This descriptor intentionally describes the current OpenClaw watchdog reference composition, not the full future plugin capability surface.",
"Evaluator and decision-runner extraction should consume this descriptor as the runtime truth contract."
]
}

View File

@@ -0,0 +1,81 @@
{
"$schema": "../../../schemas/reporting-governance/capability-descriptor.schema.json",
"apiVersion": "reporting-governance/v1alpha1",
"kind": "AdapterCapabilities",
"metadata": {
"id": "example-openclaw-watchdog-reference",
"title": "Example OpenClaw Watchdog Reference Descriptor",
"version": "1.0.0"
},
"runtime": {
"name": "openclaw",
"mode": "hybrid",
"surfaces": [
"watchdog",
"queue",
"dispatcher",
"bridge",
"sender_binding",
"orchestrator",
"storage"
],
"entrypoint": "scripts/watchdog_auto_notify_orchestrator.mjs"
},
"compatibility": {
"plugin_spec_versions": [
"0.1.0-mainline"
]
},
"capabilities": {
"observation": {
"task_lifecycle": {"supported": true, "level": "partial"},
"subagent_lifecycle": {"supported": true, "level": "partial"},
"checkpoint_obligations": {"supported": true, "level": "partial"},
"outgoing_report_attempts": {"supported": true, "level": "partial"},
"watchdog_state": {"supported": true, "level": "full"},
"queue_spool_receipts": {"supported": true, "level": "full"}
},
"normalization": {
"canonical_events": {"supported": true, "level": "full"},
"canonical_evidence": {"supported": true, "level": "partial"},
"decision_inputs": {"supported": true, "level": "partial"},
"correlation_propagation": {"supported": true, "level": "partial"}
},
"enforcement": {
"block_transition": {"supported": true, "level": "partial"},
"rewrite_message": {"supported": false, "level": "none"},
"annotate_placeholder": {"supported": false, "level": "none"},
"force_checkpoint": {"supported": true, "level": "partial"},
"request_review": {"supported": false, "level": "none"},
"downgrade_status": {"supported": false, "level": "none"},
"escalate": {"supported": true, "level": "full"}
},
"notification_path": {
"queue_items": {"supported": true, "level": "full"},
"spool_handoff": {"supported": true, "level": "full"},
"sender_binding": {"supported": true, "level": "full"},
"direct_send": {"supported": true, "level": "partial"},
"receipts": {"supported": true, "level": "full"},
"final_delivery_proof": {"supported": true, "level": "partial"},
"truth_model": {
"delivery_states": ["prepared", "queued", "dispatched", "pending_external_send", "acked", "blocked"],
"ack_requires_proven_send": true,
"pending_external_send_supported": true
}
},
"watchdog": {
"scheduler_installation": {"supported": true, "level": "partial"},
"watchdog_evaluation": {"supported": true, "level": "full"},
"watchdog_event_emission": {"supported": true, "level": "full"},
"operator_recovery_path": {"supported": true, "level": "full"},
"watchdog_closure": {"supported": true, "level": "full"}
},
"storage": {
"persist_events": {"supported": true, "level": "full"},
"persist_evidence": {"supported": true, "level": "full"},
"persist_decisions": {"supported": false, "level": "none"},
"persist_receipts": {"supported": true, "level": "full"},
"preserve_original_attempted_message": {"supported": true, "level": "partial"}
}
}
}

View File

@@ -14,6 +14,6 @@
"./adapters/orchestrator": "./src/adapters/orchestrator.mjs"
},
"scripts": {
"test": "node --test test/package-structure.test.mjs test/policy-evaluator.test.mjs test/compatibility-preflight.test.mjs test/decision-runner.test.mjs test/governance-contract.integration.test.mjs test/watchdog-chain.integration.test.mjs test/exports-boundary.integration.test.mjs"
"test": "node --test test/package-structure.test.mjs test/policy-evaluator.test.mjs test/compatibility-preflight.test.mjs test/profile-artifact.test.mjs test/decision-runner.test.mjs test/governance-contract.integration.test.mjs test/watchdog-chain.integration.test.mjs test/exports-boundary.integration.test.mjs"
}
}

View File

@@ -0,0 +1,36 @@
{
"$schema": "../../../schemas/reporting-governance/deployment-profile.schema.json",
"apiVersion": "reporting-governance/v1alpha1",
"kind": "DeploymentProfileArtifact",
"metadata": {
"id": "strict-manager-mode",
"version": "1.0.0",
"runtime": "openclaw",
"source_profile": "profiles/strict-manager-mode.yaml",
"compatibility_mode": "strict_envelope"
},
"spec": {
"package": {
"pluginVersion": "0.1.0-mainline"
},
"bindings": {
"runtime": "openclaw",
"entrypoint": "scripts/watchdog_auto_notify_orchestrator.mjs",
"scripts": {
"watchdog": "scripts/long_task_watchdog.mjs",
"dispatcher": "scripts/operator_notify_dispatcher.mjs",
"bridgeSupervisor": "scripts/operator_notify_bridge_supervisor.mjs",
"senderBinding": "scripts/operator_notify_sender_binding.mjs",
"orchestrator": "scripts/watchdog_auto_notify_orchestrator.mjs"
},
"artifact_roots": {
"watchdogEvidence": "state/long-task-watchdog",
"canonicalEvents": "state/long-task-watchdog-events",
"queueItems": "state/operator-notify-queue",
"spoolArtifacts": "state/operator-notify-dispatch-spool",
"bridgeReceipts": "state/operator-notify-bridge-receipts",
"senderAttempts": "state/operator-notify-sender-attempts"
}
}
}
}

View File

@@ -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';

View File

@@ -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.

View File

@@ -0,0 +1 @@
export { loadDeploymentProfileArtifact, createDeploymentBindingContract } from './profile-artifact.mjs';

View File

@@ -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,
};

View File

@@ -43,7 +43,30 @@ test('runCompatibilityPreflight passes strict profile against reference descript
assert.equal(result.errors.length, 0);
});
test('runCompatibilityPreflight fails closed on schema/version mismatch', () => {
test('runCompatibilityPreflight keeps legacy compatibility mode open when caller provides no compatibility envelope', () => {
const brokenDescriptor = {
...capabilityDescriptor,
compatibility: {
...capabilityDescriptor.compatibility,
plugin_spec_versions: ['9.9.9'],
decision_schema: 'schemas/reporting-governance/not-the-canonical-decision.schema.json'
}
};
const result = runCompatibilityPreflight({
capabilityDescriptor: brokenDescriptor
});
assert.equal(result.status, 'pass');
assert.equal(result.requested_profile, null);
assert.equal(result.requested_plugin_version, null);
assert.equal(result.compatibility.version_ok, true);
assert.equal(result.errors.length, 0);
assert.ok(result.compatibility.schema_checks.some((entry) => entry.key === 'decision_schema' && entry.ok === false));
assert.ok(result.notes.some((note) => note.includes('skipped plugin version pin')));
});
test('runCompatibilityPreflight fails closed on schema/version mismatch once compatibility envelope is present', () => {
const brokenDescriptor = {
...capabilityDescriptor,
compatibility: {

View File

@@ -81,7 +81,7 @@ test('package root export resolves public package surface only', () => {
}
});
test('adapters subpath export resolves package-owned adapter index', () => {
test('adapters subpath export resolves package-owned adapter index plus profile artifact loader helpers', () => {
const root = createFixtureRoot();
try {
installPackageAlias(root);
@@ -93,7 +93,9 @@ test('adapters subpath export resolves package-owned adapter index', () => {
`);
assert.deepEqual(result.adapterKeys, [
'createDeploymentBindingContract',
'createRuntimeBinding',
'loadDeploymentProfileArtifact',
'runBridgeSupervisorAdapter',
'runDispatcherAdapter',
'runOrchestratorAdapter',

View File

@@ -131,6 +131,36 @@ test('executeGovernanceContract stays compatible for legacy callers without prof
assert.deepEqual(result.contract.package_actions, ['emit_event']);
});
test('legacy caller still fails once profile supplies compatibility envelope against mismatched descriptor', () => {
const brokenDescriptor = {
...capabilityDescriptor,
compatibility: {
...capabilityDescriptor.compatibility,
plugin_spec_versions: ['9.9.9'],
decision_schema: 'schemas/reporting-governance/not-the-canonical-decision.schema.json'
}
};
const result = executeGovernanceContract({
event: {
type: 'silence_timeout',
payload: { checkpoint_overdue: true }
},
capabilityDescriptor: brokenDescriptor,
policyPacks: [noSilencePack],
context: {
signals: ['checkpoint_overdue']
},
profile: strictProfile,
packageVersion: '0.1.0-mainline'
});
assert.equal(result.preflight.status, 'fail_closed');
assert.equal(result.contract.delivery_state, 'blocked');
assert.equal(result.contract.receipt_status, 'blocked');
assert.ok(result.planning.receipt.notes.some((note) => note.includes('schema mismatch: decision_schema')));
});
test('contract truthfully degrades when notify path can queue but cannot directly dispatch', () => {
const limitedDescriptor = {
...capabilityDescriptor,

View File

@@ -19,9 +19,11 @@ const requiredPaths = [
'src/adapters/sender-binding.mjs',
'src/adapters/orchestrator.mjs',
'src/storage',
'src/storage/profile-artifact.mjs',
'src/reference/openclaw-watchdog-chain.md',
'capabilities/openclaw-watchdog-reference.json',
'examples/openclaw-watchdog-reference.descriptor.example.json'
'examples/openclaw-watchdog-reference.descriptor.example.json',
'profiles/strict-manager-mode.profile.json'
];
test('reporting-governance package skeleton paths exist', () => {

View File

@@ -0,0 +1,47 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { loadDeploymentProfileArtifact, createDeploymentBindingContract } from '../src/storage/profile-artifact.mjs';
import { createRuntimeBinding } from '../src/adapters/index.mjs';
const packageRoot = path.resolve(import.meta.dirname, '..');
const repoRoot = path.resolve(packageRoot, '..', '..');
test('deployment profile artifact loads from package profiles and preserves compatibility envelope metadata', () => {
const { artifactPath, artifact } = loadDeploymentProfileArtifact({ profileId: 'strict-manager-mode' });
assert.equal(path.relative(packageRoot, artifactPath), path.join('profiles', 'strict-manager-mode.profile.json'));
assert.equal(artifact.kind, 'DeploymentProfileArtifact');
assert.equal(artifact.metadata.id, 'strict-manager-mode');
assert.equal(artifact.metadata.compatibility_mode, 'strict_envelope');
assert.equal(artifact.spec.package.pluginVersion, '0.1.0-mainline');
});
test('deployment binding contract resolves package artifact into real repo script and artifact paths', () => {
const { artifact } = loadDeploymentProfileArtifact({ profileId: 'strict-manager-mode' });
const binding = createDeploymentBindingContract({ artifact });
assert.equal(binding.runtime, 'openclaw');
assert.equal(binding.pluginVersion, '0.1.0-mainline');
assert.equal(binding.compatibilityMode, 'strict_envelope');
assert.equal(binding.entrypoint, path.resolve(repoRoot, 'scripts/watchdog_auto_notify_orchestrator.mjs'));
assert.equal(binding.scripts.watchdog, path.resolve(repoRoot, 'scripts/long_task_watchdog.mjs'));
assert.equal(binding.artifactRoots.queueItems, path.resolve(repoRoot, 'state/operator-notify-queue'));
assert.equal(fs.existsSync(binding.scripts.orchestrator), true);
});
test('runtime binding can be instantiated from profile artifact binding contract', () => {
const { artifact } = loadDeploymentProfileArtifact({ profileId: 'strict-manager-mode' });
const contract = createDeploymentBindingContract({ artifact });
const runtimeBinding = createRuntimeBinding({
cwd: repoRoot,
scripts: contract.scripts,
});
assert.equal(runtimeBinding.cwd, repoRoot);
assert.equal(runtimeBinding.scripts.dispatcher, contract.scripts.dispatcher);
assert.equal(runtimeBinding.scripts.bridgeSupervisor, contract.scripts.bridgeSupervisor);
assert.equal(runtimeBinding.scripts.senderBinding, contract.scripts.senderBinding);
});