@principles/pd-cli 1.133.0 → 1.135.0
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.
- package/dist/commands/__tests__/intent-flag-wiring.test.js +74 -3
- package/dist/commands/__tests__/intent-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +2 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +1 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +60 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +1 -0
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js +11 -4
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +9 -0
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +1 -1
- package/dist/commands/admission-gate.d.ts +24 -0
- package/dist/commands/admission-gate.d.ts.map +1 -0
- package/dist/commands/admission-gate.js +29 -0
- package/dist/commands/admission-gate.js.map +1 -0
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +72 -6
- package/dist/commands/candidate.js.map +1 -1
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js +42 -0
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/demo-story-a.d.ts.map +1 -1
- package/dist/commands/demo-story-a.js +4 -2
- package/dist/commands/demo-story-a.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +155 -8
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/errors-list.d.ts +25 -0
- package/dist/commands/errors-list.d.ts.map +1 -0
- package/dist/commands/errors-list.js +368 -0
- package/dist/commands/errors-list.js.map +1 -0
- package/dist/commands/health-codex.d.ts +7 -0
- package/dist/commands/health-codex.d.ts.map +1 -0
- package/dist/commands/health-codex.js +209 -0
- package/dist/commands/health-codex.js.map +1 -0
- package/dist/commands/health.d.ts +24 -0
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +48 -0
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/intent.d.ts +21 -4
- package/dist/commands/intent.d.ts.map +1 -1
- package/dist/commands/intent.js +88 -27
- package/dist/commands/intent.js.map +1 -1
- package/dist/commands/pain-evidence.d.ts +6 -0
- package/dist/commands/pain-evidence.d.ts.map +1 -1
- package/dist/commands/pain-evidence.js +6 -0
- package/dist/commands/pain-evidence.js.map +1 -1
- package/dist/commands/pain-record.d.ts.map +1 -1
- package/dist/commands/pain-record.js +21 -0
- package/dist/commands/pain-record.js.map +1 -1
- package/dist/commands/pain-retry.d.ts +4 -0
- package/dist/commands/pain-retry.d.ts.map +1 -1
- package/dist/commands/pain-retry.js +327 -32
- package/dist/commands/pain-retry.js.map +1 -1
- package/dist/commands/proven-channel-baseline.d.ts.map +1 -1
- package/dist/commands/proven-channel-baseline.js +4 -2
- package/dist/commands/proven-channel-baseline.js.map +1 -1
- package/dist/commands/runtime-activation.d.ts +30 -0
- package/dist/commands/runtime-activation.d.ts.map +1 -1
- package/dist/commands/runtime-activation.js +567 -11
- package/dist/commands/runtime-activation.js.map +1 -1
- package/dist/commands/runtime-canary.d.ts.map +1 -1
- package/dist/commands/runtime-canary.js +32 -7
- package/dist/commands/runtime-canary.js.map +1 -1
- package/dist/commands/runtime-health-snapshot.d.ts.map +1 -1
- package/dist/commands/runtime-health-snapshot.js +4 -2
- package/dist/commands/runtime-health-snapshot.js.map +1 -1
- package/dist/commands/runtime-init.d.ts +43 -0
- package/dist/commands/runtime-init.d.ts.map +1 -0
- package/dist/commands/runtime-init.js +264 -0
- package/dist/commands/runtime-init.js.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts +25 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.js +374 -0
- package/dist/commands/runtime-internalization-context-trace.js.map +1 -0
- package/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js +3 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
- package/dist/commands/runtime-internalization-integrity.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-integrity.js +4 -2
- package/dist/commands/runtime-internalization-integrity.js.map +1 -1
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +20 -5
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.d.ts +1 -0
- package/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.js +149 -6
- package/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/dist/commands/runtime-uat.d.ts.map +1 -1
- package/dist/commands/runtime-uat.guard.test.js +13 -15
- package/dist/commands/runtime-uat.guard.test.js.map +1 -1
- package/dist/commands/runtime-uat.js +4 -3
- package/dist/commands/runtime-uat.js.map +1 -1
- package/dist/config-reader.js +1 -1
- package/dist/config-reader.js.map +1 -1
- package/dist/index.js +54 -78
- package/dist/index.js.map +1 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +2 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +1 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js +304 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js.map +1 -0
- package/dist/services/__tests__/rulehost-readiness.test.js.map +1 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -1
- package/dist/services/config-doctor.d.ts.map +1 -1
- package/dist/services/config-doctor.js +9 -2
- package/dist/services/config-doctor.js.map +1 -1
- package/dist/services/console-launcher.d.ts +6 -0
- package/dist/services/console-launcher.d.ts.map +1 -1
- package/dist/services/console-launcher.js.map +1 -1
- package/dist/services/pain-flood-simulation-runner.d.ts.map +1 -1
- package/dist/services/pain-flood-simulation-runner.js +9 -0
- package/dist/services/pain-flood-simulation-runner.js.map +1 -1
- package/dist/services/pd-config-loader.js +2 -2
- package/dist/services/pd-config-loader.js.map +1 -1
- package/dist/services/proven-channel-baseline-runner.d.ts.map +1 -1
- package/dist/services/proven-channel-baseline-runner.js +10 -2
- package/dist/services/proven-channel-baseline-runner.js.map +1 -1
- package/dist/services/quality-scorecard/data-extractor.js.map +1 -1
- package/dist/services/resolve-runtime-from-pd-config.js.map +1 -1
- package/dist/services/rulehost-pipeline-runner.d.ts +77 -1
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +111 -7
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/dist/services/rulehost-readiness.js.map +1 -1
- package/dist/services/runtime-adapter-resolver.d.ts +1 -0
- package/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
- package/dist/services/runtime-adapter-resolver.js +3 -0
- package/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/dist/services/synthetic-baseline-runner.d.ts.map +1 -1
- package/dist/services/synthetic-baseline-runner.js +11 -0
- package/dist/services/synthetic-baseline-runner.js.map +1 -1
- package/dist/utils/production-workspace-guard.d.ts +4 -4
- package/dist/utils/production-workspace-guard.d.ts.map +1 -1
- package/dist/utils/production-workspace-guard.js +19 -16
- package/dist/utils/production-workspace-guard.js.map +1 -1
- package/dist/utils/production-workspace-guard.test.js +54 -19
- package/dist/utils/production-workspace-guard.test.js.map +1 -1
- package/package.json +8 -5
- package/src/commands/__tests__/intent-flag-wiring.test.ts +88 -3
- package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +73 -0
- package/src/commands/__tests__/rulecode-flag-wiring.test.ts +31 -24
- package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +12 -0
- package/src/commands/admission-gate.ts +30 -0
- package/src/commands/candidate.ts +74 -6
- package/src/commands/console.ts +41 -0
- package/src/commands/demo-story-a.ts +4 -2
- package/src/commands/diagnose.ts +158 -10
- package/src/commands/errors-list.ts +440 -0
- package/src/commands/health-codex.ts +249 -0
- package/src/commands/health.ts +60 -0
- package/src/commands/intent.ts +98 -29
- package/src/commands/pain-evidence.ts +6 -0
- package/src/commands/pain-record.ts +21 -0
- package/src/commands/pain-retry.ts +353 -35
- package/src/commands/proven-channel-baseline.ts +4 -2
- package/src/commands/runtime-activation.ts +657 -12
- package/src/commands/runtime-canary.ts +31 -5
- package/src/commands/runtime-health-snapshot.ts +4 -2
- package/src/commands/runtime-init.ts +305 -0
- package/src/commands/runtime-internalization-context-trace.ts +445 -0
- package/src/commands/runtime-internalization-enqueue-successors.ts +3 -1
- package/src/commands/runtime-internalization-integrity.ts +4 -2
- package/src/commands/runtime-internalization-run-once.ts +20 -5
- package/src/commands/runtime-internalization-run-rulehost.ts +159 -6
- package/src/commands/runtime-uat.guard.test.ts +14 -16
- package/src/commands/runtime-uat.ts +4 -3
- package/src/config-reader.ts +1 -1
- package/src/index.ts +69 -78
- package/src/services/__tests__/evaluator-runner-deps.test.ts +341 -0
- package/src/services/config-doctor.ts +9 -2
- package/src/services/console-launcher.ts +6 -0
- package/src/services/pain-flood-simulation-runner.ts +9 -0
- package/src/services/pd-config-loader.ts +2 -2
- package/src/services/proven-channel-baseline-runner.ts +10 -3
- package/src/services/rulehost-pipeline-runner.ts +157 -6
- package/src/services/runtime-adapter-resolver.ts +4 -0
- package/src/services/synthetic-baseline-runner.ts +11 -0
- package/src/utils/production-workspace-guard.test.ts +62 -20
- package/src/utils/production-workspace-guard.ts +19 -16
- package/tests/bdd/cli-contract.steps.ts +343 -0
- package/tests/bdd/context-trace.steps.ts +229 -0
- package/tests/bdd/support/gherkin-loader.ts +147 -0
- package/tests/bdd/support/repo-root.ts +62 -0
- package/tests/bdd/support/vitest-bdd.ts +179 -0
- package/tests/commands/admission-gate-coverage.test.ts +79 -0
- package/tests/commands/candidate-audit-repair.test.ts +77 -0
- package/tests/commands/candidate-intake.test.ts +155 -10
- package/tests/commands/candidate-internalization-backfill.test.ts +30 -0
- package/tests/commands/cli-help-snapshot.test.ts +15 -1
- package/tests/commands/console-open.test.ts +229 -32
- package/tests/commands/diagnose.test.ts +619 -14
- package/tests/commands/errors-list.test.ts +678 -0
- package/tests/commands/health-codex.test.ts +243 -0
- package/tests/commands/health-host-wiring.test.ts +76 -0
- package/tests/commands/intent.test.ts +4 -4
- package/tests/commands/pain-record.test.ts +80 -0
- package/tests/commands/pain-retry.test.ts +94 -3
- package/tests/commands/run-rulehost-handler.test.ts +133 -0
- package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +227 -0
- package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +167 -0
- package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +262 -0
- package/tests/commands/runtime-activation-list-flag-wiring.test.ts +193 -0
- package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +222 -0
- package/tests/commands/runtime-activation.test.ts +1090 -5
- package/tests/commands/runtime-canary.test.ts +50 -0
- package/tests/commands/runtime-features.test.ts +2 -0
- package/tests/commands/runtime-init-empty-workspace.test.ts +234 -0
- package/tests/commands/runtime-init.test.ts +346 -0
- package/tests/commands/runtime-internalization-context-trace.test.ts +110 -0
- package/tests/commands/runtime-internalization.test.ts +2 -2
- package/tests/config-reader.test.ts +6 -5
- package/tests/e2e/cross-package-acceptance.test.ts +55 -13
- package/tests/services/mainline-snapshot-assembler.test.ts +6 -3
- package/tests/services/resolve-runtime-from-pd-config.test.ts +2 -0
- package/tests/services/rulehost-pipeline-runner.test.ts +1 -1
- package/vitest.config.ts +1 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { describe, it, expect } from 'vitest';
|
|
10
10
|
import { Command } from 'commander';
|
|
11
|
-
import { registerIntentCommand } from '../intent.js';
|
|
11
|
+
import { registerIntentCommand, parseLang } from '../intent.js';
|
|
12
12
|
|
|
13
13
|
type ActionOptions = Record<string, unknown>;
|
|
14
14
|
interface CapturedAction {
|
|
@@ -54,7 +54,7 @@ describe('pd intent — command registration', () => {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
describe('pd intent init — option metadata', () => {
|
|
57
|
-
it('has --workspace (-w), --force, --dry-run, --confirm, and --json options', () => {
|
|
57
|
+
it('has --workspace (-w), --force, --dry-run, --confirm, --lang, and --json options', () => {
|
|
58
58
|
const program = freshProgram();
|
|
59
59
|
registerIntentCommand(program);
|
|
60
60
|
const intentCmd = requireCmd(program.commands.find((c) => c.name() === 'intent'), 'intent');
|
|
@@ -65,6 +65,7 @@ describe('pd intent init — option metadata', () => {
|
|
|
65
65
|
expect(initCmd.options.find((o) => o.long === '--force')).toBeDefined();
|
|
66
66
|
expect(initCmd.options.find((o) => o.long === '--dry-run')).toBeDefined();
|
|
67
67
|
expect(initCmd.options.find((o) => o.long === '--confirm')).toBeDefined();
|
|
68
|
+
expect(initCmd.options.find((o) => o.long === '--lang')).toBeDefined();
|
|
68
69
|
expect(initCmd.options.find((o) => o.long === '--json')).toBeDefined();
|
|
69
70
|
});
|
|
70
71
|
|
|
@@ -82,7 +83,7 @@ describe('pd intent init — option metadata', () => {
|
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
describe('pd intent show — option metadata', () => {
|
|
85
|
-
it('has --workspace (-w) and --json options, no --force', () => {
|
|
86
|
+
it('has --workspace (-w), --lang, and --json options, no --force', () => {
|
|
86
87
|
const program = freshProgram();
|
|
87
88
|
registerIntentCommand(program);
|
|
88
89
|
const intentCmd = requireCmd(program.commands.find((c) => c.name() === 'intent'), 'intent');
|
|
@@ -90,6 +91,7 @@ describe('pd intent show — option metadata', () => {
|
|
|
90
91
|
|
|
91
92
|
expect(showCmd.options.find((o) => o.long === '--workspace')).toBeDefined();
|
|
92
93
|
expect(showCmd.options.find((o) => o.long === '--workspace')?.short).toBe('-w');
|
|
94
|
+
expect(showCmd.options.find((o) => o.long === '--lang')).toBeDefined();
|
|
93
95
|
expect(showCmd.options.find((o) => o.long === '--json')).toBeDefined();
|
|
94
96
|
expect(showCmd.options.find((o) => o.long === '--force')).toBeUndefined();
|
|
95
97
|
});
|
|
@@ -195,3 +197,86 @@ describe('pd intent show — parser-level dispatch', () => {
|
|
|
195
197
|
expect(captured.opts?.workspace).toBe('/tmp/ws');
|
|
196
198
|
});
|
|
197
199
|
});
|
|
200
|
+
|
|
201
|
+
describe('pd intent --lang — parser-level dispatch (cli-7)', () => {
|
|
202
|
+
it('init: parses --lang zh-CN', async () => {
|
|
203
|
+
const program = freshProgram();
|
|
204
|
+
const intentCmd = registerIntentCommand(program);
|
|
205
|
+
const initCmd = requireCmd(intentCmd.commands.find((c) => c.name() === 'init'), 'init');
|
|
206
|
+
const captured: CapturedAction = { opts: null };
|
|
207
|
+
attachCapture(initCmd, captured);
|
|
208
|
+
|
|
209
|
+
await program.parseAsync(['node', 'pd', 'intent', 'init', '--lang', 'zh-CN']);
|
|
210
|
+
expect(captured.opts).not.toBeNull();
|
|
211
|
+
expect(captured.opts?.lang).toBe('zh-CN');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('init: parses --lang en', async () => {
|
|
215
|
+
const program = freshProgram();
|
|
216
|
+
const intentCmd = registerIntentCommand(program);
|
|
217
|
+
const initCmd = requireCmd(intentCmd.commands.find((c) => c.name() === 'init'), 'init');
|
|
218
|
+
const captured: CapturedAction = { opts: null };
|
|
219
|
+
attachCapture(initCmd, captured);
|
|
220
|
+
|
|
221
|
+
await program.parseAsync(['node', 'pd', 'intent', 'init', '--lang', 'en']);
|
|
222
|
+
expect(captured.opts).not.toBeNull();
|
|
223
|
+
expect(captured.opts?.lang).toBe('en');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('init: lang defaults to undefined when not passed', async () => {
|
|
227
|
+
const program = freshProgram();
|
|
228
|
+
const intentCmd = registerIntentCommand(program);
|
|
229
|
+
const initCmd = requireCmd(intentCmd.commands.find((c) => c.name() === 'init'), 'init');
|
|
230
|
+
const captured: CapturedAction = { opts: null };
|
|
231
|
+
attachCapture(initCmd, captured);
|
|
232
|
+
|
|
233
|
+
await program.parseAsync(['node', 'pd', 'intent', 'init']);
|
|
234
|
+
expect(captured.opts).not.toBeNull();
|
|
235
|
+
expect(captured.opts?.lang).toBeUndefined();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('show: parses --lang en', async () => {
|
|
239
|
+
const program = freshProgram();
|
|
240
|
+
const intentCmd = registerIntentCommand(program);
|
|
241
|
+
const showCmd = requireCmd(intentCmd.commands.find((c) => c.name() === 'show'), 'show');
|
|
242
|
+
const captured: CapturedAction = { opts: null };
|
|
243
|
+
attachCapture(showCmd, captured);
|
|
244
|
+
|
|
245
|
+
await program.parseAsync(['node', 'pd', 'intent', 'show', '--lang', 'en']);
|
|
246
|
+
expect(captured.opts).not.toBeNull();
|
|
247
|
+
expect(captured.opts?.lang).toBe('en');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('show: lang defaults to undefined when not passed', async () => {
|
|
251
|
+
const program = freshProgram();
|
|
252
|
+
const intentCmd = registerIntentCommand(program);
|
|
253
|
+
const showCmd = requireCmd(intentCmd.commands.find((c) => c.name() === 'show'), 'show');
|
|
254
|
+
const captured: CapturedAction = { opts: null };
|
|
255
|
+
attachCapture(showCmd, captured);
|
|
256
|
+
|
|
257
|
+
await program.parseAsync(['node', 'pd', 'intent', 'show']);
|
|
258
|
+
expect(captured.opts).not.toBeNull();
|
|
259
|
+
expect(captured.opts?.lang).toBeUndefined();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('parseLang — invalid value rejection (rc-9)', () => {
|
|
264
|
+
it('returns zh-CN when value is undefined (default)', () => {
|
|
265
|
+
expect(parseLang(undefined)).toBe('zh-CN');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('returns zh-CN when value is zh-CN', () => {
|
|
269
|
+
expect(parseLang('zh-CN')).toBe('zh-CN');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('returns en when value is en', () => {
|
|
273
|
+
expect(parseLang('en')).toBe('en');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('returns null for invalid value (no silent fallback)', () => {
|
|
277
|
+
expect(parseLang('fr')).toBeNull();
|
|
278
|
+
expect(parseLang('zh')).toBeNull();
|
|
279
|
+
expect(parseLang('english')).toBeNull();
|
|
280
|
+
expect(parseLang('')).toBeNull();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI flag-wiring tests for `pd pain retry --maxTokens` (PRI-509 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - `--maxTokens <n>` parses to a number and is passed to the handler
|
|
6
|
+
* - `--maxTokens` falls back to undefined when the flag is omitted
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the flag-wiring test pattern from run-rulehost-flag-wiring.test.ts.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { registerPainRetryCommand } from '../pain-retry.js';
|
|
13
|
+
|
|
14
|
+
function freshProgram(): Command {
|
|
15
|
+
return new Command();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function attachCapture(cmd: Command, captured: { opts: Record<string, unknown> | null }) {
|
|
19
|
+
const origAction = cmd.action.bind(cmd);
|
|
20
|
+
cmd.action = ((handler: (opts: Record<string, unknown>) => void) => {
|
|
21
|
+
return origAction((opts: Record<string, unknown>) => {
|
|
22
|
+
captured.opts = opts;
|
|
23
|
+
return handler(opts);
|
|
24
|
+
});
|
|
25
|
+
}) as typeof cmd.action;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function register(painCmd: Command, captured: { opts: Record<string, unknown> | null }) {
|
|
29
|
+
const cmd = registerPainRetryCommand(painCmd, async (opts) => {
|
|
30
|
+
captured.opts = opts as unknown as Record<string, unknown>;
|
|
31
|
+
});
|
|
32
|
+
attachCapture(cmd, captured);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('pd pain retry --maxTokens flag wiring', () => {
|
|
36
|
+
let captured: { opts: Record<string, unknown> | null };
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
captured = { opts: null };
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('parses --maxTokens to a number and passes it to the handler', async () => {
|
|
43
|
+
const program = freshProgram();
|
|
44
|
+
const painCmd = program.command('pain');
|
|
45
|
+
register(painCmd, captured);
|
|
46
|
+
|
|
47
|
+
await program.parseAsync(['node', 'pd', 'pain', 'retry', '--pain-id', 'pain-1', '--runtime', 'pi-ai', '--maxTokens', '16000']);
|
|
48
|
+
|
|
49
|
+
expect(captured.opts).not.toBeNull();
|
|
50
|
+
expect(captured.opts?.maxTokens).toBe(16000);
|
|
51
|
+
expect(typeof captured.opts?.maxTokens).toBe('number');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('parses --maxTokens with a numeric string (parseInt path)', async () => {
|
|
55
|
+
const program = freshProgram();
|
|
56
|
+
const painCmd = program.command('pain');
|
|
57
|
+
register(painCmd, captured);
|
|
58
|
+
|
|
59
|
+
await program.parseAsync(['node', 'pd', 'pain', 'retry', '--pain-id', 'pain-1', '--runtime', 'pi-ai', '--maxTokens', '32000']);
|
|
60
|
+
|
|
61
|
+
expect(captured.opts?.maxTokens).toBe(32000);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('keeps maxTokens undefined when flag is omitted (falls back to config)', async () => {
|
|
65
|
+
const program = freshProgram();
|
|
66
|
+
const painCmd = program.command('pain');
|
|
67
|
+
register(painCmd, captured);
|
|
68
|
+
|
|
69
|
+
await program.parseAsync(['node', 'pd', 'pain', 'retry', '--pain-id', 'pain-1', '--runtime', 'pi-ai']);
|
|
70
|
+
|
|
71
|
+
expect(captured.opts?.maxTokens).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -52,6 +52,13 @@ function freshProgram(): Command {
|
|
|
52
52
|
return program;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** Find a subcommand by name, throwing if absent (replaces non-null assertions). */
|
|
56
|
+
function requireSubcommand(parent: Command, name: string): Command {
|
|
57
|
+
const cmd = parent.commands.find((c) => c.name() === name);
|
|
58
|
+
if (!cmd) throw new Error(`Expected subcommand "${name}" to be registered`);
|
|
59
|
+
return cmd;
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
56
63
|
// ── spec subcommand ───────────────────────────────────────────────────────
|
|
57
64
|
|
|
@@ -62,10 +69,10 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
62
69
|
const specCmd = rulecodeCmd.commands.find((c) => c.name() === 'spec');
|
|
63
70
|
expect(specCmd).toBeDefined();
|
|
64
71
|
|
|
65
|
-
const jsonOpt = specCmd
|
|
72
|
+
const jsonOpt = (specCmd as Command).options.find((o) => o.long === '--json');
|
|
66
73
|
expect(jsonOpt).toBeDefined();
|
|
67
74
|
|
|
68
|
-
const wsOpt = specCmd
|
|
75
|
+
const wsOpt = (specCmd as Command).options.find((o) => o.long === '--workspace');
|
|
69
76
|
expect(wsOpt).toBeDefined();
|
|
70
77
|
expect(wsOpt?.short).toBe('-w');
|
|
71
78
|
});
|
|
@@ -75,7 +82,7 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
75
82
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
76
83
|
|
|
77
84
|
const specCmd = rulecodeCmd.commands.find((c) => c.name() === 'spec');
|
|
78
|
-
const codeOpt = specCmd
|
|
85
|
+
const codeOpt = (specCmd as Command).options.find((o) => o.long === '--code');
|
|
79
86
|
expect(codeOpt).toBeUndefined();
|
|
80
87
|
});
|
|
81
88
|
|
|
@@ -88,16 +95,16 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
88
95
|
const validateCmd = rulecodeCmd.commands.find((c) => c.name() === 'validate');
|
|
89
96
|
expect(validateCmd).toBeDefined();
|
|
90
97
|
|
|
91
|
-
const codeOpt = validateCmd
|
|
98
|
+
const codeOpt = (validateCmd as Command).options.find((o) => o.long === '--code');
|
|
92
99
|
expect(codeOpt).toBeDefined();
|
|
93
100
|
|
|
94
|
-
const codeFileOpt = validateCmd
|
|
101
|
+
const codeFileOpt = (validateCmd as Command).options.find((o) => o.long === '--code-file');
|
|
95
102
|
expect(codeFileOpt).toBeDefined();
|
|
96
103
|
|
|
97
|
-
const jsonOpt = validateCmd
|
|
104
|
+
const jsonOpt = (validateCmd as Command).options.find((o) => o.long === '--json');
|
|
98
105
|
expect(jsonOpt).toBeDefined();
|
|
99
106
|
|
|
100
|
-
const wsOpt = validateCmd
|
|
107
|
+
const wsOpt = (validateCmd as Command).options.find((o) => o.long === '--workspace');
|
|
101
108
|
expect(wsOpt).toBeDefined();
|
|
102
109
|
expect(wsOpt?.short).toBe('-w');
|
|
103
110
|
});
|
|
@@ -105,7 +112,7 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
105
112
|
it('validate --code is NOT required at parser level (can use --code-file instead)', async () => {
|
|
106
113
|
const program = freshProgram();
|
|
107
114
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
108
|
-
const validateCmd = rulecodeCmd
|
|
115
|
+
const validateCmd = requireSubcommand(rulecodeCmd, 'validate');
|
|
109
116
|
const captured: CapturedAction = { opts: null };
|
|
110
117
|
attachCapture(validateCmd, captured);
|
|
111
118
|
|
|
@@ -113,7 +120,7 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
113
120
|
await program.parseAsync(['node', 'pd', 'rulecode', 'validate', '--json']);
|
|
114
121
|
|
|
115
122
|
expect(captured.opts).not.toBeNull();
|
|
116
|
-
expect(captured.opts
|
|
123
|
+
expect((captured.opts as ActionOptions).code).toBeUndefined();
|
|
117
124
|
});
|
|
118
125
|
|
|
119
126
|
// ── replay subcommand ─────────────────────────────────────────────────────
|
|
@@ -125,19 +132,19 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
125
132
|
const replayCmd = rulecodeCmd.commands.find((c) => c.name() === 'replay');
|
|
126
133
|
expect(replayCmd).toBeDefined();
|
|
127
134
|
|
|
128
|
-
const codeOpt = replayCmd
|
|
135
|
+
const codeOpt = (replayCmd as Command).options.find((o) => o.long === '--code');
|
|
129
136
|
expect(codeOpt).toBeDefined();
|
|
130
137
|
|
|
131
|
-
const codeFileOpt = replayCmd
|
|
138
|
+
const codeFileOpt = (replayCmd as Command).options.find((o) => o.long === '--code-file');
|
|
132
139
|
expect(codeFileOpt).toBeDefined();
|
|
133
140
|
|
|
134
|
-
const gtOpt = replayCmd
|
|
141
|
+
const gtOpt = (replayCmd as Command).options.find((o) => o.long === '--golden-trace');
|
|
135
142
|
expect(gtOpt).toBeDefined();
|
|
136
143
|
|
|
137
|
-
const jsonOpt = replayCmd
|
|
144
|
+
const jsonOpt = (replayCmd as Command).options.find((o) => o.long === '--json');
|
|
138
145
|
expect(jsonOpt).toBeDefined();
|
|
139
146
|
|
|
140
|
-
const wsOpt = replayCmd
|
|
147
|
+
const wsOpt = (replayCmd as Command).options.find((o) => o.long === '--workspace');
|
|
141
148
|
expect(wsOpt).toBeDefined();
|
|
142
149
|
expect(wsOpt?.short).toBe('-w');
|
|
143
150
|
});
|
|
@@ -147,7 +154,7 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
147
154
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
148
155
|
|
|
149
156
|
const replayCmd = rulecodeCmd.commands.find((c) => c.name() === 'replay');
|
|
150
|
-
const gtOpt = replayCmd
|
|
157
|
+
const gtOpt = (replayCmd as Command).options.find((o) => o.long === '--golden-trace');
|
|
151
158
|
expect(gtOpt?.required).toBe(true);
|
|
152
159
|
});
|
|
153
160
|
|
|
@@ -168,20 +175,20 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
168
175
|
it('parseAsync dispatches spec subcommand with json=true', async () => {
|
|
169
176
|
const program = freshProgram();
|
|
170
177
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
171
|
-
const specCmd = rulecodeCmd
|
|
178
|
+
const specCmd = requireSubcommand(rulecodeCmd, 'spec');
|
|
172
179
|
const captured: CapturedAction = { opts: null };
|
|
173
180
|
attachCapture(specCmd, captured);
|
|
174
181
|
|
|
175
182
|
await program.parseAsync(['node', 'pd', 'rulecode', 'spec', '--json']);
|
|
176
183
|
|
|
177
184
|
expect(captured.opts).not.toBeNull();
|
|
178
|
-
expect(captured.opts
|
|
185
|
+
expect((captured.opts as ActionOptions).json).toBe(true);
|
|
179
186
|
});
|
|
180
187
|
|
|
181
188
|
it('parseAsync dispatches validate with --code', async () => {
|
|
182
189
|
const program = freshProgram();
|
|
183
190
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
184
|
-
const validateCmd = rulecodeCmd
|
|
191
|
+
const validateCmd = requireSubcommand(rulecodeCmd, 'validate');
|
|
185
192
|
const captured: CapturedAction = { opts: null };
|
|
186
193
|
attachCapture(validateCmd, captured);
|
|
187
194
|
|
|
@@ -192,14 +199,14 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
192
199
|
]);
|
|
193
200
|
|
|
194
201
|
expect(captured.opts).not.toBeNull();
|
|
195
|
-
expect(captured.opts
|
|
196
|
-
expect(captured.opts
|
|
202
|
+
expect((captured.opts as ActionOptions).code).toContain('function evaluate');
|
|
203
|
+
expect((captured.opts as ActionOptions).json).toBe(true);
|
|
197
204
|
});
|
|
198
205
|
|
|
199
206
|
it('parseAsync dispatches replay with --code and --golden-trace', async () => {
|
|
200
207
|
const program = freshProgram();
|
|
201
208
|
const rulecodeCmd = registerRulecodeCommand(program);
|
|
202
|
-
const replayCmd = rulecodeCmd
|
|
209
|
+
const replayCmd = requireSubcommand(rulecodeCmd, 'replay');
|
|
203
210
|
const captured: CapturedAction = { opts: null };
|
|
204
211
|
attachCapture(replayCmd, captured);
|
|
205
212
|
|
|
@@ -211,9 +218,9 @@ describe('pd rulecode — flag wiring (CLI gate rule 7)', () => {
|
|
|
211
218
|
]);
|
|
212
219
|
|
|
213
220
|
expect(captured.opts).not.toBeNull();
|
|
214
|
-
expect(captured.opts
|
|
215
|
-
expect(captured.opts
|
|
216
|
-
expect(captured.opts
|
|
221
|
+
expect((captured.opts as ActionOptions).code).toContain('function evaluate');
|
|
222
|
+
expect((captured.opts as ActionOptions).goldenTrace).toBe('/tmp/trace.json');
|
|
223
|
+
expect((captured.opts as ActionOptions).json).toBe(true);
|
|
217
224
|
});
|
|
218
225
|
|
|
219
226
|
it('parseAsync rejects replay without --golden-trace (requiredOption)', async () => {
|
|
@@ -88,6 +88,18 @@ describe('pd runtime internalization run-rulehost — flag wiring (CLI gate rule
|
|
|
88
88
|
expect(opt?.long).toBe('--json');
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
it('parses --behavior-examples as the Owner-labelled evidence file', async () => {
|
|
92
|
+
const program = freshProgram();
|
|
93
|
+
const intCmd = program.command('internalization');
|
|
94
|
+
const runCmd = registerRunRuleHostCommand(intCmd);
|
|
95
|
+
const captured: CapturedAction = { opts: null };
|
|
96
|
+
attachCapture(runCmd, captured);
|
|
97
|
+
|
|
98
|
+
await program.parseAsync(['node', 'pd', 'internalization', 'run-rulehost', '--pain-id', 'pain-1', '--behavior-examples', 'examples.json']);
|
|
99
|
+
|
|
100
|
+
expect(captured.opts?.behaviorExamples).toBe('examples.json');
|
|
101
|
+
});
|
|
102
|
+
|
|
91
103
|
it('registers --pain-id as required option', () => {
|
|
92
104
|
const program = freshProgram();
|
|
93
105
|
const intCmd = program.command('internalization');
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared admission gate helper for CLI commands that call intakeService.intake().
|
|
3
|
+
*
|
|
4
|
+
* PRI-442 Stage 4 / PRI-503: previously only `candidate.ts` checked the gate;
|
|
5
|
+
* `pain-retry.ts` and `diagnose.ts` bypassed it (ERR-089 sibling-branch defect).
|
|
6
|
+
* This shared helper is the single entry point so future CLI commands that
|
|
7
|
+
* call intake() can reuse it instead of re-implementing (or skipping) the check.
|
|
8
|
+
*
|
|
9
|
+
* Runtime Contract: rc-9-no-silent-fallback — refusal includes reason + nextAction
|
|
10
|
+
* (callers MUST surface these in their structured CLI output).
|
|
11
|
+
*/
|
|
12
|
+
import { evaluateCandidateAdmissionFromRecord, type AdmissionGateResult } from '@principles/core/runtime-v2';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check admission gate for a candidate. Returns null if admitted (caller may
|
|
16
|
+
* proceed); returns AdmissionGateResult with reason + nextAction if refused
|
|
17
|
+
* (caller must refuse and surface reason/nextAction in CLI output).
|
|
18
|
+
*
|
|
19
|
+
* @param candidate - Must carry recommendationKind and confidence from the candidate record
|
|
20
|
+
*/
|
|
21
|
+
export function checkAdmissionGate(
|
|
22
|
+
candidate: { recommendationKind: string; confidence: number | null },
|
|
23
|
+
): AdmissionGateResult | null {
|
|
24
|
+
const admission = evaluateCandidateAdmissionFromRecord({
|
|
25
|
+
recommendationKind: candidate.recommendationKind as Parameters<typeof evaluateCandidateAdmissionFromRecord>[0]['recommendationKind'],
|
|
26
|
+
confidence: candidate.confidence,
|
|
27
|
+
});
|
|
28
|
+
if (admission.decision === 'admitted') return null;
|
|
29
|
+
return admission;
|
|
30
|
+
}
|
|
@@ -27,6 +27,7 @@ import { loadLedger, getLedgerFilePathPublic } from '@principles/core/principle-
|
|
|
27
27
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
28
28
|
import { createRemediationResult, remediationAction } from './remediation-output.js';
|
|
29
29
|
import type { RemediationResult } from './remediation-output.js';
|
|
30
|
+
import { checkAdmissionGate } from './admission-gate.js';
|
|
30
31
|
|
|
31
32
|
// ── Interfaces ────────────────────────────────────────────────────────────────
|
|
32
33
|
|
|
@@ -353,6 +354,7 @@ export async function handleCandidateInternalize(opts: CandidateInternalizeOptio
|
|
|
353
354
|
}
|
|
354
355
|
|
|
355
356
|
const seed = buildDreamerSeedFromCandidate(candidate, { route: decision.route, ready: decision.ready, sourcePainId });
|
|
357
|
+
// eslint-disable-next-line no-restricted-syntax -- 'in' required for discriminated union narrowing (BridgeTaskSeed | BridgeDecision)
|
|
356
358
|
if ('decision' in seed) {
|
|
357
359
|
const decisionResult = seed as { decision: string; reason?: string; taskId?: string };
|
|
358
360
|
const reason = decisionResult.decision === 'already_exists'
|
|
@@ -494,6 +496,13 @@ export async function handleCandidateShow(opts: CandidateShowOptions): Promise<v
|
|
|
494
496
|
if (result.ledgerEntryId) {
|
|
495
497
|
console.log(` Ledger Entry: ${result.ledgerEntryId}`);
|
|
496
498
|
}
|
|
499
|
+
// F10-1: surface dangling sourceRunId warning in text mode (rc-9).
|
|
500
|
+
if (result.warning) {
|
|
501
|
+
console.log(` ⚠ WARNING: ${result.warning}`);
|
|
502
|
+
}
|
|
503
|
+
if (result.nextAction) {
|
|
504
|
+
console.log(` Next Action: ${result.nextAction}`);
|
|
505
|
+
}
|
|
497
506
|
console.log('');
|
|
498
507
|
} finally {
|
|
499
508
|
await stateManager.close();
|
|
@@ -520,12 +529,37 @@ export async function handleCandidateIntake(opts: CandidateIntakeOptions): Promi
|
|
|
520
529
|
const ledgerAdapter = new PrincipleTreeLedgerAdapter({ stateDir: path.join(workspaceDir, '.state') });
|
|
521
530
|
const service = new CandidateIntakeService({ stateManager, ledgerAdapter });
|
|
522
531
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
532
|
+
// Fetch candidate once for admission gate check and downstream paths.
|
|
533
|
+
const candidate = await stateManager.getCandidate(opts.candidateId);
|
|
534
|
+
if (!candidate) {
|
|
535
|
+
console.error(`Candidate not found: ${opts.candidateId}`);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// PRI-442 Stage 4: admission gate check — refuse non-admitted candidates
|
|
541
|
+
// to prevent CLI from bypassing the production admission gate.
|
|
542
|
+
const admissionBlock = checkAdmissionGate(candidate);
|
|
543
|
+
if (admissionBlock) {
|
|
544
|
+
const result = {
|
|
545
|
+
candidateId: opts.candidateId,
|
|
546
|
+
status: 'refused',
|
|
547
|
+
admissionDecision: admissionBlock.decision,
|
|
548
|
+
reason: admissionBlock.reason,
|
|
549
|
+
nextAction: admissionBlock.nextAction,
|
|
550
|
+
};
|
|
551
|
+
if (opts.json) {
|
|
552
|
+
console.log(JSON.stringify(result, null, 2));
|
|
553
|
+
} else {
|
|
554
|
+
console.error(`Admission gate refused candidate ${opts.candidateId}: ${admissionBlock.decision}`);
|
|
555
|
+
console.error(` Reason: ${admissionBlock.reason}`);
|
|
556
|
+
console.error(` Next Action: ${admissionBlock.nextAction}`);
|
|
528
557
|
}
|
|
558
|
+
process.exit(1);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (opts.dryRun) {
|
|
529
563
|
const artifact = await stateManager.getArtifact(candidate.artifactId);
|
|
530
564
|
if (!artifact) {
|
|
531
565
|
console.error(`Artifact not found for candidate: ${opts.candidateId}`);
|
|
@@ -564,7 +598,6 @@ export async function handleCandidateIntake(opts: CandidateIntakeOptions): Promi
|
|
|
564
598
|
const entry = await service.intake(opts.candidateId);
|
|
565
599
|
|
|
566
600
|
// Check if already consumed before this call
|
|
567
|
-
const candidate = await stateManager.getCandidate(opts.candidateId);
|
|
568
601
|
if (candidate?.status === 'consumed') {
|
|
569
602
|
const infoMessage = `Candidate ${opts.candidateId} was already consumed. Ledger entry: ${entry.id}`;
|
|
570
603
|
if (opts.json) {
|
|
@@ -724,6 +757,30 @@ export async function handleCandidateRepair(opts: CandidateRepairOptions): Promi
|
|
|
724
757
|
process.exit(1);
|
|
725
758
|
}
|
|
726
759
|
|
|
760
|
+
// PRI-442 Stage 4: admission gate check — even for consumed candidates,
|
|
761
|
+
// re-verify admission before re-intaking to prevent bypassing the gate.
|
|
762
|
+
// A consumed candidate that would fail the gate indicates data corruption
|
|
763
|
+
// or a prior bypass; refuse and surface the reason.
|
|
764
|
+
const admissionBlock = checkAdmissionGate(candidate);
|
|
765
|
+
if (admissionBlock) {
|
|
766
|
+
const result = {
|
|
767
|
+
candidateId: opts.candidateId,
|
|
768
|
+
status: 'refused',
|
|
769
|
+
admissionDecision: admissionBlock.decision,
|
|
770
|
+
reason: admissionBlock.reason,
|
|
771
|
+
nextAction: admissionBlock.nextAction,
|
|
772
|
+
};
|
|
773
|
+
if (opts.json) {
|
|
774
|
+
console.log(JSON.stringify(result, null, 2));
|
|
775
|
+
} else {
|
|
776
|
+
console.error(`Admission gate refused candidate ${opts.candidateId}: ${admissionBlock.decision}`);
|
|
777
|
+
console.error(` Reason: ${admissionBlock.reason}`);
|
|
778
|
+
console.error(` Next Action: ${admissionBlock.nextAction}`);
|
|
779
|
+
}
|
|
780
|
+
process.exit(1);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
727
784
|
const ledgerAdapter = new PrincipleTreeLedgerAdapter({ stateDir: path.join(workspaceDir, '.state') });
|
|
728
785
|
const service = new CandidateIntakeService({ stateManager, ledgerAdapter });
|
|
729
786
|
|
|
@@ -931,6 +988,7 @@ export async function handleCandidateInternalizationBackfill(opts: CandidateBack
|
|
|
931
988
|
continue;
|
|
932
989
|
}
|
|
933
990
|
const seed = buildDreamerSeedFromCandidate(candidate, { route: decision.route, ready: decision.ready, sourcePainId });
|
|
991
|
+
// eslint-disable-next-line no-restricted-syntax -- 'in' required for discriminated union narrowing (BridgeTaskSeed | BridgeDecision)
|
|
934
992
|
if ('decision' in seed) {
|
|
935
993
|
output.deferred++;
|
|
936
994
|
const decisionResult = seed as { decision: string; reason?: string; taskId?: string };
|
|
@@ -998,6 +1056,15 @@ export async function handleCandidateInternalizationBackfill(opts: CandidateBack
|
|
|
998
1056
|
continue;
|
|
999
1057
|
}
|
|
1000
1058
|
|
|
1059
|
+
// PRI-442 Stage 4: admission gate check — refuse non-admitted pending
|
|
1060
|
+
// candidates to prevent backfill from bypassing the admission gate.
|
|
1061
|
+
const admissionBlock = checkAdmissionGate(candidate);
|
|
1062
|
+
if (admissionBlock) {
|
|
1063
|
+
output.intakeFailed++;
|
|
1064
|
+
output.results.push({ candidateId, route: 'unknown', status: 'intake_failed', reason: `Admission gate refused: ${admissionBlock.reason}`, statusBefore: 'pending', statusAfter: 'pending', intakeDecision: 'skipped', seedDecision: 'skipped', nextAction: admissionBlock.nextAction });
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1001
1068
|
const recommendation = resolveCandidateRecommendation(candidate, stateManager, candidateId);
|
|
1002
1069
|
const decision = decideInternalizationRoute(recommendation as Parameters<typeof decideInternalizationRoute>[0]);
|
|
1003
1070
|
|
|
@@ -1010,6 +1077,7 @@ export async function handleCandidateInternalizationBackfill(opts: CandidateBack
|
|
|
1010
1077
|
continue;
|
|
1011
1078
|
}
|
|
1012
1079
|
const seed = buildDreamerSeedFromCandidate(candidate, { route: decision.route, ready: decision.ready, sourcePainId });
|
|
1080
|
+
// eslint-disable-next-line no-restricted-syntax -- 'in' required for discriminated union narrowing (BridgeTaskSeed | BridgeDecision)
|
|
1013
1081
|
if ('decision' in seed) {
|
|
1014
1082
|
output.deferred++;
|
|
1015
1083
|
const decisionResult = seed as { decision: string; reason?: string; taskId?: string };
|
package/src/commands/console.ts
CHANGED
|
@@ -72,6 +72,20 @@ export async function handleConsole(opts: ConsoleOptions = {}): Promise<void> {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// EP-06 regression guard (PR #1169): verify web UI bundle exists before launch.
|
|
76
|
+
// Without dist/web/index.html the server returns 404 "Run npm run build:ui first".
|
|
77
|
+
const webIndex = path.join(consoleDir, 'dist', 'web', 'index.html');
|
|
78
|
+
if (!fs.existsSync(webIndex)) {
|
|
79
|
+
const msg = `Console web UI not found at ${webIndex}. The console bundle is corrupted. Re-run installer.`;
|
|
80
|
+
if (opts.json) {
|
|
81
|
+
console.log(JSON.stringify({ success: false, reason: msg, nextAction: 'npx create-principles-disciple' }));
|
|
82
|
+
} else {
|
|
83
|
+
console.error(msg);
|
|
84
|
+
}
|
|
85
|
+
process.exit(1);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
const port = opts.port || '3100';
|
|
76
90
|
const host = '127.0.0.1';
|
|
77
91
|
const args = [serverEntry, '--workspace', workspaceDir, '--port', port, '--host', host];
|
|
@@ -305,6 +319,32 @@ export async function handleConsoleOpen(opts: ConsoleOpenOptions = {}): Promise<
|
|
|
305
319
|
return;
|
|
306
320
|
}
|
|
307
321
|
|
|
322
|
+
// EP-06 regression guard (PR #1169): verify web UI bundle exists before launch.
|
|
323
|
+
// Without dist/web/index.html the server returns 404 "Run npm run build:ui first"
|
|
324
|
+
// — a fatal first-impression bug for new users.
|
|
325
|
+
const webIndex = path.join(consoleDir, 'dist', 'web', 'index.html');
|
|
326
|
+
if (!fs.existsSync(webIndex)) {
|
|
327
|
+
const result: ConsoleLaunchResult = {
|
|
328
|
+
status: 'failed',
|
|
329
|
+
url: '',
|
|
330
|
+
port: 0,
|
|
331
|
+
host: '127.0.0.1',
|
|
332
|
+
workspaceDir,
|
|
333
|
+
reused: false,
|
|
334
|
+
browserOpened: false,
|
|
335
|
+
reason: 'console_web_ui_missing',
|
|
336
|
+
nextAction: `Re-run installer: npx create-principles-disciple (expected ${webIndex})`,
|
|
337
|
+
};
|
|
338
|
+
if (opts.json) {
|
|
339
|
+
console.log(JSON.stringify(result, null, 2));
|
|
340
|
+
} else {
|
|
341
|
+
console.error(`error: ${result.reason}`);
|
|
342
|
+
console.error(`next: ${result.nextAction}`);
|
|
343
|
+
}
|
|
344
|
+
process.exit(1);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
308
348
|
// 4) Read auth token for health probes (PD_CONSOLE_TOKEN)
|
|
309
349
|
const token = opts.token ?? process.env.PD_CONSOLE_TOKEN;
|
|
310
350
|
|
|
@@ -545,6 +585,7 @@ export async function handleConsoleOpen(opts: ConsoleOpenOptions = {}): Promise<
|
|
|
545
585
|
? 'Browser opened to the Console. Press Ctrl+C to stop.'
|
|
546
586
|
: `Open ${plan.url} in your browser. Press Ctrl+C to stop.`,
|
|
547
587
|
};
|
|
588
|
+
if (typeof child.pid === 'number') out.serverPid = child.pid;
|
|
548
589
|
if (plan.reason) out.reason = plan.reason;
|
|
549
590
|
if (browserWarning) {
|
|
550
591
|
out.reason = out.reason ? `${out.reason}; browser_open_failed: ${browserWarning}` : `browser_open_failed: ${browserWarning}`;
|
|
@@ -183,8 +183,10 @@ export async function handleDemoStoryA(opts: DemoStoryAOptions): Promise<void> {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
if (result.status !== 'passed') {
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
if (!opts.json) {
|
|
187
|
+
console.error('');
|
|
188
|
+
console.error(`FAIL: status=${result.status}`);
|
|
189
|
+
}
|
|
188
190
|
process.exitCode = 1;
|
|
189
191
|
}
|
|
190
192
|
} finally {
|