@principles/pd-cli 1.133.0 → 1.134.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 +40 -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/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 +40 -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/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 +19 -0
- 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
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import { RuntimeStateManager, ActivationDispatcher, PromptWriter, DeferArchiveWriter, RuleHostWriter, createProductionGateDeps, SqliteActivationStateStore, SqliteApprovalQueueStore, SqlitePIArtifactStore, isArtifactRevisionOf, } from '@principles/core/runtime-v2';
|
|
2
|
+
import { RuntimeStateManager, ActivationDispatcher, PromptWriter, DeferArchiveWriter, RuleHostWriter, createProductionGateDeps, SqliteActivationStateStore, SqliteApprovalQueueStore, SqlitePIArtifactStore, ApprovalQueue, ApprovalCompletionService, isArtifactRevisionOf, extractEvidenceRefs, extractPrincipleId, } from '@principles/core/runtime-v2';
|
|
3
3
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
4
|
+
import { loadPdConfig, computeFlagsFromLoadResult } from '../services/pd-config-loader.js';
|
|
5
|
+
/**
|
|
6
|
+
* Type guard for parsed JSON objects (rc-2-no-as-bypass).
|
|
7
|
+
* Replaces `as Record<string, unknown>` casts on untrusted contentJson.
|
|
8
|
+
*/
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
}
|
|
4
12
|
function mapRolloutDecision(reviewDecision) {
|
|
5
13
|
if (!reviewDecision)
|
|
6
14
|
return 'require_approval';
|
|
@@ -124,12 +132,16 @@ export async function handleRuntimeActivationDispatch(opts) {
|
|
|
124
132
|
};
|
|
125
133
|
const activationStateStore = new SqliteActivationStateStore(stateManager.connection);
|
|
126
134
|
const approvalQueueStore = new SqliteApprovalQueueStore(stateManager.connection);
|
|
135
|
+
const featureFlags = computeFlagsFromLoadResult(loadPdConfig(workspaceDir));
|
|
127
136
|
// Wire all three MVP-Core writers, including RuleHostWriter for code_tool_hook.
|
|
128
137
|
// PRI-408: fixes P0 breakpoint where code_tool_hook channel could not activate.
|
|
129
138
|
const dispatcher = new ActivationDispatcher(artifactReadModel, activationStateStore, {
|
|
130
139
|
writers: [
|
|
131
140
|
new PromptWriter(),
|
|
132
|
-
new RuleHostWriter({
|
|
141
|
+
new RuleHostWriter({
|
|
142
|
+
gateDeps: createProductionGateDeps(),
|
|
143
|
+
featureFlagProbe: (flagId) => featureFlags.flags[flagId]?.enabled === true,
|
|
144
|
+
}),
|
|
133
145
|
new DeferArchiveWriter(),
|
|
134
146
|
],
|
|
135
147
|
approvalQueueStore,
|
|
@@ -162,7 +174,7 @@ export async function handleRuntimeActivationDeactivate(opts) {
|
|
|
162
174
|
ok: false,
|
|
163
175
|
activationId: '',
|
|
164
176
|
reason: 'activation_id_required',
|
|
165
|
-
nextAction: 'Provide --activation-id <id> from `pd
|
|
177
|
+
nextAction: 'Provide --activation-id <id> from `pd activation list`',
|
|
166
178
|
};
|
|
167
179
|
if (opts.json) {
|
|
168
180
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -189,7 +201,7 @@ export async function handleRuntimeActivationDeactivate(opts) {
|
|
|
189
201
|
ok: false,
|
|
190
202
|
activationId: opts.activationId,
|
|
191
203
|
reason: 'not_found_or_already_deactivated',
|
|
192
|
-
nextAction: 'Check activation ID with `pd
|
|
204
|
+
nextAction: 'Check activation ID with `pd activation list`, or it may already be deactivated',
|
|
193
205
|
};
|
|
194
206
|
if (opts.json) {
|
|
195
207
|
// Strict JSON mode: exactly one parseable JSON object on stdout
|
|
@@ -232,6 +244,86 @@ export async function handleRuntimeActivationDeactivate(opts) {
|
|
|
232
244
|
await stateManager.close();
|
|
233
245
|
}
|
|
234
246
|
}
|
|
247
|
+
export async function handleRuntimeActivationPromote(opts) {
|
|
248
|
+
const activationId = opts.activationId?.trim() ?? '';
|
|
249
|
+
const refuse = (reason, nextAction) => {
|
|
250
|
+
const result = { ok: false, decision: 'refused', activationId, reason, nextAction };
|
|
251
|
+
if (opts.json)
|
|
252
|
+
console.log(JSON.stringify(result));
|
|
253
|
+
else {
|
|
254
|
+
console.error(`Promotion refused: ${reason}`);
|
|
255
|
+
console.error(`Next action: ${nextAction}`);
|
|
256
|
+
}
|
|
257
|
+
process.exitCode = 1;
|
|
258
|
+
};
|
|
259
|
+
if (!activationId) {
|
|
260
|
+
refuse('activation_id_required', 'Provide --activation-id from `pd activation list --channel code_tool_hook`.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (opts.dryRun === true && opts.confirm === true) {
|
|
264
|
+
refuse('dry_run_confirm_mutually_exclusive', 'Choose either --dry-run or --confirm.');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let stateManager;
|
|
268
|
+
try {
|
|
269
|
+
const workspaceDir = opts.workspace ? path.resolve(opts.workspace) : resolveWorkspaceDir();
|
|
270
|
+
stateManager = new RuntimeStateManager({ workspaceDir });
|
|
271
|
+
await stateManager.initialize();
|
|
272
|
+
const store = new SqliteActivationStateStore(stateManager.connection);
|
|
273
|
+
const activeHooks = await store.listCodeToolHookActivations(false);
|
|
274
|
+
// CodeRabbit PR2 Comment 3: dry-run must apply the same eligibility checks
|
|
275
|
+
// as the real promote path. `promoteActivation` runs a COUNT guard inside a
|
|
276
|
+
// BEGIN IMMEDIATE transaction and refuses when the count of matching shadow
|
|
277
|
+
// rows is not exactly 1. The previous dry-run branch used `find()` (returns
|
|
278
|
+
// the first match) and reported `would_promote` even when duplicates would
|
|
279
|
+
// make the confirm path throw. Mirror the store's uniqueness check here so
|
|
280
|
+
// dry-run and confirm agree (cli-5: failure paths must not mutate state;
|
|
281
|
+
// cli-6: degraded/refused results carry a structured reason + nextAction).
|
|
282
|
+
const matchingShadows = activeHooks.filter((record) => record.activationId === activationId && record.action === 'code_tool_hook_shadow_activate');
|
|
283
|
+
if (matchingShadows.length === 0) {
|
|
284
|
+
refuse('not_found_inactive_or_not_shadow', 'Refresh `pd activation list --channel code_tool_hook`; only active shadow activations can be promoted.');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (matchingShadows.length > 1) {
|
|
288
|
+
refuse('duplicate_shadow_activations', `${matchingShadows.length} shadow activations share activation_id=${activationId}; resolve duplicates before promoting.`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (opts.confirm !== true) {
|
|
292
|
+
const result = {
|
|
293
|
+
ok: true,
|
|
294
|
+
decision: 'would_promote',
|
|
295
|
+
activationId,
|
|
296
|
+
nextAction: `Run pd activation promote --activation-id ${activationId} --confirm to enable live blocking.`,
|
|
297
|
+
};
|
|
298
|
+
if (opts.json)
|
|
299
|
+
console.log(JSON.stringify(result));
|
|
300
|
+
else {
|
|
301
|
+
console.log(`Would promote: ${activationId}`);
|
|
302
|
+
console.log(` nextAction: ${result.nextAction}`);
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const promotedAt = new Date().toISOString();
|
|
307
|
+
const promoted = await store.promoteActivation(activationId, promotedAt);
|
|
308
|
+
if (!promoted) {
|
|
309
|
+
refuse('promotion_precondition_changed', 'Refresh the activation list; the activation changed before promotion.');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const result = { ok: true, decision: 'promoted', activationId, promotedAt };
|
|
313
|
+
if (opts.json)
|
|
314
|
+
console.log(JSON.stringify(result));
|
|
315
|
+
else {
|
|
316
|
+
console.log(`Promoted live: ${activationId}`);
|
|
317
|
+
console.log(` promotedAt: ${promotedAt}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
refuse(`promotion_failed: ${err instanceof Error ? err.message : String(err)}`, 'Check workspace configuration and database integrity; no promotion was applied.');
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
await stateManager?.close();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
235
327
|
export async function handleRuntimeActivationList(opts) {
|
|
236
328
|
// P2 #5 fix: fail loud on invalid channel instead of silently listing all.
|
|
237
329
|
const VALID_CHANNELS = new Set(['prompt', 'code_tool_hook', undefined]);
|
|
@@ -275,25 +367,170 @@ export async function handleRuntimeActivationList(opts) {
|
|
|
275
367
|
const filtered = opts.includeDeactivated
|
|
276
368
|
? records
|
|
277
369
|
: records.filter(r => r.deactivatedAt === null);
|
|
370
|
+
// F9-1: Validate artifact_id → pi_artifacts.artifact_id reference for each
|
|
371
|
+
// activation. A dangling reference means the activation cannot function
|
|
372
|
+
// (RuleHost cannot load implementationCode, dispatcher cannot verify
|
|
373
|
+
// lineage). Emit degraded status with reason + nextAction instead of
|
|
374
|
+
// silently returning corrupted records (rc-9-no-silent-fallback,
|
|
375
|
+
// rc-6-lineage-consistency; related ERR: ERR-002).
|
|
376
|
+
const { piArtifactStore } = stateManager;
|
|
377
|
+
const uniqueArtifactIds = new Set(filtered.map(r => r.artifactId));
|
|
378
|
+
const danglingArtifactIds = new Set();
|
|
379
|
+
// PRI-491: Map artifactId to { contextVersion, evidenceRefs } extracted
|
|
380
|
+
// from contentJson. rc-1/rc-2: contentJson is parsed as unknown and
|
|
381
|
+
// type-narrowed; never `as`-cast without prior typeof check.
|
|
382
|
+
const artifactMetadata = new Map();
|
|
383
|
+
for (const artifactId of uniqueArtifactIds) {
|
|
384
|
+
try {
|
|
385
|
+
const artifact = await piArtifactStore.getArtifactById(artifactId);
|
|
386
|
+
if (!artifact) {
|
|
387
|
+
danglingArtifactIds.add(artifactId);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
// PRI-491: Parse contentJson to extract contextVersion + evidenceRefs.
|
|
391
|
+
// rc-1: treat parsed JSON as unknown; rc-2: narrow with typeof before use.
|
|
392
|
+
let parsedContent = null;
|
|
393
|
+
try {
|
|
394
|
+
const parsed = JSON.parse(artifact.contentJson);
|
|
395
|
+
if (isRecord(parsed)) {
|
|
396
|
+
parsedContent = parsed;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
// Malformed contentJson - treat as no metadata (not dangling, just unreadable)
|
|
401
|
+
}
|
|
402
|
+
const requiresCtxV2 = parsedContent !== null
|
|
403
|
+
&& Object.hasOwn(parsedContent, 'requiresContextVersion')
|
|
404
|
+
&& parsedContent.requiresContextVersion === 2;
|
|
405
|
+
const contextVersion = requiresCtxV2 ? 'v2' : 'v1';
|
|
406
|
+
const evidenceRefs = parsedContent !== null ? extractEvidenceRefs(parsedContent) : null;
|
|
407
|
+
// PRI-500: extract principleId to match Console's ActivationRecord field.
|
|
408
|
+
// Uses extractPrincipleId (4-step fallback) so dreamer artifacts whose
|
|
409
|
+
// sourcePrincipleId was stripped still resolve via contentJson.
|
|
410
|
+
const principleId = extractPrincipleId(toSnapshot(artifact));
|
|
411
|
+
artifactMetadata.set(artifactId, { contextVersion, evidenceRefs, principleId });
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
// Treat lookup failure as dangling - fail loud rather than silent (rc-9).
|
|
415
|
+
danglingArtifactIds.add(artifactId);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const hasDangling = danglingArtifactIds.size > 0;
|
|
419
|
+
// PRI-491: Probe rulecode_context_v2 flag to determine suspended_by_flag
|
|
420
|
+
// status for v2 activations. When the flag is off, v2 activations are
|
|
421
|
+
// suspended (not executing) even though they remain active in the DB.
|
|
422
|
+
const featureFlags = computeFlagsFromLoadResult(loadPdConfig(workspaceDir));
|
|
423
|
+
const v2FlagEnabled = featureFlags.flags.rulecode_context_v2?.enabled === true;
|
|
424
|
+
const annotated = filtered.map(r => {
|
|
425
|
+
// Derive mode from action (shadow_activate -> shadow, live_activate -> live)
|
|
426
|
+
const mode = r.action === 'code_tool_hook_shadow_activate'
|
|
427
|
+
? 'shadow'
|
|
428
|
+
: r.action === 'code_tool_hook_live_activate'
|
|
429
|
+
? 'live'
|
|
430
|
+
: undefined;
|
|
431
|
+
// Look up artifact metadata (contextVersion, evidenceRefs)
|
|
432
|
+
const meta = artifactMetadata.get(r.artifactId);
|
|
433
|
+
const contextVersion = meta?.contextVersion;
|
|
434
|
+
const evidenceRefs = meta?.evidenceRefs ?? undefined;
|
|
435
|
+
const evidenceSummary = evidenceRefs && evidenceRefs.length > 0
|
|
436
|
+
? `${evidenceRefs.length} evidence ref(s): ${evidenceRefs.slice(0, 3).join(', ')}${evidenceRefs.length > 3 ? '...' : ''}`
|
|
437
|
+
: undefined;
|
|
438
|
+
// Derive status: deactivated > suspended_by_flag > active
|
|
439
|
+
let status;
|
|
440
|
+
let nextAction;
|
|
441
|
+
if (r.deactivatedAt) {
|
|
442
|
+
status = 'deactivated';
|
|
443
|
+
nextAction = undefined;
|
|
444
|
+
}
|
|
445
|
+
else if (contextVersion === 'v2' && !v2FlagEnabled) {
|
|
446
|
+
status = 'suspended_by_flag';
|
|
447
|
+
nextAction = `Enable rulecode_context_v2 flag or deactivate: pd activation deactivate --activation-id ${r.activationId} --confirm`;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
status = 'active';
|
|
451
|
+
if (mode === 'shadow') {
|
|
452
|
+
nextAction = `pd activation promote --activation-id ${r.activationId} --confirm`;
|
|
453
|
+
}
|
|
454
|
+
else if (mode === 'live') {
|
|
455
|
+
nextAction = `pd activation deactivate --activation-id ${r.activationId} --confirm`;
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
nextAction = undefined;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const record = {
|
|
462
|
+
...r,
|
|
463
|
+
principleId: meta?.principleId ?? 'unlinked',
|
|
464
|
+
mode,
|
|
465
|
+
status,
|
|
466
|
+
contextVersion,
|
|
467
|
+
evidenceRefs,
|
|
468
|
+
evidenceSummary,
|
|
469
|
+
nextAction,
|
|
470
|
+
};
|
|
471
|
+
if (danglingArtifactIds.has(r.artifactId)) {
|
|
472
|
+
record.warning = `artifact_id "${r.artifactId}" does not exist in pi_artifacts - activation is orphaned`;
|
|
473
|
+
}
|
|
474
|
+
return record;
|
|
475
|
+
});
|
|
278
476
|
if (opts.json) {
|
|
279
|
-
// Strict JSON mode: exactly one parseable JSON object on stdout
|
|
280
|
-
|
|
477
|
+
// Strict JSON mode: exactly one parseable JSON object on stdout.
|
|
478
|
+
// When dangling references are detected, emit degraded status with
|
|
479
|
+
// reason + nextAction so operators can act on the corruption (rc-9,
|
|
480
|
+
// cli-6-output-next-action).
|
|
481
|
+
const payload = { activations: annotated };
|
|
482
|
+
if (hasDangling) {
|
|
483
|
+
payload.status = 'degraded';
|
|
484
|
+
payload.reason = `${danglingArtifactIds.size} activation(s) reference non-existent artifact_id(s): ${Array.from(danglingArtifactIds).join(', ')}`;
|
|
485
|
+
payload.nextAction = 'Run `pd runtime internalization integrity` for full chain diagnostics. Consider deactivating orphaned activations via `pd runtime activation deactivate` or restoring the missing artifacts.';
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
payload.status = 'ok';
|
|
489
|
+
}
|
|
490
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
281
491
|
}
|
|
282
492
|
else {
|
|
283
|
-
if (
|
|
493
|
+
if (annotated.length === 0) {
|
|
284
494
|
console.log('No active activations found.');
|
|
285
495
|
}
|
|
286
496
|
else {
|
|
287
|
-
for (const r of
|
|
288
|
-
|
|
289
|
-
|
|
497
|
+
for (const r of annotated) {
|
|
498
|
+
// PRI-491: Text output shows mode/status/contextVersion so the owner
|
|
499
|
+
// can tell at a glance whether a rule will block now.
|
|
500
|
+
const statusLabel = r.deactivatedAt
|
|
501
|
+
? `[DEACTIVATED ${r.deactivatedAt}]`
|
|
502
|
+
: r.status === 'suspended_by_flag'
|
|
503
|
+
? `[SUSPENDED by flag]`
|
|
504
|
+
: `[ACTIVE]`;
|
|
505
|
+
const modeLabel = r.mode ? ` (${r.mode})` : '';
|
|
506
|
+
console.log(`${statusLabel}${modeLabel} ${r.activationId}`);
|
|
507
|
+
console.log(` principleId: ${r.principleId ?? 'unlinked'}`);
|
|
290
508
|
console.log(` artifactId: ${r.artifactId}`);
|
|
291
509
|
console.log(` channel: ${r.channel}`);
|
|
292
510
|
console.log(` action: ${r.action}`);
|
|
293
511
|
console.log(` targetRef: ${r.targetRef}`);
|
|
294
512
|
console.log(` activatedAt: ${r.activatedAt}`);
|
|
513
|
+
if (r.promotedAt) {
|
|
514
|
+
console.log(` promotedAt: ${r.promotedAt}`);
|
|
515
|
+
}
|
|
516
|
+
if (r.contextVersion) {
|
|
517
|
+
console.log(` contextVersion: ${r.contextVersion}`);
|
|
518
|
+
}
|
|
519
|
+
if (r.evidenceSummary) {
|
|
520
|
+
console.log(` evidence: ${r.evidenceSummary}`);
|
|
521
|
+
}
|
|
522
|
+
if (r.nextAction) {
|
|
523
|
+
console.log(` nextAction: ${r.nextAction}`);
|
|
524
|
+
}
|
|
525
|
+
if (r.warning) {
|
|
526
|
+
console.log(` WARNING: ${r.warning}`);
|
|
527
|
+
}
|
|
295
528
|
console.log('');
|
|
296
529
|
}
|
|
530
|
+
if (hasDangling) {
|
|
531
|
+
console.error(`Warning: ${danglingArtifactIds.size} activation(s) reference non-existent artifact_id(s).`);
|
|
532
|
+
console.error(`Next action: Run \`pd runtime internalization integrity\` for full chain diagnostics. Consider deactivating orphaned activations or restoring missing artifacts.`);
|
|
533
|
+
}
|
|
297
534
|
}
|
|
298
535
|
}
|
|
299
536
|
}
|
|
@@ -536,7 +773,7 @@ export async function handleRuntimeActivationEdit(opts) {
|
|
|
536
773
|
console.log(` previousArtifactId: ${result.previousArtifactId}`);
|
|
537
774
|
}
|
|
538
775
|
console.log(` editedAt: ${result.editedAt}`);
|
|
539
|
-
console.log('Next action: review the new artifact, then approve
|
|
776
|
+
console.log('Next action: review the new artifact, then run `pd activation approve --approval-id <id>` to approve and dispatch');
|
|
540
777
|
}
|
|
541
778
|
}
|
|
542
779
|
catch (err) {
|
|
@@ -561,4 +798,323 @@ export async function handleRuntimeActivationEdit(opts) {
|
|
|
561
798
|
await stateManager.close();
|
|
562
799
|
}
|
|
563
800
|
}
|
|
801
|
+
export async function handleActivationApprove(opts) {
|
|
802
|
+
if (!opts.approvalId) {
|
|
803
|
+
const result = {
|
|
804
|
+
ok: false,
|
|
805
|
+
approvalId: '',
|
|
806
|
+
reason: 'approval_id_required',
|
|
807
|
+
nextAction: 'Provide --approval-id <id>. Run `pd approval list` to see pending approvals.',
|
|
808
|
+
};
|
|
809
|
+
if (opts.json) {
|
|
810
|
+
console.log(JSON.stringify(result, null, 2));
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
console.error('Error: --approval-id is required');
|
|
814
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
815
|
+
}
|
|
816
|
+
process.exitCode = 1;
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const decidedBy = opts.decidedBy ?? 'cli-operator';
|
|
820
|
+
// CodeRabbit review fix (cli-1-strict-json): declare stateManager outside
|
|
821
|
+
// try so the finally block can close it, but resolve workspace INSIDE try
|
|
822
|
+
// so resolveWorkspaceDir() failures are caught and routed through --json.
|
|
823
|
+
let stateManager = null;
|
|
824
|
+
try {
|
|
825
|
+
const workspaceDir = opts.workspace ? path.resolve(opts.workspace) : resolveWorkspaceDir();
|
|
826
|
+
stateManager = new RuntimeStateManager({ workspaceDir });
|
|
827
|
+
await stateManager.initialize();
|
|
828
|
+
const sqliteConn = stateManager.connection;
|
|
829
|
+
const approvalQueueStore = new SqliteApprovalQueueStore(sqliteConn);
|
|
830
|
+
const queue = new ApprovalQueue(approvalQueueStore);
|
|
831
|
+
// Step 1: approve the pending approval record.
|
|
832
|
+
let approvalResult;
|
|
833
|
+
try {
|
|
834
|
+
approvalResult = await queue.approve(opts.approvalId, decidedBy, opts.note);
|
|
835
|
+
}
|
|
836
|
+
catch (err) {
|
|
837
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
838
|
+
const result = {
|
|
839
|
+
ok: false,
|
|
840
|
+
approvalId: opts.approvalId,
|
|
841
|
+
reason: `approve_write_failed: ${errMsg}`,
|
|
842
|
+
nextAction: 'Check workspace DB integrity. Try `pd runtime diagnostics`.',
|
|
843
|
+
};
|
|
844
|
+
if (opts.json) {
|
|
845
|
+
console.log(JSON.stringify(result, null, 2));
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
console.error(`Error: ${result.reason}`);
|
|
849
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
850
|
+
}
|
|
851
|
+
process.exitCode = 1;
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (!approvalResult.ok) {
|
|
855
|
+
// Map ApprovalDecisionResult errors to ApproveResult (rc-3 fail-loud).
|
|
856
|
+
// ApprovalDecisionResult.error is the discriminated union 'not_found' | 'already_decided';
|
|
857
|
+
// both branches are exhaustive, so no third case is reachable at runtime.
|
|
858
|
+
const reason = approvalResult.error === 'not_found'
|
|
859
|
+
? 'approval_not_found'
|
|
860
|
+
: `already_decided: status is ${approvalResult.status ?? 'unknown'}`;
|
|
861
|
+
const nextAction = approvalResult.error === 'not_found'
|
|
862
|
+
? 'Check the approval ID. Run `pd approval list` to see pending approvals.'
|
|
863
|
+
: 'Only pending approvals can be approved. The approval is already decided.';
|
|
864
|
+
const result = {
|
|
865
|
+
ok: false,
|
|
866
|
+
approvalId: opts.approvalId,
|
|
867
|
+
reason,
|
|
868
|
+
nextAction,
|
|
869
|
+
};
|
|
870
|
+
if (opts.json) {
|
|
871
|
+
console.log(JSON.stringify(result, null, 2));
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
console.error(`Error: ${result.reason}`);
|
|
875
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
876
|
+
}
|
|
877
|
+
process.exitCode = 1;
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
// Step 2: dispatch activation via ApprovalCompletionService (handles
|
|
881
|
+
// idempotency, feature flag, and rollback on failure).
|
|
882
|
+
const activationStateStore = new SqliteActivationStateStore(sqliteConn);
|
|
883
|
+
const piArtifactStore = new SqlitePIArtifactStore(sqliteConn);
|
|
884
|
+
const artifactReadModel = {
|
|
885
|
+
getArtifactById: async (id) => {
|
|
886
|
+
const rec = await piArtifactStore.getArtifactById(id);
|
|
887
|
+
return rec ? toSnapshot(rec) : null;
|
|
888
|
+
},
|
|
889
|
+
};
|
|
890
|
+
// PRI-489: inject the real workspace `rulecode_context_v2` feature flag
|
|
891
|
+
// probe into the approve path's RuleHostWriter — same wiring as the
|
|
892
|
+
// dispatch path (handleRuntimeActivationDispatch) and the Console model
|
|
893
|
+
// (ApprovalsConsoleModel.dispatchActivationAfterApproval). Previously
|
|
894
|
+
// this path constructed RuleHostWriter without the probe, so a v2
|
|
895
|
+
// artifact in a flag-off workspace would pass canActivate here while
|
|
896
|
+
// being rejected by dispatch/Console — an inconsistent contract that
|
|
897
|
+
// violated ERR-024 (validator wired in one enforcement path but not
|
|
898
|
+
// another) and ERR-089 (sibling approval path diverged from dispatch).
|
|
899
|
+
const featureFlags = computeFlagsFromLoadResult(loadPdConfig(workspaceDir));
|
|
900
|
+
const dispatcher = new ActivationDispatcher(artifactReadModel, activationStateStore, {
|
|
901
|
+
writers: [
|
|
902
|
+
new PromptWriter(),
|
|
903
|
+
new RuleHostWriter({
|
|
904
|
+
gateDeps: createProductionGateDeps(),
|
|
905
|
+
featureFlagProbe: (flagId) => featureFlags.flags[flagId]?.enabled === true,
|
|
906
|
+
}),
|
|
907
|
+
new DeferArchiveWriter(),
|
|
908
|
+
],
|
|
909
|
+
approvalQueueStore,
|
|
910
|
+
});
|
|
911
|
+
const completionService = new ApprovalCompletionService(approvalQueueStore, dispatcher, activationStateStore);
|
|
912
|
+
let completionResult;
|
|
913
|
+
try {
|
|
914
|
+
completionResult = await completionService.completeApproval({
|
|
915
|
+
approvalId: opts.approvalId,
|
|
916
|
+
actor: { kind: 'human', userId: decidedBy },
|
|
917
|
+
now: new Date().toISOString(),
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
catch (err) {
|
|
921
|
+
// CodeRabbit review fix (cli-5-failure-no-mutation): approval was
|
|
922
|
+
// written but completion threw unexpectedly. Roll back the approval to
|
|
923
|
+
// 'pending' so the operator can retry without a stale 'approved' record
|
|
924
|
+
// that has no activation. Mirrors ApprovalsConsoleModel.approve() L168-182.
|
|
925
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
926
|
+
let approvalRolledBack = false;
|
|
927
|
+
try {
|
|
928
|
+
const rollbackResult = await queue.resetToPending(opts.approvalId);
|
|
929
|
+
approvalRolledBack = rollbackResult.ok;
|
|
930
|
+
}
|
|
931
|
+
catch { /* best-effort rollback */ }
|
|
932
|
+
const result = {
|
|
933
|
+
ok: false,
|
|
934
|
+
approvalId: opts.approvalId,
|
|
935
|
+
reason: `activation_completion_failed: ${errMsg}`,
|
|
936
|
+
nextAction: approvalRolledBack
|
|
937
|
+
? 'Approval rolled back to pending. Fix the issue and re-run `pd activation approve --approval-id <id>`.'
|
|
938
|
+
: `Approval remains 'approved'. Run \`pd activation dispatch --artifact-id ${approvalResult.record.artifactId} --confirm\` to retry activation.`,
|
|
939
|
+
approvalRolledBack,
|
|
940
|
+
};
|
|
941
|
+
if (opts.json) {
|
|
942
|
+
console.log(JSON.stringify(result, null, 2));
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
console.error(`Error: ${result.reason}`);
|
|
946
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
947
|
+
}
|
|
948
|
+
process.exitCode = 1;
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (!completionResult.ok) {
|
|
952
|
+
// CodeRabbit review fix (cli-5-failure-no-mutation): activation
|
|
953
|
+
// returned !ok. Roll back the approval to 'pending' so the operator
|
|
954
|
+
// can retry without a stale 'approved' record. Mirrors
|
|
955
|
+
// ApprovalsConsoleModel.approve() L168-182.
|
|
956
|
+
let approvalRolledBack = false;
|
|
957
|
+
try {
|
|
958
|
+
const rollbackResult = await queue.resetToPending(opts.approvalId);
|
|
959
|
+
approvalRolledBack = rollbackResult.ok;
|
|
960
|
+
}
|
|
961
|
+
catch { /* best-effort rollback */ }
|
|
962
|
+
const result = {
|
|
963
|
+
ok: false,
|
|
964
|
+
approvalId: opts.approvalId,
|
|
965
|
+
reason: `activation_failed: ${completionResult.reason}`,
|
|
966
|
+
nextAction: approvalRolledBack
|
|
967
|
+
? `Approval rolled back to pending. ${completionResult.nextAction ?? 'Fix the issue and re-run `pd activation approve --approval-id <id>`.'}`
|
|
968
|
+
: `Approval remains 'approved'. ${completionResult.nextAction ?? 'Check the artifact validation status and retry.'}`,
|
|
969
|
+
approvalRolledBack,
|
|
970
|
+
};
|
|
971
|
+
if (opts.json) {
|
|
972
|
+
console.log(JSON.stringify(result, null, 2));
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
console.error(`Error: ${result.reason}`);
|
|
976
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
977
|
+
}
|
|
978
|
+
process.exitCode = 1;
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
// Step 3: success — surface activation result.
|
|
982
|
+
const result = {
|
|
983
|
+
ok: true,
|
|
984
|
+
approvalId: opts.approvalId,
|
|
985
|
+
activationId: completionResult.activationId,
|
|
986
|
+
decision: completionResult.decision.decision,
|
|
987
|
+
nextAction: 'pd activation list',
|
|
988
|
+
};
|
|
989
|
+
if (opts.json) {
|
|
990
|
+
console.log(JSON.stringify(result, null, 2));
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
console.log(`Approval approved: ${result.approvalId}`);
|
|
994
|
+
console.log(` activationId: ${result.activationId ?? 'N/A'}`);
|
|
995
|
+
console.log(` decision: ${result.decision}`);
|
|
996
|
+
console.log(`Next action: ${result.nextAction}`);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
catch (err) {
|
|
1000
|
+
// P2 #5: initialize/DB exceptions must not break --json contract.
|
|
1001
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1002
|
+
const result = {
|
|
1003
|
+
ok: false,
|
|
1004
|
+
approvalId: opts.approvalId ?? '',
|
|
1005
|
+
reason: `initialize_failed: ${errMsg}`,
|
|
1006
|
+
nextAction: 'Check workspace directory and DB integrity. Try `pd runtime diagnostics`.',
|
|
1007
|
+
};
|
|
1008
|
+
if (opts.json) {
|
|
1009
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.error(`Error: ${result.reason}`);
|
|
1013
|
+
console.error(`Next action: ${result.nextAction}`);
|
|
1014
|
+
}
|
|
1015
|
+
process.exitCode = 1;
|
|
1016
|
+
}
|
|
1017
|
+
finally {
|
|
1018
|
+
// stateManager may be null if resolveWorkspaceDir() threw before assignment.
|
|
1019
|
+
await stateManager?.close();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
// ── Commander Wiring (CLI gate rule 7: test the real command wiring) ─────────
|
|
1023
|
+
//
|
|
1024
|
+
// Extracted so parser-level tests can exercise the actual flag registration
|
|
1025
|
+
// without importing all of `index.ts`. Mirrors `registerRunRuleHostCommand`.
|
|
1026
|
+
export function registerRuntimeActivationPromoteCommand(parent) {
|
|
1027
|
+
return parent
|
|
1028
|
+
.command('promote')
|
|
1029
|
+
.description('Promote a code_tool_hook activation from shadow observation to live blocking')
|
|
1030
|
+
.requiredOption('--activation-id <id>', 'Shadow activation ID to promote')
|
|
1031
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1032
|
+
.option('--dry-run', 'Validate eligibility without changing activation state')
|
|
1033
|
+
.option('--confirm', 'Confirm promotion to live blocking')
|
|
1034
|
+
.option('--json', 'Output raw JSON')
|
|
1035
|
+
.action(async (opts) => {
|
|
1036
|
+
await handleRuntimeActivationPromote({
|
|
1037
|
+
activationId: opts.activationId,
|
|
1038
|
+
workspace: opts.workspace,
|
|
1039
|
+
dryRun: opts.dryRun,
|
|
1040
|
+
confirm: opts.confirm,
|
|
1041
|
+
json: opts.json,
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
// PRI-493: Extracted helpers for parser-level flag-wiring tests (cli-7).
|
|
1046
|
+
// Mirrors registerRuntimeActivationPromoteCommand — single source of truth
|
|
1047
|
+
// shared with index.ts. Flag typos in production surface at parseAsync time.
|
|
1048
|
+
export function registerRuntimeActivationDeactivateCommand(parent) {
|
|
1049
|
+
return parent
|
|
1050
|
+
.command('deactivate')
|
|
1051
|
+
.description('Deactivate an activation by activation ID')
|
|
1052
|
+
.requiredOption('--activation-id <id>', 'Activation ID to deactivate')
|
|
1053
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1054
|
+
.option('--json', 'Output raw JSON')
|
|
1055
|
+
.action(async (opts) => {
|
|
1056
|
+
await handleRuntimeActivationDeactivate({
|
|
1057
|
+
activationId: opts.activationId,
|
|
1058
|
+
workspace: opts.workspace,
|
|
1059
|
+
json: opts.json,
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
export function registerRuntimeActivationListCommand(parent) {
|
|
1064
|
+
return parent
|
|
1065
|
+
.command('list')
|
|
1066
|
+
.description('List all activations for a workspace')
|
|
1067
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1068
|
+
.option('-c, --channel <channel>', 'Filter by channel (prompt|code_tool_hook)')
|
|
1069
|
+
.option('--include-deactivated', 'Include deactivated records in output')
|
|
1070
|
+
.option('--json', 'Output raw JSON')
|
|
1071
|
+
.action(async (opts) => {
|
|
1072
|
+
await handleRuntimeActivationList({
|
|
1073
|
+
workspace: opts.workspace,
|
|
1074
|
+
channel: opts.channel,
|
|
1075
|
+
includeDeactivated: opts.includeDeactivated,
|
|
1076
|
+
json: opts.json,
|
|
1077
|
+
});
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
export function registerRuntimeActivationDispatchCommand(parent) {
|
|
1081
|
+
return parent
|
|
1082
|
+
.command('dispatch')
|
|
1083
|
+
.description('Dispatch an activation for a rollout-reviewed artifact')
|
|
1084
|
+
.option('-a, --artifact-id <id>', 'PIArtifact ID to activate')
|
|
1085
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1086
|
+
.option('-c, --channel <channel>', 'Activation channel (prompt|defer_archive)', 'prompt')
|
|
1087
|
+
.option('--dry-run', 'Dry-run mode (default, no writes)')
|
|
1088
|
+
.option('--confirm', 'Confirm and write activation record')
|
|
1089
|
+
.option('--json', 'Output raw JSON')
|
|
1090
|
+
.action(async (opts) => {
|
|
1091
|
+
await handleRuntimeActivationDispatch({
|
|
1092
|
+
workspace: opts.workspace,
|
|
1093
|
+
artifactId: opts.artifactId,
|
|
1094
|
+
channel: opts.channel,
|
|
1095
|
+
dryRun: opts.dryRun,
|
|
1096
|
+
confirm: opts.confirm,
|
|
1097
|
+
json: opts.json,
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
export function registerRuntimeActivationApproveCommand(parent) {
|
|
1102
|
+
return parent
|
|
1103
|
+
.command('approve')
|
|
1104
|
+
.description('Approve a pending approval and dispatch its activation')
|
|
1105
|
+
.requiredOption('-a, --approval-id <id>', 'Approval ID to approve')
|
|
1106
|
+
.option('--decided-by <user>', 'Reviewer name (default: cli-operator)')
|
|
1107
|
+
.option('--note <text>', 'Optional approval note')
|
|
1108
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1109
|
+
.option('--json', 'Output raw JSON')
|
|
1110
|
+
.action(async (opts) => {
|
|
1111
|
+
await handleActivationApprove({
|
|
1112
|
+
approvalId: opts.approvalId,
|
|
1113
|
+
decidedBy: opts.decidedBy,
|
|
1114
|
+
note: opts.note,
|
|
1115
|
+
workspace: opts.workspace,
|
|
1116
|
+
json: opts.json,
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
564
1120
|
//# sourceMappingURL=runtime-activation.js.map
|