create-principles-disciple 1.130.1 → 1.130.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/console/dist/server/models/ActivationsConsoleModel.js +16 -2
  2. package/console/dist/server/models/ApprovalsConsoleModel.js +25 -1
  3. package/core/dist/runtime-v2/__tests__/replay-production-parity.test.d.ts +13 -0
  4. package/core/dist/runtime-v2/__tests__/replay-production-parity.test.d.ts.map +1 -0
  5. package/core/dist/runtime-v2/__tests__/replay-production-parity.test.js +133 -0
  6. package/core/dist/runtime-v2/__tests__/replay-production-parity.test.js.map +1 -0
  7. package/core/dist/runtime-v2/activation/__tests__/activation-dispatcher.test.js +36 -0
  8. package/core/dist/runtime-v2/activation/__tests__/activation-dispatcher.test.js.map +1 -1
  9. package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts.map +1 -1
  10. package/core/dist/runtime-v2/activation/activation-dispatcher.js +4 -0
  11. package/core/dist/runtime-v2/activation/activation-dispatcher.js.map +1 -1
  12. package/core/dist/runtime-v2/activation/activation-types.d.ts +11 -1
  13. package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
  14. package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
  15. package/core/dist/runtime-v2/activation/production-gate-deps.d.ts +18 -1
  16. package/core/dist/runtime-v2/activation/production-gate-deps.d.ts.map +1 -1
  17. package/core/dist/runtime-v2/activation/production-gate-deps.js +5 -1
  18. package/core/dist/runtime-v2/activation/production-gate-deps.js.map +1 -1
  19. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts +18 -0
  20. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
  21. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +36 -1
  22. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
  23. package/core/dist/runtime-v2/golden-trace-replay-validator.d.ts +8 -0
  24. package/core/dist/runtime-v2/golden-trace-replay-validator.d.ts.map +1 -1
  25. package/core/dist/runtime-v2/golden-trace-replay-validator.js +9 -3
  26. package/core/dist/runtime-v2/golden-trace-replay-validator.js.map +1 -1
  27. package/core/dist/runtime-v2/golden-trace.d.ts +9 -0
  28. package/core/dist/runtime-v2/golden-trace.d.ts.map +1 -1
  29. package/core/dist/runtime-v2/golden-trace.js +7 -1
  30. package/core/dist/runtime-v2/golden-trace.js.map +1 -1
  31. package/core/dist/runtime-v2/index.d.ts +5 -1
  32. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  33. package/core/dist/runtime-v2/index.js +3 -1
  34. package/core/dist/runtime-v2/index.js.map +1 -1
  35. package/core/dist/runtime-v2/internalization/__tests__/rule-reliability-validation.test.d.ts +12 -0
  36. package/core/dist/runtime-v2/internalization/__tests__/rule-reliability-validation.test.d.ts.map +1 -0
  37. package/core/dist/runtime-v2/internalization/__tests__/rule-reliability-validation.test.js +256 -0
  38. package/core/dist/runtime-v2/internalization/__tests__/rule-reliability-validation.test.js.map +1 -0
  39. package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.d.ts +11 -0
  40. package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.d.ts.map +1 -0
  41. package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.js +133 -0
  42. package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.js.map +1 -0
  43. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +10 -0
  44. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  46. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  47. package/core/dist/runtime-v2/internalization/evaluator-runner.js +4 -0
  48. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  49. package/core/dist/runtime-v2/internalization/index.d.ts +5 -1
  50. package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
  51. package/core/dist/runtime-v2/internalization/index.js +3 -1
  52. package/core/dist/runtime-v2/internalization/index.js.map +1 -1
  53. package/core/dist/runtime-v2/internalization/refiner-rulehost-gate.d.ts +21 -0
  54. package/core/dist/runtime-v2/internalization/refiner-rulehost-gate.d.ts.map +1 -1
  55. package/core/dist/runtime-v2/internalization/refiner-rulehost-gate.js +30 -9
  56. package/core/dist/runtime-v2/internalization/refiner-rulehost-gate.js.map +1 -1
  57. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.d.ts +18 -0
  58. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.d.ts.map +1 -1
  59. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js +11 -3
  60. package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js.map +1 -1
  61. package/core/dist/runtime-v2/internalization/rule-context-v2.d.ts.map +1 -1
  62. package/core/dist/runtime-v2/internalization/rule-context-v2.js +6 -26
  63. package/core/dist/runtime-v2/internalization/rule-context-v2.js.map +1 -1
  64. package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts +10 -1
  65. package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts.map +1 -1
  66. package/core/dist/runtime-v2/internalization/rule-host-input-builder.d.ts +21 -0
  67. package/core/dist/runtime-v2/internalization/rule-host-input-builder.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/internalization/rule-host-input-builder.js +28 -1
  69. package/core/dist/runtime-v2/internalization/rule-host-input-builder.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/rule-reliability-validation.d.ts +67 -0
  71. package/core/dist/runtime-v2/internalization/rule-reliability-validation.d.ts.map +1 -0
  72. package/core/dist/runtime-v2/internalization/rule-reliability-validation.js +178 -0
  73. package/core/dist/runtime-v2/internalization/rule-reliability-validation.js.map +1 -0
  74. package/core/dist/runtime-v2/internalization/tool-semantic-baseline.d.ts +26 -0
  75. package/core/dist/runtime-v2/internalization/tool-semantic-baseline.d.ts.map +1 -0
  76. package/core/dist/runtime-v2/internalization/tool-semantic-baseline.js +57 -0
  77. package/core/dist/runtime-v2/internalization/tool-semantic-baseline.js.map +1 -0
  78. package/core/dist/runtime-v2/internalization/tool-semantic-registry.d.ts +76 -0
  79. package/core/dist/runtime-v2/internalization/tool-semantic-registry.d.ts.map +1 -0
  80. package/core/dist/runtime-v2/internalization/tool-semantic-registry.js +113 -0
  81. package/core/dist/runtime-v2/internalization/tool-semantic-registry.js.map +1 -0
  82. package/core/package.json +1 -1
  83. package/host-runtime/dist/host-tool-declaration.d.ts +57 -0
  84. package/host-runtime/dist/host-tool-declaration.js +161 -0
  85. package/host-runtime/dist/host-tool-semantic-resolver.d.ts +47 -0
  86. package/host-runtime/dist/host-tool-semantic-resolver.js +73 -0
  87. package/host-runtime/dist/index.d.ts +5 -1
  88. package/host-runtime/dist/index.js +5 -0
  89. package/host-runtime/dist/internalization-consumer-cycle.d.ts +9 -0
  90. package/host-runtime/dist/internalization-consumer-cycle.js +9 -3
  91. package/host-runtime/dist/internalization-consumer-governance.d.ts +9 -3
  92. package/host-runtime/dist/internalization-consumer-governance.js +13 -5
  93. package/host-runtime/dist/production-rulehost-gate.d.ts +10 -0
  94. package/host-runtime/dist/production-rulehost-gate.js +11 -4
  95. package/host-runtime/package.json +1 -1
  96. package/package.json +2 -2
  97. package/pd-cli/dist/commands/rulecode.d.ts +11 -0
  98. package/pd-cli/dist/commands/rulecode.d.ts.map +1 -1
  99. package/pd-cli/dist/commands/rulecode.js +4 -1
  100. package/pd-cli/dist/commands/rulecode.js.map +1 -1
  101. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  102. package/pd-cli/dist/commands/runtime-activation.js +83 -3
  103. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  104. package/pd-cli/dist/services/workspace-tool-semantics.d.ts +19 -0
  105. package/pd-cli/dist/services/workspace-tool-semantics.d.ts.map +1 -0
  106. package/pd-cli/dist/services/workspace-tool-semantics.js +20 -0
  107. package/pd-cli/dist/services/workspace-tool-semantics.js.map +1 -0
  108. package/pd-cli/package.json +1 -1
  109. package/plugin/dist/bundle.js +450 -450
  110. package/plugin/dist/constants/tool-semantics.d.ts +36 -0
  111. package/plugin/dist/governance-audit.js +143 -143
  112. package/plugin/dist/rulehost-evidence.js +162 -162
  113. package/plugin/openclaw.plugin.json +1 -1
  114. package/plugin/package.json +1 -1
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Host Tool Declaration Store — PRI-634-F R2 (review P1-2, multi-host)
3
+ *
4
+ * PURPOSE: durable, host-authored provenance of which raw tool names each
5
+ * workspace host dispatches to the RuleHost gate. Each host persists its OWN
6
+ * declaration file — `.pd/host-tool-semantics/<hostKind>.json` — so OpenClaw
7
+ * and Codex sharing one workspace never overwrite each other (R1's single
8
+ * file was last-writer-wins: verification results depended on startup
9
+ * order). Host-neutral consumers load ALL declarations and resolve a merged
10
+ * registry via the host-tool-semantic-resolver.
11
+ *
12
+ * The host code stays the source of truth; the per-host file is a
13
+ * workspace-local projection, refreshed on every host startup.
14
+ *
15
+ * Trust boundary: file content is UNTRUSTED input at read time — parsed as
16
+ * unknown and structurally validated (rc-1/rc-2/rc-4); malformed files fail
17
+ * loud with a typed per-host reason, never degrade silently (rc-3/rc-9).
18
+ */
19
+ import { readFileSync, writeFileSync, mkdirSync, mkdtempSync, renameSync, rmSync, readdirSync } from 'node:fs';
20
+ import path from 'node:path';
21
+ import { validateToolSemanticMappings, } from '@principles/core/runtime-v2';
22
+ export const HOST_TOOL_DECLARATION_DIRNAME = 'host-tool-semantics';
23
+ export const HOST_TOOL_DECLARATION_VERSION = 1;
24
+ const MAX_DECLARATION_BYTES = 256 * 1024;
25
+ /** hostKind doubles as a filename — code-controlled but validated anyway. */
26
+ const HOST_KIND_PATTERN = /^[a-z][a-z0-9_-]*$/;
27
+ function declarationsDir(workspaceDir) {
28
+ return path.join(workspaceDir, '.pd', HOST_TOOL_DECLARATION_DIRNAME);
29
+ }
30
+ function declarationPath(workspaceDir, hostKind) {
31
+ return path.join(declarationsDir(workspaceDir), `${hostKind}.json`);
32
+ }
33
+ function isRecord(value) {
34
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
35
+ }
36
+ function parseDeclaration(hostKind, raw) {
37
+ if (Buffer.byteLength(raw, 'utf8') > MAX_DECLARATION_BYTES) {
38
+ return { invalidReason: `declaration exceeds ${MAX_DECLARATION_BYTES} bytes` };
39
+ }
40
+ let parsed;
41
+ try {
42
+ parsed = JSON.parse(raw);
43
+ }
44
+ catch {
45
+ return { invalidReason: 'not valid JSON' };
46
+ }
47
+ if (!isRecord(parsed) || parsed.version !== HOST_TOOL_DECLARATION_VERSION) {
48
+ return { invalidReason: `version must be ${HOST_TOOL_DECLARATION_VERSION}` };
49
+ }
50
+ const { mappings, declaredAt } = parsed;
51
+ if (typeof declaredAt !== 'string' || declaredAt.trim() === '') {
52
+ return { invalidReason: 'declaredAt must be a non-empty string' };
53
+ }
54
+ const validation = validateToolSemanticMappings(mappings);
55
+ if (!validation.valid) {
56
+ return { invalidReason: `mappings invalid: ${validation.errors.slice(0, 3).join('; ')}` };
57
+ }
58
+ if (!Array.isArray(mappings) || mappings.length === 0) {
59
+ return { invalidReason: 'declaration must contain at least one mapping' };
60
+ }
61
+ return {
62
+ version: HOST_TOOL_DECLARATION_VERSION,
63
+ hostKind,
64
+ mappings,
65
+ declaredAt,
66
+ };
67
+ }
68
+ /**
69
+ * Persist ONE host's declaration (idempotent; hosts refresh on startup).
70
+ * Per-host file layout means concurrent/multi-host workspaces never
71
+ * overwrite each other (R2 P1-2). Atomic replace via a mkdtemp-unique
72
+ * staging dir (the sanctioned pattern for a possibly-shared location).
73
+ */
74
+ export function saveHostToolDeclaration(workspaceDir, declaration) {
75
+ if (!isRecord(declaration) || typeof declaration.hostKind !== 'string' || !HOST_KIND_PATTERN.test(declaration.hostKind)) {
76
+ return { ok: false, reason: `hostKind must match ${HOST_KIND_PATTERN.source}` };
77
+ }
78
+ const payload = JSON.stringify({
79
+ version: declaration.version,
80
+ hostKind: declaration.hostKind,
81
+ mappings: declaration.mappings,
82
+ declaredAt: declaration.declaredAt,
83
+ });
84
+ if (Buffer.byteLength(payload, 'utf8') > MAX_DECLARATION_BYTES) {
85
+ return { ok: false, reason: `host tool declaration exceeds ${MAX_DECLARATION_BYTES} bytes` };
86
+ }
87
+ const dir = declarationsDir(workspaceDir);
88
+ const finalPath = declarationPath(workspaceDir, declaration.hostKind);
89
+ try {
90
+ mkdirSync(dir, { recursive: true });
91
+ const stagingDir = mkdtempSync(path.join(dir, 'declaration-'));
92
+ try {
93
+ const tmpPath = path.join(stagingDir, 'declaration.json');
94
+ writeFileSync(tmpPath, payload, 'utf8');
95
+ renameSync(tmpPath, finalPath);
96
+ }
97
+ finally {
98
+ rmSync(stagingDir, { recursive: true, force: true });
99
+ }
100
+ return { ok: true };
101
+ }
102
+ catch (error) {
103
+ return { ok: false, reason: error instanceof Error ? error.message : String(error) };
104
+ }
105
+ }
106
+ /**
107
+ * Load ALL host declarations for the workspace. Typed result —
108
+ * ok:false reason=host_tool_declaration_missing : no declarations persisted
109
+ * ok:false reason=host_tool_declaration_invalid : at least one persisted
110
+ * file is malformed (named by hostKind — a broken host declaration is a
111
+ * defect the host must fix; consumers refuse rather than validate against
112
+ * a partial view).
113
+ * Consumers decide policy — this module never guesses.
114
+ */
115
+ export function loadHostToolDeclarations(workspaceDir) {
116
+ let entries;
117
+ try {
118
+ entries = readdirSync(declarationsDir(workspaceDir));
119
+ }
120
+ catch {
121
+ return {
122
+ ok: false,
123
+ reason: 'host_tool_declaration_missing',
124
+ nextAction: 'start each workspace host once (OpenClaw plugin / Codex worker) so it persists its tool declaration',
125
+ };
126
+ }
127
+ const declarations = [];
128
+ for (const entry of entries.sort()) {
129
+ if (!entry.endsWith('.json'))
130
+ continue;
131
+ const hostKind = entry.slice(0, -'.json'.length);
132
+ let raw;
133
+ try {
134
+ raw = readFileSync(path.join(declarationsDir(workspaceDir), entry), 'utf8');
135
+ }
136
+ catch {
137
+ return {
138
+ ok: false,
139
+ reason: `host_tool_declaration_invalid (${hostKind}: unreadable)`,
140
+ nextAction: `inspect ${declarationsDir(workspaceDir)}/${entry}`,
141
+ };
142
+ }
143
+ const parsed = parseDeclaration(hostKind, raw);
144
+ if ('invalidReason' in parsed) {
145
+ return {
146
+ ok: false,
147
+ reason: `host_tool_declaration_invalid (${hostKind}: ${parsed.invalidReason})`,
148
+ nextAction: `re-run host '${hostKind}' to refresh ${declarationsDir(workspaceDir)}/${entry}`,
149
+ };
150
+ }
151
+ declarations.push(parsed);
152
+ }
153
+ if (declarations.length === 0) {
154
+ return {
155
+ ok: false,
156
+ reason: 'host_tool_declaration_missing',
157
+ nextAction: 'start each workspace host once (OpenClaw plugin / Codex worker) so it persists its tool declaration',
158
+ };
159
+ }
160
+ return { ok: true, declarations };
161
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Host Tool Semantic Resolver — PRI-634-F R2 Phase 1 (SPEC §五 P1-1/P1-2)
3
+ *
4
+ * PURPOSE: the ONE deep module every entry point uses to answer "which raw
5
+ * tool names can really reach this workspace's RuleHost gate, and what kind
6
+ * is each?" — load (all persisted host declarations) → merge (every declared
7
+ * host contributes its gate-reachable names; no host is guessed, no host is
8
+ * dropped) → validate (structural, fail-loud) → resolve (a merged
9
+ * ToolSemanticRegistry). Consumers:
10
+ *
11
+ * - pd-cli activation / approval / promotion-readiness
12
+ * - pd-console ApprovalsConsoleModel / ActivationsConsoleModel (R2 P1-1:
13
+ * previously constructed RuleHostWriter directly and validated with the
14
+ * bare baseline — the same artifact could refuse on the CLI and approve
15
+ * on the Console)
16
+ * - auto-consumer / codex worker (host-threaded registries, already
17
+ * host-exact; the resolver exists for host-neutral entries)
18
+ *
19
+ * Multi-host semantics (R2 P1-2): declarations are PER-HOST files under
20
+ * `.pd/host-tool-semantics/<hostKind>.json` (last-writer-wins on a shared
21
+ * workspace was the R1 defect — startup order must not change verification
22
+ * results). The merged registry is the UNION of all declared hosts'
23
+ * gate-reachable names, so a rule generated under any co-installed host
24
+ * validates against its own host's surface.
25
+ *
26
+ * Pure logic + declared-file I/O only. Merging is order-independent.
27
+ */
28
+ import { type ToolSemanticRegistry } from '@principles/core/runtime-v2';
29
+ import { saveHostToolDeclaration, type HostToolDeclaration } from './host-tool-declaration.js';
30
+ export type HostToolSemanticResolution = {
31
+ readonly ok: true;
32
+ readonly registry: ToolSemanticRegistry;
33
+ readonly hostKinds: readonly string[];
34
+ } | {
35
+ readonly ok: false;
36
+ readonly reason: string;
37
+ readonly nextAction: string;
38
+ };
39
+ /**
40
+ * Resolve the merged workspace registry from durable host provenance.
41
+ * Fails loud when no declaration is persisted or any persisted declaration
42
+ * is malformed — never falls back to the core baseline (an existence check
43
+ * against baseline names is a forged proof; ERR-114).
44
+ */
45
+ export declare function resolveWorkspaceHostToolSemantics(workspaceDir: string): HostToolSemanticResolution;
46
+ export { saveHostToolDeclaration };
47
+ export type { HostToolDeclaration };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Host Tool Semantic Resolver — PRI-634-F R2 Phase 1 (SPEC §五 P1-1/P1-2)
3
+ *
4
+ * PURPOSE: the ONE deep module every entry point uses to answer "which raw
5
+ * tool names can really reach this workspace's RuleHost gate, and what kind
6
+ * is each?" — load (all persisted host declarations) → merge (every declared
7
+ * host contributes its gate-reachable names; no host is guessed, no host is
8
+ * dropped) → validate (structural, fail-loud) → resolve (a merged
9
+ * ToolSemanticRegistry). Consumers:
10
+ *
11
+ * - pd-cli activation / approval / promotion-readiness
12
+ * - pd-console ApprovalsConsoleModel / ActivationsConsoleModel (R2 P1-1:
13
+ * previously constructed RuleHostWriter directly and validated with the
14
+ * bare baseline — the same artifact could refuse on the CLI and approve
15
+ * on the Console)
16
+ * - auto-consumer / codex worker (host-threaded registries, already
17
+ * host-exact; the resolver exists for host-neutral entries)
18
+ *
19
+ * Multi-host semantics (R2 P1-2): declarations are PER-HOST files under
20
+ * `.pd/host-tool-semantics/<hostKind>.json` (last-writer-wins on a shared
21
+ * workspace was the R1 defect — startup order must not change verification
22
+ * results). The merged registry is the UNION of all declared hosts'
23
+ * gate-reachable names, so a rule generated under any co-installed host
24
+ * validates against its own host's surface.
25
+ *
26
+ * Pure logic + declared-file I/O only. Merging is order-independent.
27
+ */
28
+ import { buildToolSemanticRegistry, } from '@principles/core/runtime-v2';
29
+ import { loadHostToolDeclarations, saveHostToolDeclaration, } from './host-tool-declaration.js';
30
+ /**
31
+ * Resolve the merged workspace registry from durable host provenance.
32
+ * Fails loud when no declaration is persisted or any persisted declaration
33
+ * is malformed — never falls back to the core baseline (an existence check
34
+ * against baseline names is a forged proof; ERR-114).
35
+ */
36
+ export function resolveWorkspaceHostToolSemantics(workspaceDir) {
37
+ const loaded = loadHostToolDeclarations(workspaceDir);
38
+ if (!loaded.ok) {
39
+ return { ok: false, reason: loaded.reason, nextAction: loaded.nextAction };
40
+ }
41
+ const mappings = [];
42
+ for (const declaration of loaded.declarations) {
43
+ for (const mapping of declaration.mappings) {
44
+ // Same raw name from two hosts must agree on kind; a conflict means a
45
+ // host declared a name it does not own — fail loud rather than pick a
46
+ // winner by startup order (P4 one-source-of-truth).
47
+ const existing = mappings.find((m) => m.rawToolName === mapping.rawToolName);
48
+ if (existing && existing.canonicalKind !== mapping.canonicalKind) {
49
+ return {
50
+ ok: false,
51
+ reason: `host_tool_declaration_invalid (conflicting canonicalKind for '${mapping.rawToolName}': ${existing.canonicalKind} vs ${mapping.canonicalKind})`,
52
+ nextAction: 'each raw tool name must resolve to one canonical kind across all co-installed hosts — fix the conflicting host declaration',
53
+ };
54
+ }
55
+ if (!existing)
56
+ mappings.push(mapping);
57
+ }
58
+ }
59
+ const built = buildToolSemanticRegistry(mappings);
60
+ if (!built.ok) {
61
+ return {
62
+ ok: false,
63
+ reason: 'host_tool_declaration_invalid',
64
+ nextAction: `merged host tool mappings invalid: ${built.errors.slice(0, 3).join('; ')}`,
65
+ };
66
+ }
67
+ return {
68
+ ok: true,
69
+ registry: built.registry,
70
+ hostKinds: loaded.declarations.map((d) => d.hostKind).sort(),
71
+ };
72
+ }
73
+ export { saveHostToolDeclaration };
@@ -3,7 +3,7 @@ import { buildActivePrinciplePromptContext } from './active-principle-prompt.js'
3
3
  import { type RuleContextProvider, type RuleInputEnrichmentProvider } from './production-rulehost-gate.js';
4
4
  import type { RuleImplementationRuntime } from './rule-implementation-runtime.js';
5
5
  import { type PainDatabaseFactory, type PainEnrichmentProvider } from './production-pain-evidence.js';
6
- import type { GovernanceHostKind } from '@principles/core/runtime-v2';
6
+ import type { GovernanceHostKind, ToolSemanticRegistry } from '@principles/core/runtime-v2';
7
7
  export * from './active-principle-prompt.js';
8
8
  export * from './pd-config.js';
9
9
  export * from './production-rulehost-gate.js';
@@ -15,6 +15,8 @@ export * from './pain-evidence-ingress.js';
15
15
  export * from './host-liveness-contract.js';
16
16
  export * from './internalization-consumer-governance.js';
17
17
  export * from './internalization-consumer-cycle.js';
18
+ export * from './host-tool-declaration.js';
19
+ export * from './host-tool-semantic-resolver.js';
18
20
  export * from './workspace-telemetry-emitter.js';
19
21
  export * from './product-telemetry/consent-store.js';
20
22
  export * from './product-telemetry/eligibility.js';
@@ -57,4 +59,6 @@ export declare function createProductionHostRuntime(options?: Partial<Pick<HostR
57
59
  painDatabaseFactory?: PainDatabaseFactory;
58
60
  /** PRI-640: host attribution supplied by the constructing host adapter (OpenClaw / Codex). */
59
61
  hostKind?: GovernanceHostKind;
62
+ /** PRI-634-F: host-declared tool semantics supplied by the constructing host adapter. */
63
+ toolSemantics?: ToolSemanticRegistry;
60
64
  }): HostRuntime;
@@ -17,6 +17,10 @@ export * from './host-liveness-contract.js';
17
17
  // auto-consumer + Companion workspace worker call the same cycle).
18
18
  export * from './internalization-consumer-governance.js';
19
19
  export * from './internalization-consumer-cycle.js';
20
+ // PRI-634-F R2: durable host-authored tool declaration — hosts persist, host-
21
+ // neutral consumers (pd-cli/pd-console) resolve through the ONE resolver.
22
+ export * from './host-tool-declaration.js';
23
+ export * from './host-tool-semantic-resolver.js';
20
24
  // PRI-634 A3: workspace-scoped critical telemetry — canonical implementation
21
25
  // moved from openclaw-plugin so both hosts share ONE semantics.
22
26
  export * from './workspace-telemetry-emitter.js';
@@ -144,6 +148,7 @@ export function createProductionHostRuntime(options = {}) {
144
148
  ...(options.ruleContextProvider ? { ruleContextProvider: options.ruleContextProvider } : {}),
145
149
  ...(options.ruleInputEnrichmentProvider ? { ruleInputEnrichmentProvider: options.ruleInputEnrichmentProvider } : {}),
146
150
  ...(options.ruleImplementationRuntime ? { implementationRuntime: options.ruleImplementationRuntime } : {}),
151
+ ...(options.toolSemantics ? { toolSemantics: options.toolSemantics } : {}),
147
152
  });
148
153
  return createHostRuntime({
149
154
  afterToolCall: options.afterToolCall ?? createProductionPainEvidenceHandler({
@@ -1,3 +1,4 @@
1
+ import { type ToolSemanticRegistry } from '@principles/core/runtime-v2';
1
2
  import { type ConsumerGovernanceLogger } from './internalization-consumer-governance.js';
2
3
  export declare const INTERNALIZATION_AUTO_CONSUMER_FLAG_ID = "internalization_auto_consumer";
3
4
  /** Structural logger port — OpenClaw PluginLogger and worker loggers satisfy this. */
@@ -23,6 +24,14 @@ export interface InternalizationConsumerCyclePorts {
23
24
  * catalog would be worse than none.
24
25
  */
25
26
  readonly hostToolCatalog?: ConsumerHostToolCatalog;
27
+ /**
28
+ * PRI-634-F: host-declared ToolSemanticRegistry (raw tool name → canonical
29
+ * kind). Threaded into the activation gate (RuleHostWriter) and its
30
+ * gateDeps so sandbox replay resolves tool semantics IDENTICALLY to the
31
+ * host's production gate (replay/production input parity). Optional:
32
+ * hosts that have not declared semantics keep core-baseline behavior.
33
+ */
34
+ readonly toolSemantics?: ToolSemanticRegistry;
26
35
  /** Log label used in human log prefixes ('AutoConsumer' | 'CodexWorker' | …). */
27
36
  readonly logLabel: string;
28
37
  readonly envGetter?: (name: string) => string | undefined;
@@ -140,7 +140,7 @@ async function runReconciliationBudget(workspaceDir, orchestrator, ports) {
140
140
  */
141
141
  export async function runInternalizationConsumerCycle(workspaceDir, ports) {
142
142
  const { logger, emitEvent, logLabel, owner } = ports;
143
- const { hostToolCatalog } = ports;
143
+ const { hostToolCatalog, toolSemantics } = ports;
144
144
  const envGetter = ports.envGetter ?? ((name) => process.env[name]);
145
145
  let orchestrator = null;
146
146
  const flag = loadFeatureFlagFromConfig(workspaceDir, INTERNALIZATION_AUTO_CONSUMER_FLAG_ID, {
@@ -368,7 +368,13 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
368
368
  ...createEvaluatorRepairDeps(workspaceDir, stateManager, logger),
369
369
  }, {
370
370
  ...runnerOptions,
371
- gateDeps: createProductionGateDeps(),
371
+ // PRI-634-F: host registry + workspace root (when declared) make
372
+ // the evaluator's adversarial replay resolve tool semantics and
373
+ // normalizedPath identically to the host production gate.
374
+ gateDeps: createProductionGateDeps({
375
+ ...(toolSemantics ? { toolSemantics } : {}),
376
+ projectDir: workspaceDir,
377
+ }),
372
378
  ...(hostToolCatalog
373
379
  ? { hostToolCatalog: { readOnlyTools: [...hostToolCatalog.readOnlyTools], writeTools: [...hostToolCatalog.writeTools] } }
374
380
  : {}),
@@ -386,7 +392,7 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
386
392
  runner = new RolloutReviewerRunner({
387
393
  stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter,
388
394
  artifactStore: stateManager.piArtifactStore, validator: new DefaultRolloutReviewerValidator(),
389
- ...createRolloutGovernanceDeps(workspaceDir, orchestrator, logger),
395
+ ...createRolloutGovernanceDeps(workspaceDir, orchestrator, { logger, ...(toolSemantics ? { toolSemantics } : {}) }),
390
396
  }, runnerOptions);
391
397
  break;
392
398
  default:
@@ -14,14 +14,17 @@
14
14
  * dedupes by `${artifactId}::${channel}`; reopen idempotency is owned by
15
15
  * orchestrator.reopenTaskForRevision.
16
16
  */
17
- import { type RolloutAutoDispatchInput, type RolloutAutoDispatchOutcome, type RolloutRevisionRoutingInput, type SeedArtificerRepairParams, type RuntimeStateManager, type InternalizationOrchestrator } from '@principles/core/runtime-v2';
17
+ import { type RolloutAutoDispatchInput, type RolloutAutoDispatchOutcome, type RolloutRevisionRoutingInput, type SeedArtificerRepairParams, type RuntimeStateManager, type InternalizationOrchestrator, type ToolSemanticRegistry } from '@principles/core/runtime-v2';
18
18
  /** Structural logger port — PluginLogger satisfies this without adaptation. */
19
19
  export interface ConsumerGovernanceLogger {
20
20
  info?: (msg: string) => void;
21
21
  warn?: (msg: string) => void;
22
22
  }
23
23
  /** rollout → ActivationDispatcher 的生产接线 (per dispatch 打开短连接, 与 Console 模式一致) */
24
- export declare function dispatchRolloutActivation(workspaceDir: string, input: RolloutAutoDispatchInput, logger?: ConsumerGovernanceLogger): Promise<RolloutAutoDispatchOutcome>;
24
+ export declare function dispatchRolloutActivation(workspaceDir: string, input: RolloutAutoDispatchInput, options?: {
25
+ logger?: ConsumerGovernanceLogger;
26
+ toolSemantics?: ToolSemanticRegistry;
27
+ }): Promise<RolloutAutoDispatchOutcome>;
25
28
  /** flag 探针: config 异常 → 全 false (fail-closed, 不 throw) */
26
29
  /** evaluator repair deps (PRI-509 机制的生产接线; flag 语义保留为运行时开关) */
27
30
  export declare function createEvaluatorRepairDeps(workspaceDir: string, stateManager: RuntimeStateManager, logger?: ConsumerGovernanceLogger): {
@@ -29,7 +32,10 @@ export declare function createEvaluatorRepairDeps(workspaceDir: string, stateMan
29
32
  seedArtificerRepairTask: (params: SeedArtificerRepairParams) => Promise<string>;
30
33
  };
31
34
  /** rollout reviewer 治理 deps: dispatch + revision reopen */
32
- export declare function createRolloutGovernanceDeps(workspaceDir: string, orchestrator: InternalizationOrchestrator, logger?: ConsumerGovernanceLogger): {
35
+ export declare function createRolloutGovernanceDeps(workspaceDir: string, orchestrator: InternalizationOrchestrator, options?: {
36
+ logger?: ConsumerGovernanceLogger;
37
+ toolSemantics?: ToolSemanticRegistry;
38
+ }): {
33
39
  dispatchActivation: (input: RolloutAutoDispatchInput) => Promise<RolloutAutoDispatchOutcome>;
34
40
  reopenRevisionTarget: (input: RolloutRevisionRoutingInput) => Promise<{
35
41
  ok: boolean;
@@ -38,7 +38,7 @@ function makeFlagProbe(workspaceDir) {
38
38
  return (flagId) => isFeatureEnabled(flags, flagId);
39
39
  }
40
40
  /** rollout → ActivationDispatcher 的生产接线 (per dispatch 打开短连接, 与 Console 模式一致) */
41
- export async function dispatchRolloutActivation(workspaceDir, input, logger) {
41
+ export async function dispatchRolloutActivation(workspaceDir, input, options = {}) {
42
42
  const connection = new SqliteConnection(workspaceDir);
43
43
  try {
44
44
  const piArtifactStore = new SqlitePIArtifactStore(connection);
@@ -67,7 +67,15 @@ export async function dispatchRolloutActivation(workspaceDir, input, logger) {
67
67
  const dispatcher = new ActivationDispatcher(artifactReadModel, activationStateStore, {
68
68
  writers: [
69
69
  new PromptWriter(),
70
- new RuleHostWriter({ gateDeps: createProductionGateDeps(), featureFlagProbe: flagProbe }),
70
+ // PRI-634-F: toolSemantics (host-declared) + the workspace root flow
71
+ // into the activation writer — reliability validation + replay with
72
+ // production-identical path normalization.
73
+ new RuleHostWriter({
74
+ gateDeps: createProductionGateDeps(options.toolSemantics ? { toolSemantics: options.toolSemantics } : {}),
75
+ featureFlagProbe: flagProbe,
76
+ ...(options.toolSemantics ? { toolSemantics: options.toolSemantics } : {}),
77
+ projectDir: workspaceDir,
78
+ }),
71
79
  new DeferArchiveWriter(),
72
80
  ],
73
81
  approvalQueueStore,
@@ -82,7 +90,7 @@ export async function dispatchRolloutActivation(workspaceDir, input, logger) {
82
90
  confidence: input.confidence,
83
91
  });
84
92
  const outcome = normalizeDecision(decision);
85
- logger?.info?.(`[PD:Consumer] rollout dispatch: artifact=${input.artifactId} channel=${input.channel} → ${outcome.decision}${outcome.activationId ? ` (${outcome.activationId})` : ''}${outcome.reason ? ` reason=${outcome.reason}` : ''}`);
93
+ options.logger?.info?.(`[PD:Consumer] rollout dispatch: artifact=${input.artifactId} channel=${input.channel} → ${outcome.decision}${outcome.activationId ? ` (${outcome.activationId})` : ''}${outcome.reason ? ` reason=${outcome.reason}` : ''}`);
86
94
  return outcome;
87
95
  }
88
96
  finally {
@@ -135,9 +143,9 @@ export function createEvaluatorRepairDeps(workspaceDir, stateManager, logger) {
135
143
  };
136
144
  }
137
145
  /** rollout reviewer 治理 deps: dispatch + revision reopen */
138
- export function createRolloutGovernanceDeps(workspaceDir, orchestrator, logger) {
146
+ export function createRolloutGovernanceDeps(workspaceDir, orchestrator, options = {}) {
139
147
  return {
140
- dispatchActivation: (input) => dispatchRolloutActivation(workspaceDir, input, logger),
148
+ dispatchActivation: (input) => dispatchRolloutActivation(workspaceDir, input, options),
141
149
  reopenRevisionTarget: async (input) => {
142
150
  const result = await orchestrator.reopenTaskForRevision(input.targetTaskId, {
143
151
  revisionFeedback: input.revisionFeedback,
@@ -1,3 +1,4 @@
1
+ import { type ToolSemanticRegistry } from '@principles/core/runtime-v2';
1
2
  import type { HostEvent, HostEventResult } from '@principles/core/host';
2
3
  import { type RuleImplementationRuntime } from './rule-implementation-runtime.js';
3
4
  export interface ProductionRuleContextRequest {
@@ -18,5 +19,14 @@ export interface ProductionRuleHostGateOptions {
18
19
  ruleContextProvider?: RuleContextProvider;
19
20
  ruleInputEnrichmentProvider?: RuleInputEnrichmentProvider;
20
21
  implementationRuntime?: RuleImplementationRuntime;
22
+ /**
23
+ * PRI-634-F: host-declared ToolSemanticRegistry. Extraction hints and
24
+ * action.canonicalKind derive from this registry (defaults to the
25
+ * core-baseline registry when the constructing host has not declared its
26
+ * tool semantics). Replaces the previous inline bash/write name sets,
27
+ * which silently disagreed with the OpenClaw host vocabulary
28
+ * (shell/cmd/insert/patch/delete_file/move_file were never hinted).
29
+ */
30
+ toolSemantics?: ToolSemanticRegistry;
21
31
  }
22
32
  export declare function createProductionRuleHostGate(options?: ProductionRuleHostGateOptions): (event: HostEvent) => Promise<HostEventResult>;
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { buildRuleHostAction, estimateLineChanges, mergeDecisions, SqliteConnection, UNAVAILABLE_RULE_CONTEXT, validateRuleContextV2, validateRuleHostResult, } from '@principles/core/runtime-v2';
3
+ import { buildRuleHostAction, buildToolSemanticRegistry, deriveToolHintsFromCanonicalKind, estimateLineChanges, mergeDecisions, SqliteConnection, UNAVAILABLE_RULE_CONTEXT, validateRuleContextV2, validateRuleHostResult, } from '@principles/core/runtime-v2';
4
4
  import { scanRetiredContractSymbols } from './legacy-rule-contract-symbols.js';
5
5
  import { createNodeRuleImplementationRuntime, MAX_ACTIVE_RULES, RULE_BATCH_SOURCE_BYTES, RULE_SOURCE_BYTES, } from './rule-implementation-runtime.js';
6
6
  const WARNING_LIMIT = 500;
@@ -62,8 +62,15 @@ function isRuleContext(value) {
62
62
  function isRuleResult(value) {
63
63
  return validateRuleHostResult(value).valid;
64
64
  }
65
+ const baselineToolSemanticsResult = buildToolSemanticRegistry();
66
+ // Static baseline input — always builds; throwing here is a programming error.
67
+ if (!baselineToolSemanticsResult.ok) {
68
+ throw new Error(`baseline tool semantic registry failed to build: ${baselineToolSemanticsResult.errors.join('; ')}`);
69
+ }
70
+ const BASELINE_TOOL_SEMANTICS = baselineToolSemanticsResult.registry;
65
71
  export function createProductionRuleHostGate(options = {}) {
66
72
  const implementationRuntime = options.implementationRuntime ?? createNodeRuleImplementationRuntime();
73
+ const toolSemantics = options.toolSemantics ?? BASELINE_TOOL_SEMANTICS;
67
74
  return async (event) => {
68
75
  const startedAt = Date.now();
69
76
  const warnings = [];
@@ -71,9 +78,9 @@ export function createProductionRuleHostGate(options = {}) {
71
78
  if (!input || input.toolName !== event.context.toolName) {
72
79
  return { decision: 'allow', source: event.source, warnings: [boundedWarning('tool_input_invalid', 'decode and validate toolName and params before dispatch')] };
73
80
  }
74
- const isBash = new Set(['bash', 'exec', 'execute', 'run_shell_command']).has(input.toolName);
75
- const isWrite = new Set(['write', 'write_file', 'edit', 'edit_file', 'replace', 'apply_patch']).has(input.toolName);
76
- const action = buildRuleHostAction(input.toolName, input.params, event.context.workspaceDir, { isBashTool: isBash, isWriteTool: isWrite });
81
+ const canonicalKind = toolSemantics.resolve(input.toolName);
82
+ const { isBashTool: isBash, isWriteTool: isWrite } = deriveToolHintsFromCanonicalKind(canonicalKind);
83
+ const action = buildRuleHostAction(input.toolName, input.params, event.context.workspaceDir, { isBashTool: isBash, isWriteTool: isWrite, canonicalKind });
77
84
  if (action.normalizedPath === null)
78
85
  return { decision: 'allow', source: event.source, metadata: { evaluatedLiveRules: 0 } };
79
86
  let context;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/host-runtime",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-principles-disciple",
3
- "version": "1.130.1",
3
+ "version": "1.130.3",
4
4
  "description": "Interactive CLI installer for Principles Disciple OpenClaw plugin",
5
5
  "type": "module",
6
6
  "bin": {
@@ -72,6 +72,6 @@
72
72
  "directory": "packages/create-principles-disciple"
73
73
  },
74
74
  "pd": {
75
- "bundledPluginVersion": "1.228.2"
75
+ "bundledPluginVersion": "1.229.0"
76
76
  }
77
77
  }
@@ -53,6 +53,17 @@ export interface RulecodeReplayOutput {
53
53
  forbiddenPatternViolations: string[];
54
54
  reason?: string;
55
55
  nextAction?: string;
56
+ /**
57
+ * PRI-634-F R2 (review P2): structured failure attribution
58
+ * ({layer, reasonCode, evidence, nextAction}) — machine-readable on
59
+ * `--json`, printed on the text path. Absent when replay passed.
60
+ */
61
+ failure?: {
62
+ layer: string;
63
+ reasonCode: string;
64
+ evidence: string;
65
+ nextAction: string;
66
+ };
56
67
  }
57
68
  /**
58
69
  * Load and validate golden trace cases from a JSON file.
@@ -1 +1 @@
1
- {"version":3,"file":"rulecode.d.ts","sourceRoot":"","sources":["../../src/commands/rulecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAKxE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtE,0BAA0B,EAAE,MAAM,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA2CD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAuDpF;AAID,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CASzE;AAED,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDjF;AAED,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2H7E;AAID;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAoDnE"}
1
+ {"version":3,"file":"rulecode.d.ts","sourceRoot":"","sources":["../../src/commands/rulecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAKxE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtE,0BAA0B,EAAE,MAAM,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AA2CD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAuDpF;AAID,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CASzE;AAED,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDjF;AAED,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6H7E;AAID;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAoDnE"}
@@ -293,10 +293,11 @@ export async function handleRulecodeReplay(opts) {
293
293
  message: c.message,
294
294
  })),
295
295
  forbiddenPatternViolations: gateResult.sandboxResult.forbiddenPatternViolations,
296
+ ...(gateResult.failure ? { failure: gateResult.failure } : {}),
296
297
  };
297
298
  if (output.status === 'failed') {
298
299
  output.reason = `sandbox replay rejected: ${gateResult.decision}`;
299
- output.nextAction = 'fix the code or golden trace cases based on the failed cases above';
300
+ output.nextAction = gateResult.failure?.nextAction ?? 'fix the code or golden trace cases based on the failed cases above';
300
301
  }
301
302
  emitResult(output, {
302
303
  json,
@@ -304,6 +305,8 @@ export async function handleRulecodeReplay(opts) {
304
305
  if (o.status === 'ok')
305
306
  return 'PASSED: all golden trace cases replayed successfully.';
306
307
  const lines = [`FAILED: ${o.decision}`];
308
+ if (o.failure)
309
+ lines.push(` - failureLayer: ${o.failure.layer} | reasonCode: ${o.failure.reasonCode}`);
307
310
  for (const r of o.reasons)
308
311
  lines.push(` - ${r}`);
309
312
  for (const c of o.failedCases)