@hongmaple0820/scale-engine 0.48.0 → 0.50.1
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/README.en.md +2 -2
- package/README.md +2 -2
- package/dist/agents/evidenceDiscipline.d.ts +7 -0
- package/dist/agents/evidenceDiscipline.js +21 -0
- package/dist/agents/evidenceDiscipline.js.map +1 -0
- package/dist/agents/profiles.js +8 -1
- package/dist/agents/profiles.js.map +1 -1
- package/dist/agents/types.d.ts +1 -0
- package/dist/api/DashboardHttpConfig.d.ts +28 -0
- package/dist/api/DashboardHttpConfig.js +110 -0
- package/dist/api/DashboardHttpConfig.js.map +1 -0
- package/dist/api/cli.js +102 -11
- package/dist/api/cli.js.map +1 -1
- package/dist/api/http.d.ts +1 -0
- package/dist/api/http.js +50 -0
- package/dist/api/http.js.map +1 -0
- package/dist/artifact/types.d.ts +64 -0
- package/dist/artifact/types.js.map +1 -1
- package/dist/bootstrap/DependencyBootstrap.d.ts +1 -0
- package/dist/bootstrap/DependencyBootstrap.js +14 -3
- package/dist/bootstrap/DependencyBootstrap.js.map +1 -1
- package/dist/cli/cortexApplyCommand.d.ts +26 -0
- package/dist/cli/cortexApplyCommand.js +74 -0
- package/dist/cli/cortexApplyCommand.js.map +1 -0
- package/dist/cli/cortexCandidateCommands.d.ts +42 -0
- package/dist/cli/cortexCandidateCommands.js +119 -0
- package/dist/cli/cortexCandidateCommands.js.map +1 -0
- package/dist/cli/cortexCommands.d.ts +51 -0
- package/dist/cli/cortexCommands.js +127 -13
- package/dist/cli/cortexCommands.js.map +1 -1
- package/dist/cli/engineBootstrap.d.ts +1 -1
- package/dist/cli/engineBootstrap.js +2 -0
- package/dist/cli/engineBootstrap.js.map +1 -1
- package/dist/cli/evalCommands.js +13 -1
- package/dist/cli/evalCommands.js.map +1 -1
- package/dist/cli/phaseCommands.d.ts +81 -1
- package/dist/cli/phaseCommands.js +465 -31
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/cli/runtimeSkillCommands.js +12 -2
- package/dist/cli/runtimeSkillCommands.js.map +1 -1
- package/dist/cli/shieldCommands.d.ts +1 -0
- package/dist/cli/shieldCommands.js +20 -7
- package/dist/cli/shieldCommands.js.map +1 -1
- package/dist/cli/workflowEvidenceCommands.d.ts +120 -0
- package/dist/cli/workflowEvidenceCommands.js +228 -2
- package/dist/cli/workflowEvidenceCommands.js.map +1 -1
- package/dist/cortex/AutoFixEventObservations.d.ts +11 -0
- package/dist/cortex/AutoFixEventObservations.js +72 -0
- package/dist/cortex/AutoFixEventObservations.js.map +1 -0
- package/dist/cortex/GateEvidenceObservations.d.ts +22 -0
- package/dist/cortex/GateEvidenceObservations.js +179 -0
- package/dist/cortex/GateEvidenceObservations.js.map +1 -0
- package/dist/cortex/GovernanceMetrics.d.ts +2 -0
- package/dist/cortex/GovernanceMetrics.js +112 -22
- package/dist/cortex/GovernanceMetrics.js.map +1 -1
- package/dist/cortex/InstinctApplicationRecorder.d.ts +28 -0
- package/dist/cortex/InstinctApplicationRecorder.js +145 -0
- package/dist/cortex/InstinctApplicationRecorder.js.map +1 -0
- package/dist/cortex/InstinctCandidateAudit.d.ts +3 -0
- package/dist/cortex/InstinctCandidateAudit.js +39 -0
- package/dist/cortex/InstinctCandidateAudit.js.map +1 -0
- package/dist/cortex/InstinctCandidateReview.d.ts +32 -0
- package/dist/cortex/InstinctCandidateReview.js +125 -0
- package/dist/cortex/InstinctCandidateReview.js.map +1 -0
- package/dist/cortex/InstinctExtractor.d.ts +1 -0
- package/dist/cortex/InstinctExtractor.js +24 -17
- package/dist/cortex/InstinctExtractor.js.map +1 -1
- package/dist/cortex/InstinctRuntimeEvidence.d.ts +14 -0
- package/dist/cortex/InstinctRuntimeEvidence.js +120 -0
- package/dist/cortex/InstinctRuntimeEvidence.js.map +1 -0
- package/dist/cortex/InstinctStore.d.ts +50 -4
- package/dist/cortex/InstinctStore.js +262 -48
- package/dist/cortex/InstinctStore.js.map +1 -1
- package/dist/cortex/InstinctValidation.d.ts +9 -0
- package/dist/cortex/InstinctValidation.js +55 -0
- package/dist/cortex/InstinctValidation.js.map +1 -0
- package/dist/cortex/SessionInjector.d.ts +1 -0
- package/dist/cortex/SessionInjector.js +28 -8
- package/dist/cortex/SessionInjector.js.map +1 -1
- package/dist/dashboard/DashboardServer.d.ts +79 -0
- package/dist/dashboard/DashboardServer.js +330 -6
- package/dist/dashboard/DashboardServer.js.map +1 -1
- package/dist/dashboard/spa/app.js +515 -0
- package/dist/dashboard/spa/components/DataTable.js +53 -0
- package/dist/dashboard/spa/components/EventStream.js +66 -0
- package/dist/dashboard/spa/components/LoadingState.js +39 -0
- package/dist/dashboard/spa/components/MetricCard.js +30 -0
- package/dist/dashboard/spa/components/Panel.js +27 -0
- package/dist/dashboard/spa/components/StatusBadge.js +51 -0
- package/dist/dashboard/spa/i18n.js +767 -0
- package/dist/dashboard/spa/index.html +463 -0
- package/dist/dashboard/spa/pages/costs.js +522 -0
- package/dist/dashboard/spa/pages/documents.js +540 -0
- package/dist/dashboard/spa/pages/knowledge.js +457 -0
- package/dist/dashboard/spa/pages/monitoring.js +361 -0
- package/dist/dashboard/spa/pages/overview.js +301 -0
- package/dist/dashboard/spa/pages/topology-renderers.js +251 -0
- package/dist/dashboard/spa/pages/topology.js +370 -0
- package/dist/dashboard/spa/pages/workflow-renderers.js +239 -0
- package/dist/dashboard/spa/pages/workflow.js +217 -0
- package/dist/env/EnvironmentDoctor.js +12 -7
- package/dist/env/EnvironmentDoctor.js.map +1 -1
- package/dist/eval/BenchmarkPublisher.d.ts +2 -0
- package/dist/eval/BenchmarkPublisher.js +43 -0
- package/dist/eval/BenchmarkPublisher.js.map +1 -1
- package/dist/eval/WorkflowEval.d.ts +9 -0
- package/dist/eval/WorkflowEval.js +348 -2
- package/dist/eval/WorkflowEval.js.map +1 -1
- package/dist/guardrails/ast/confirmers.d.ts +18 -0
- package/dist/guardrails/ast/confirmers.js +69 -0
- package/dist/guardrails/ast/confirmers.js.map +1 -0
- package/dist/guardrails/ast/parse.d.ts +20 -0
- package/dist/guardrails/ast/parse.js +51 -0
- package/dist/guardrails/ast/parse.js.map +1 -0
- package/dist/memory/MemoryBrain.d.ts +13 -0
- package/dist/memory/MemoryBrain.js +47 -0
- package/dist/memory/MemoryBrain.js.map +1 -1
- package/dist/memory/MemoryFabric.d.ts +1 -0
- package/dist/memory/MemoryFabric.js +12 -8
- package/dist/memory/MemoryFabric.js.map +1 -1
- package/dist/memory/MemoryLearning.d.ts +1 -0
- package/dist/memory/MemoryLearning.js +6 -3
- package/dist/memory/MemoryLearning.js.map +1 -1
- package/dist/memory/MemoryProviders.d.ts +8 -1
- package/dist/memory/MemoryProviders.js +143 -29
- package/dist/memory/MemoryProviders.js.map +1 -1
- package/dist/output/HTMLDocumentRenderer.d.ts +9 -0
- package/dist/output/HTMLDocumentRenderer.js +19 -0
- package/dist/output/HTMLDocumentRenderer.js.map +1 -1
- package/dist/review/FreshContextVerifier.d.ts +35 -0
- package/dist/review/FreshContextVerifier.js +120 -0
- package/dist/review/FreshContextVerifier.js.map +1 -0
- package/dist/review/JsonLlmClient.d.ts +37 -0
- package/dist/review/JsonLlmClient.js +94 -0
- package/dist/review/JsonLlmClient.js.map +1 -0
- package/dist/review/LlmJudge.d.ts +61 -0
- package/dist/review/LlmJudge.js +167 -0
- package/dist/review/LlmJudge.js.map +1 -0
- package/dist/runtime/AiOsRuntime.d.ts +14 -1
- package/dist/runtime/AiOsRuntime.js +59 -3
- package/dist/runtime/AiOsRuntime.js.map +1 -1
- package/dist/runtime/RuntimeDoctor.js +3 -1
- package/dist/runtime/RuntimeDoctor.js.map +1 -1
- package/dist/runtime/RuntimeEvidenceLedger.d.ts +6 -0
- package/dist/runtime/RuntimeEvidenceLedger.js +52 -1
- package/dist/runtime/RuntimeEvidenceLedger.js.map +1 -1
- package/dist/runtime/SessionLedger.d.ts +2 -0
- package/dist/runtime/SessionLedger.js +4 -0
- package/dist/runtime/SessionLedger.js.map +1 -1
- package/dist/setup/SetupVerification.js +53 -5
- package/dist/setup/SetupVerification.js.map +1 -1
- package/dist/shield/PolicyCompiler.js +73 -12
- package/dist/shield/PolicyCompiler.js.map +1 -1
- package/dist/shield/ProtectedPaths.js +4 -2
- package/dist/shield/ProtectedPaths.js.map +1 -1
- package/dist/skills/SkillCatalog.d.ts +2 -0
- package/dist/skills/SkillCatalog.js +8 -0
- package/dist/skills/SkillCatalog.js.map +1 -1
- package/dist/skills/SkillDoctor.d.ts +19 -2
- package/dist/skills/SkillDoctor.js +163 -13
- package/dist/skills/SkillDoctor.js.map +1 -1
- package/dist/tools/SafeCommandRunner.d.ts +1 -0
- package/dist/tools/SafeCommandRunner.js +1 -0
- package/dist/tools/SafeCommandRunner.js.map +1 -1
- package/dist/tools/ToolCapabilityRegistry.js +25 -3
- package/dist/tools/ToolCapabilityRegistry.js.map +1 -1
- package/dist/tools/ToolOrchestrator.js +21 -0
- package/dist/tools/ToolOrchestrator.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workflow/AgentLoopReadiness.d.ts +103 -0
- package/dist/workflow/AgentLoopReadiness.js +371 -0
- package/dist/workflow/AgentLoopReadiness.js.map +1 -0
- package/dist/workflow/BoundaryEnforcement.d.ts +60 -0
- package/dist/workflow/BoundaryEnforcement.js +182 -0
- package/dist/workflow/BoundaryEnforcement.js.map +1 -0
- package/dist/workflow/EcosystemReadinessGate.d.ts +46 -0
- package/dist/workflow/EcosystemReadinessGate.js +126 -0
- package/dist/workflow/EcosystemReadinessGate.js.map +1 -0
- package/dist/workflow/EngineeringStandards.js +67 -12
- package/dist/workflow/EngineeringStandards.js.map +1 -1
- package/dist/workflow/GateCatalog.js +21 -2
- package/dist/workflow/GateCatalog.js.map +1 -1
- package/dist/workflow/GovernanceTemplatePacks.js +2 -26
- package/dist/workflow/GovernanceTemplatePacks.js.map +1 -1
- package/dist/workflow/GovernanceTemplates.js +8 -1
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/ProfileEnforcement.d.ts +7 -0
- package/dist/workflow/ProfileEnforcement.js +12 -0
- package/dist/workflow/ProfileEnforcement.js.map +1 -0
- package/dist/workflow/ReleaseDeploymentLedger.d.ts +63 -0
- package/dist/workflow/ReleaseDeploymentLedger.js +154 -0
- package/dist/workflow/ReleaseDeploymentLedger.js.map +1 -0
- package/dist/workflow/ReviewAnalyzer.js +50 -3
- package/dist/workflow/ReviewAnalyzer.js.map +1 -1
- package/dist/workflow/ReviewStore.d.ts +10 -0
- package/dist/workflow/ReviewStore.js.map +1 -1
- package/dist/workflow/SessionPreamble.d.ts +7 -0
- package/dist/workflow/SessionPreamble.js +48 -9
- package/dist/workflow/SessionPreamble.js.map +1 -1
- package/dist/workflow/SurfaceCoverage.d.ts +19 -0
- package/dist/workflow/SurfaceCoverage.js +57 -0
- package/dist/workflow/SurfaceCoverage.js.map +1 -0
- package/dist/workflow/VerificationCommands.d.ts +1 -0
- package/dist/workflow/VerificationCommands.js.map +1 -1
- package/dist/workflow/VerificationProfile.d.ts +5 -0
- package/dist/workflow/VerificationProfile.js +26 -0
- package/dist/workflow/VerificationProfile.js.map +1 -1
- package/dist/workflow/VerificationSchema.d.ts +3 -0
- package/dist/workflow/VerificationSchema.js +6 -0
- package/dist/workflow/VerificationSchema.js.map +1 -1
- package/dist/workflow/WorkflowEffectiveness.d.ts +97 -0
- package/dist/workflow/WorkflowEffectiveness.js +302 -0
- package/dist/workflow/WorkflowEffectiveness.js.map +1 -0
- package/dist/workflow/WorkflowEffectivenessRenderer.d.ts +2 -0
- package/dist/workflow/WorkflowEffectivenessRenderer.js +67 -0
- package/dist/workflow/WorkflowEffectivenessRenderer.js.map +1 -0
- package/dist/workflow/WorkflowEffectivenessScoring.d.ts +6 -0
- package/dist/workflow/WorkflowEffectivenessScoring.js +243 -0
- package/dist/workflow/WorkflowEffectivenessScoring.js.map +1 -0
- package/dist/workflow/gates/EnhancedGates.js +2 -0
- package/dist/workflow/gates/EnhancedGates.js.map +1 -1
- package/dist/workflow/gates/GateSystem.d.ts +16 -0
- package/dist/workflow/gates/GateSystem.js +208 -41
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/gates/MetaGovernanceGates.js +269 -8
- package/dist/workflow/gates/MetaGovernanceGates.js.map +1 -1
- package/dist/workflow/gates/TestIntegrityGate.d.ts +51 -0
- package/dist/workflow/gates/TestIntegrityGate.js +175 -0
- package/dist/workflow/gates/TestIntegrityGate.js.map +1 -0
- package/dist/workflow/types.d.ts +1 -1
- package/docs/guides/DEVELOPMENT_WORKFLOW.md +28 -0
- package/docs/reference/cli.md +2 -1
- package/docs/start/agent-governance-demo.md +1 -1
- package/docs/workflow/E2E_EXAMPLE.md +133 -0
- package/docs/workflow/README.md +7 -1
- package/docs/workflow/TEMPLATE_GUIDE.md +162 -0
- package/docs/workflow/templates/github-actions-scale-preflight.yml +4 -1
- package/docs/workflow/templates/plan.md +26 -0
- package/docs/workflow/templates/spec.md +28 -0
- package/package.json +7 -3
- package/scripts/workflow/run-vitest.mjs +123 -0
|
@@ -18,7 +18,12 @@ import { WorkflowArtifactWriter } from '../workflow/WorkflowArtifactWriter.js';
|
|
|
18
18
|
import { resolveVerificationTargets } from '../workflow/VerificationProfile.js';
|
|
19
19
|
import { EvidenceStore } from '../workflow/EvidenceStore.js';
|
|
20
20
|
import { ReviewStore } from '../workflow/ReviewStore.js';
|
|
21
|
+
import { JudgePromptStore, LlmJudge } from '../review/LlmJudge.js';
|
|
22
|
+
import { JsonLlmClient } from '../review/JsonLlmClient.js';
|
|
23
|
+
import { FreshContextVerifier } from '../review/FreshContextVerifier.js';
|
|
21
24
|
import { TaskMetricsStore } from '../workflow/TaskMetricsStore.js';
|
|
25
|
+
import { ReleaseDeploymentLedger } from '../workflow/ReleaseDeploymentLedger.js';
|
|
26
|
+
import { recordRuntimeInstinctApplications } from '../cortex/InstinctApplicationRecorder.js';
|
|
22
27
|
import { appendVerificationArtifact, checkTaskArtifactCompleteness, scaffoldTaskArtifacts } from '../workflow/TaskArtifactScaffolder.js';
|
|
23
28
|
import { createWorkflowGuidance, renderWorkflowGuidance } from '../workflow/WorkflowGuidance.js';
|
|
24
29
|
import { blockingWorkflowOpenTasks, removeWorkflowOpenTask } from '../workflow/WorkflowOpenTasks.js';
|
|
@@ -33,6 +38,8 @@ import { loadToolPolicy } from '../tools/ToolPolicy.js';
|
|
|
33
38
|
import { runSafeCommand } from '../tools/SafeCommandRunner.js';
|
|
34
39
|
import { join } from 'node:path';
|
|
35
40
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
41
|
+
import { computeSurfaceCoverage, formatSurfaceCoverageWarnings } from '../workflow/SurfaceCoverage.js';
|
|
42
|
+
import { evaluateBoundaries, evaluateConstraints, formatBoundaryWarnings, formatConstraintWarnings, isEnforcedBoundaryProfile, countBoundaryBlockers, } from '../workflow/BoundaryEnforcement.js';
|
|
36
43
|
import { HTMLDocumentRenderer } from '../output/HTMLDocumentRenderer.js';
|
|
37
44
|
import { SCALE_ENGINE_VERSION } from '../version.js';
|
|
38
45
|
import { optimizeCodingPrompt } from '../prompts/PromptOptimizer.js';
|
|
@@ -57,6 +64,7 @@ function validateReviewEvidence(ids) {
|
|
|
57
64
|
const reviewStore = new ReviewStore(SCALE_DIR);
|
|
58
65
|
const missing = [];
|
|
59
66
|
const failed = [];
|
|
67
|
+
const records = [];
|
|
60
68
|
for (const id of ids ?? []) {
|
|
61
69
|
const record = reviewStore.getReview(id);
|
|
62
70
|
if (!record) {
|
|
@@ -64,9 +72,14 @@ function validateReviewEvidence(ids) {
|
|
|
64
72
|
}
|
|
65
73
|
else if (!record.passed) {
|
|
66
74
|
failed.push(id);
|
|
75
|
+
records.push(record);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
records.push(record);
|
|
67
79
|
}
|
|
68
80
|
}
|
|
69
|
-
|
|
81
|
+
const latest = records.sort((a, b) => b.createdAt - a.createdAt)[0];
|
|
82
|
+
return { ok: (ids?.length ?? 0) > 0 && missing.length === 0 && latest?.passed === true, missing, failed };
|
|
70
83
|
}
|
|
71
84
|
function getValidatedReviewRecords(ids) {
|
|
72
85
|
const reviewStore = new ReviewStore(SCALE_DIR);
|
|
@@ -321,12 +334,56 @@ async function recordVerificationMetric(options) {
|
|
|
321
334
|
metricsStore.writeMarkdownReport(PROJECT_DIR);
|
|
322
335
|
return record;
|
|
323
336
|
}
|
|
337
|
+
async function recordShipDeployment(options) {
|
|
338
|
+
const version = optionalString(options.args['deploy-version']) ?? readPackageVersion(PROJECT_DIR);
|
|
339
|
+
const completedAt = optionalString(options.args['deployed-at']);
|
|
340
|
+
const commitTimestamp = await resolveCommitTimestamp(options.commitHash);
|
|
341
|
+
const notes = [
|
|
342
|
+
`task=${options.taskId}`,
|
|
343
|
+
options.taskTitle ? `title=${options.taskTitle}` : undefined,
|
|
344
|
+
options.taskPayload.verificationEvidenceIds?.length ? `verificationEvidence=${options.taskPayload.verificationEvidenceIds.join(',')}` : undefined,
|
|
345
|
+
options.taskPayload.reviewEvidenceIds?.length ? `reviewEvidence=${options.taskPayload.reviewEvidenceIds.join(',')}` : undefined,
|
|
346
|
+
].filter((value) => Boolean(value)).join('; ');
|
|
347
|
+
return new ReleaseDeploymentLedger(SCALE_DIR).record({
|
|
348
|
+
service: optionalString(options.args['deploy-service']) ?? 'scale-engine',
|
|
349
|
+
environment: optionalString(options.args['deploy-environment']) ?? 'production',
|
|
350
|
+
status: 'succeeded',
|
|
351
|
+
version,
|
|
352
|
+
commitSha: options.commitHash,
|
|
353
|
+
commitTimestamp,
|
|
354
|
+
completedAt,
|
|
355
|
+
source: 'ship',
|
|
356
|
+
notes,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function optionalString(value) {
|
|
360
|
+
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
361
|
+
return normalized ? normalized : undefined;
|
|
362
|
+
}
|
|
363
|
+
function readPackageVersion(projectDir) {
|
|
364
|
+
const pkgPath = join(projectDir, 'package.json');
|
|
365
|
+
if (!existsSync(pkgPath))
|
|
366
|
+
return undefined;
|
|
367
|
+
try {
|
|
368
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
369
|
+
return optionalString(pkg.version);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async function resolveCommitTimestamp(commitHash) {
|
|
376
|
+
const result = await runGit(['show', '-s', '--format=%cI', commitHash]);
|
|
377
|
+
if (result.exitCode !== 0)
|
|
378
|
+
return undefined;
|
|
379
|
+
return optionalString(result.stdout);
|
|
380
|
+
}
|
|
324
381
|
// Helper: Generate spec markdown file
|
|
325
|
-
function generateSpecMarkdown(id, title, payload) {
|
|
382
|
+
function generateSpecMarkdown(id, title, payload, status = 'FROZEN') {
|
|
326
383
|
return `# Spec: ${title}
|
|
327
384
|
|
|
328
385
|
**ID**: ${id}
|
|
329
|
-
**Status**:
|
|
386
|
+
**Status**: ${status}
|
|
330
387
|
**Ambiguity Score**: ${payload.ambiguityScore ?? 0.15}
|
|
331
388
|
|
|
332
389
|
## What
|
|
@@ -343,11 +400,34 @@ ${payload.edgeCases.map(e => `- ${e}`).join('\n') || '(none defined)'}
|
|
|
343
400
|
|
|
344
401
|
## North Star
|
|
345
402
|
${payload.northStar || 'User value delivered'}
|
|
346
|
-
|
|
403
|
+
${renderSpecContractSections(payload)}
|
|
347
404
|
---
|
|
348
405
|
*Generated by SCALE Engine DEFINE phase*
|
|
349
406
|
`;
|
|
350
407
|
}
|
|
408
|
+
// Helper: Render the optional P0 six-element contract sections.
|
|
409
|
+
// Each section is omitted when its field is unset, keeping legacy specs unchanged.
|
|
410
|
+
function renderSpecContractSections(payload) {
|
|
411
|
+
const sections = [];
|
|
412
|
+
if (payload.verificationSurface?.length) {
|
|
413
|
+
sections.push(`\n## Verification Surface\n${payload.verificationSurface.map(s => `- ${s}`).join('\n')}`);
|
|
414
|
+
}
|
|
415
|
+
if (payload.constraints?.length) {
|
|
416
|
+
sections.push(`\n## Constraints\n${payload.constraints.map(c => `- ${c}`).join('\n')}`);
|
|
417
|
+
}
|
|
418
|
+
if (payload.boundaries) {
|
|
419
|
+
const b = payload.boundaries;
|
|
420
|
+
const line = (label, items) => `- ${label}: ${items.length ? items.join(', ') : '(none)'}`;
|
|
421
|
+
sections.push(`\n## Boundaries\n${line('Files', b.files)}\n${line('Tools', b.tools)}\n${line('Forbidden', b.forbidden)}`);
|
|
422
|
+
}
|
|
423
|
+
if (payload.iterationStrategy) {
|
|
424
|
+
sections.push(`\n## Iteration Strategy\n${payload.iterationStrategy}`);
|
|
425
|
+
}
|
|
426
|
+
if (payload.blockedStopCondition) {
|
|
427
|
+
sections.push(`\n## Blocked Stop Condition\n${payload.blockedStopCondition}`);
|
|
428
|
+
}
|
|
429
|
+
return sections.length ? `\n${sections.join('\n')}\n` : '\n';
|
|
430
|
+
}
|
|
351
431
|
// Helper: Calculate ambiguity score
|
|
352
432
|
function calculateAmbiguityScore(description, successCriteria) {
|
|
353
433
|
let score = 0.2; // Base score (maximum threshold)
|
|
@@ -364,9 +444,20 @@ function calculateAmbiguityScore(description, successCriteria) {
|
|
|
364
444
|
export const phaseDefine = defineCommand({
|
|
365
445
|
meta: { name: 'define', description: 'DEFINE: Create Spec with AmbiguityScorer + SocraticQuestioner (/spec)' },
|
|
366
446
|
args: {
|
|
367
|
-
title: { type: 'positional', required:
|
|
447
|
+
title: { type: 'positional', required: false },
|
|
368
448
|
description: { type: 'string', alias: 'd' },
|
|
369
449
|
'success-criteria': { type: 'string', alias: 'c', description: 'Comma-separated criteria' },
|
|
450
|
+
// P0 draft/confirm two-step lifecycle (backward compatible: bare `define` still auto-freezes)
|
|
451
|
+
draft: { type: 'boolean', default: false, description: 'Stop the new Spec at REVIEWING (requires `define --confirm <id>` to FROZEN)' },
|
|
452
|
+
confirm: { type: 'string', description: 'Confirm and freeze an existing draft Spec id (REVIEWING -> FROZEN)' },
|
|
453
|
+
// P0 six-element contract inputs (optional, comma-separated where plural)
|
|
454
|
+
'verification-surface': { type: 'string', description: 'Comma-separated evidence sources: test names / benchmark commands / artifact paths' },
|
|
455
|
+
'constraints': { type: 'string', description: 'Comma-separated invariants that must not regress (perf/security/compat)' },
|
|
456
|
+
'boundary-files': { type: 'string', description: 'Comma-separated files allowed to change' },
|
|
457
|
+
'boundary-tools': { type: 'string', description: 'Comma-separated tools allowed to use' },
|
|
458
|
+
'boundary-forbidden': { type: 'string', description: 'Comma-separated scope that must not be touched' },
|
|
459
|
+
'iteration-strategy': { type: 'string', description: 'How each build iteration decides the next step' },
|
|
460
|
+
'blocked-stop': { type: 'string', description: 'What to report / what is needed to unblock when no path is viable' },
|
|
370
461
|
// Socratic refinement answers (optional)
|
|
371
462
|
'goal': { type: 'string', description: 'Goal answer for Socratic refinement' },
|
|
372
463
|
'constraint': { type: 'string', description: 'Constraint answer for Socratic refinement' },
|
|
@@ -380,6 +471,15 @@ export const phaseDefine = defineCommand({
|
|
|
380
471
|
},
|
|
381
472
|
async run({ args }) {
|
|
382
473
|
const { store, fsm, workflowEngine } = getEngine();
|
|
474
|
+
// P0: --confirm freezes an existing draft Spec (REVIEWING -> FROZEN) without re-creating it.
|
|
475
|
+
if (args.confirm) {
|
|
476
|
+
await confirmDraftSpec(store, fsm, String(args.confirm), Boolean(args.json));
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (!args.title) {
|
|
480
|
+
console.error('\nMissing required argument: title (or pass --confirm <spec-id> to freeze a draft)\n');
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
383
483
|
const rawDesc = String(args.description ?? args.title);
|
|
384
484
|
// Parse success criteria
|
|
385
485
|
const successCriteria = args['success-criteria']
|
|
@@ -467,6 +567,17 @@ export const phaseDefine = defineCommand({
|
|
|
467
567
|
initialStatus: 'DRAFT',
|
|
468
568
|
createdBy: { kind: 'human', userId: 'cli' },
|
|
469
569
|
});
|
|
570
|
+
// P0 six-element contract inputs (optional; omitted fields stay undefined)
|
|
571
|
+
const csv = (v) => {
|
|
572
|
+
const items = typeof v === 'string' ? v.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
573
|
+
return items.length ? items : undefined;
|
|
574
|
+
};
|
|
575
|
+
const boundaryFiles = csv(args['boundary-files']);
|
|
576
|
+
const boundaryTools = csv(args['boundary-tools']);
|
|
577
|
+
const boundaryForbidden = csv(args['boundary-forbidden']);
|
|
578
|
+
const boundaries = (boundaryFiles || boundaryTools || boundaryForbidden)
|
|
579
|
+
? { files: boundaryFiles ?? [], tools: boundaryTools ?? [], forbidden: boundaryForbidden ?? [] }
|
|
580
|
+
: undefined;
|
|
470
581
|
// Create Spec artifact with proper payload (use refined requirement if available)
|
|
471
582
|
const specPayload = {
|
|
472
583
|
what: refinedRequirement,
|
|
@@ -475,6 +586,11 @@ export const phaseDefine = defineCommand({
|
|
|
475
586
|
edgeCases: [],
|
|
476
587
|
northStar: 'Deliver user value',
|
|
477
588
|
ambiguityScore,
|
|
589
|
+
verificationSurface: csv(args['verification-surface']),
|
|
590
|
+
constraints: csv(args['constraints']),
|
|
591
|
+
boundaries,
|
|
592
|
+
iterationStrategy: typeof args['iteration-strategy'] === 'string' && args['iteration-strategy'] ? String(args['iteration-strategy']) : undefined,
|
|
593
|
+
blockedStopCondition: typeof args['blocked-stop'] === 'string' && args['blocked-stop'] ? String(args['blocked-stop']) : undefined,
|
|
478
594
|
};
|
|
479
595
|
const spec = await store.create({
|
|
480
596
|
type: 'Spec', title: args.title,
|
|
@@ -483,11 +599,14 @@ export const phaseDefine = defineCommand({
|
|
|
483
599
|
initialStatus: 'DRAFT',
|
|
484
600
|
createdBy: { kind: 'human', userId: 'cli' },
|
|
485
601
|
});
|
|
602
|
+
// Draft mode stops at REVIEWING; default mode auto-freezes (FROZEN).
|
|
603
|
+
const isDraft = Boolean(args.draft);
|
|
604
|
+
const finalStatus = isDraft ? 'REVIEWING' : 'FROZEN';
|
|
486
605
|
// Generate spec markdown file
|
|
487
606
|
const specsDir = join(SCALE_DIR, 'specs');
|
|
488
607
|
ensureDir(specsDir);
|
|
489
608
|
const specPath = join(specsDir, `${spec.id}.md`);
|
|
490
|
-
writeFileSync(specPath, generateSpecMarkdown(spec.id, args.title, specPayload));
|
|
609
|
+
writeFileSync(specPath, generateSpecMarkdown(spec.id, args.title, specPayload, finalStatus));
|
|
491
610
|
// Generate spec HTML file (default format: html)
|
|
492
611
|
const outputFormat = args.format ?? 'md';
|
|
493
612
|
let specHtmlPath;
|
|
@@ -496,7 +615,7 @@ export const phaseDefine = defineCommand({
|
|
|
496
615
|
title: args.title,
|
|
497
616
|
brand: args.brand,
|
|
498
617
|
version: SCALE_ENGINE_VERSION,
|
|
499
|
-
status:
|
|
618
|
+
status: finalStatus,
|
|
500
619
|
});
|
|
501
620
|
const html = renderer.renderSpec({
|
|
502
621
|
id: spec.id,
|
|
@@ -507,11 +626,16 @@ export const phaseDefine = defineCommand({
|
|
|
507
626
|
edgeCases: specPayload.edgeCases,
|
|
508
627
|
northStar: specPayload.northStar,
|
|
509
628
|
ambiguityScore,
|
|
629
|
+
verificationSurface: specPayload.verificationSurface,
|
|
630
|
+
constraints: specPayload.constraints,
|
|
631
|
+
boundaries: specPayload.boundaries,
|
|
632
|
+
iterationStrategy: specPayload.iterationStrategy,
|
|
633
|
+
blockedStopCondition: specPayload.blockedStopCondition,
|
|
510
634
|
});
|
|
511
635
|
specHtmlPath = join(specsDir, `${spec.id}.html`);
|
|
512
636
|
renderer.writeToFile(html, specHtmlPath);
|
|
513
637
|
}
|
|
514
|
-
// FSM transitions: DRAFT -> REVIEWING -> FROZEN
|
|
638
|
+
// FSM transitions: DRAFT -> REVIEWING (-> FROZEN unless --draft)
|
|
515
639
|
// Phase 1: refine (DRAFT -> REVIEWING) - no guards
|
|
516
640
|
const refineResult = await fsm.canTransition(spec.id, 'refine');
|
|
517
641
|
if (!refineResult.allowed) {
|
|
@@ -522,22 +646,30 @@ export const phaseDefine = defineCommand({
|
|
|
522
646
|
process.exit(1);
|
|
523
647
|
}
|
|
524
648
|
await fsm.transition(spec.id, 'refine', { actor: { kind: 'system', component: 'phase-define' } });
|
|
525
|
-
// Phase 2: approve (REVIEWING -> FROZEN) - guards: ambiguityScore <= 0.2, has successCriteria
|
|
526
|
-
|
|
527
|
-
if (!
|
|
649
|
+
// Phase 2: approve (REVIEWING -> FROZEN) - guards: ambiguityScore <= 0.2, has successCriteria.
|
|
650
|
+
// Skipped in --draft mode: the draft waits for `scale define --confirm <id>`.
|
|
651
|
+
if (!isDraft) {
|
|
652
|
+
const approveResult = await fsm.canTransition(spec.id, 'approve');
|
|
653
|
+
if (!approveResult.allowed) {
|
|
654
|
+
if (!args.json) {
|
|
655
|
+
console.error('\nFSM transition blocked: REVIEWING -> FROZEN');
|
|
656
|
+
console.error(' Spec cannot be frozen due to:');
|
|
657
|
+
approveResult.blockedBy?.forEach(b => console.error(` [GUARD] ${b.guard}: ${b.message}`));
|
|
658
|
+
console.error('\n Resolve issues before proceeding.');
|
|
659
|
+
}
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
await fsm.transition(spec.id, 'approve', { actor: { kind: 'system', component: 'phase-define' } });
|
|
528
663
|
if (!args.json) {
|
|
529
|
-
console.
|
|
530
|
-
console.error(' Spec cannot be frozen due to:');
|
|
531
|
-
approveResult.blockedBy?.forEach(b => console.error(` [GUARD] ${b.guard}: ${b.message}`));
|
|
532
|
-
console.error('\n Resolve issues before proceeding.');
|
|
664
|
+
console.log(' FSM: DRAFT -> REVIEWING -> FROZEN ✓');
|
|
533
665
|
}
|
|
534
|
-
process.exit(1);
|
|
535
666
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
console.log(' FSM: DRAFT -> REVIEWING -> FROZEN ✓');
|
|
667
|
+
else if (!args.json) {
|
|
668
|
+
console.log(' FSM: DRAFT -> REVIEWING (draft; not yet FROZEN)');
|
|
539
669
|
}
|
|
540
|
-
|
|
670
|
+
// Refresh the spec so the reported status reflects the post-transition state.
|
|
671
|
+
const finalSpec = (await store.get(spec.id)) ?? spec;
|
|
672
|
+
const result = { phase: 'DEFINE', spec: finalSpec, specPath, specHtmlPath, ambiguityScore, successCriteria, format: outputFormat, promptOptimization, status: finalStatus, draft: isDraft };
|
|
541
673
|
// Write explore artifact for Gate G1 verification
|
|
542
674
|
const artifactWriter = new WorkflowArtifactWriter(SCALE_DIR);
|
|
543
675
|
artifactWriter.writeExploreResult({
|
|
@@ -557,10 +689,94 @@ export const phaseDefine = defineCommand({
|
|
|
557
689
|
console.log(` HTML file: ${specHtmlPath}`);
|
|
558
690
|
console.log(` Ambiguity score: ${ambiguityScore.toFixed(2)}`);
|
|
559
691
|
console.log(` Success criteria: ${successCriteria.length}`);
|
|
560
|
-
|
|
692
|
+
if (isDraft) {
|
|
693
|
+
console.log(`\n Draft created (REVIEWING). Review, then confirm:`);
|
|
694
|
+
console.log(` Next: scale define --confirm ${spec.id}\n`);
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
console.log(`\n Next: scale plan ${spec.id}\n`);
|
|
698
|
+
}
|
|
561
699
|
}
|
|
562
700
|
},
|
|
563
701
|
});
|
|
702
|
+
// Helper: Confirm a draft Spec (REVIEWING -> FROZEN) for the `define --confirm <id>` flow.
|
|
703
|
+
async function confirmDraftSpec(store, fsm, specId, json) {
|
|
704
|
+
const spec = await store.get(specId);
|
|
705
|
+
if (!spec || spec.type !== 'Spec') {
|
|
706
|
+
console.error(`\nSpec not found: ${specId}\n`);
|
|
707
|
+
process.exit(1);
|
|
708
|
+
}
|
|
709
|
+
if (spec.status === 'FROZEN') {
|
|
710
|
+
if (!json)
|
|
711
|
+
console.log(`\nSpec ${specId} is already FROZEN.\n`);
|
|
712
|
+
else
|
|
713
|
+
console.log(JSON.stringify({ phase: 'DEFINE', confirm: true, spec, status: 'FROZEN', alreadyFrozen: true }, null, 2));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const approveResult = await fsm.canTransition(specId, 'approve');
|
|
717
|
+
if (!approveResult.allowed) {
|
|
718
|
+
if (!json) {
|
|
719
|
+
console.error('\nFSM transition blocked: REVIEWING -> FROZEN');
|
|
720
|
+
console.error(' Spec cannot be confirmed due to:');
|
|
721
|
+
approveResult.blockedBy?.forEach(b => console.error(` [GUARD] ${b.guard}: ${b.message}`));
|
|
722
|
+
console.error('\n Resolve issues before confirming.');
|
|
723
|
+
}
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
await fsm.transition(specId, 'approve', { actor: { kind: 'human', userId: 'cli' } });
|
|
727
|
+
// Refresh persisted markdown status (draft was written as REVIEWING).
|
|
728
|
+
const specPath = join(SCALE_DIR, 'specs', `${specId}.md`);
|
|
729
|
+
if (existsSync(specPath)) {
|
|
730
|
+
writeFileSync(specPath, generateSpecMarkdown(specId, spec.title, spec.payload, 'FROZEN'));
|
|
731
|
+
}
|
|
732
|
+
const confirmed = await store.get(specId);
|
|
733
|
+
if (json) {
|
|
734
|
+
console.log(JSON.stringify({ phase: 'DEFINE', confirm: true, spec: confirmed, status: 'FROZEN' }, null, 2));
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
console.log(`\nCONFIRM: ${specId}`);
|
|
738
|
+
console.log(' FSM: REVIEWING -> FROZEN ✓');
|
|
739
|
+
console.log(`\n Next: scale plan ${specId}\n`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Helper: Resolve the originating Spec for a Task by walking Task -> Plan -> Spec.
|
|
743
|
+
async function resolveSpecForTask(store, task) {
|
|
744
|
+
const planId = task?.parents?.[0];
|
|
745
|
+
if (!planId)
|
|
746
|
+
return undefined;
|
|
747
|
+
const plan = await store.get(planId);
|
|
748
|
+
const specId = plan?.parents?.[0];
|
|
749
|
+
if (!specId)
|
|
750
|
+
return undefined;
|
|
751
|
+
const spec = await store.get(specId);
|
|
752
|
+
if (!spec || spec.type !== 'Spec')
|
|
753
|
+
return undefined;
|
|
754
|
+
return { id: spec.id, payload: spec.payload };
|
|
755
|
+
}
|
|
756
|
+
// Helper: Collect free-form evidence signals (commands run, files, evidence refs/artifacts)
|
|
757
|
+
// used to soft-map a Spec's verificationSurface during verify/ship (P0 Decision C1).
|
|
758
|
+
async function gatherVerificationSignals(store, options) {
|
|
759
|
+
const signals = [];
|
|
760
|
+
for (const command of options.commands ?? [])
|
|
761
|
+
if (command)
|
|
762
|
+
signals.push(command);
|
|
763
|
+
for (const file of options.files ?? [])
|
|
764
|
+
if (file)
|
|
765
|
+
signals.push(file);
|
|
766
|
+
for (const id of options.evidenceIds ?? []) {
|
|
767
|
+
const record = await store.get(id);
|
|
768
|
+
if (!record || record.type !== 'Evidence')
|
|
769
|
+
continue;
|
|
770
|
+
const payload = record.payload;
|
|
771
|
+
if (payload.verificationSurfaceRef)
|
|
772
|
+
signals.push(payload.verificationSurfaceRef);
|
|
773
|
+
if (payload.toolUsed)
|
|
774
|
+
signals.push(payload.toolUsed);
|
|
775
|
+
if (payload.artifacts?.length)
|
|
776
|
+
signals.push(...payload.artifacts);
|
|
777
|
+
}
|
|
778
|
+
return signals;
|
|
779
|
+
}
|
|
564
780
|
// Helper: Generate plan markdown file
|
|
565
781
|
function generatePlanMarkdown(id, specId, payload) {
|
|
566
782
|
return `# Plan: ${id}
|
|
@@ -906,10 +1122,15 @@ export const phaseVerify = defineCommand({
|
|
|
906
1122
|
'skip-build': { type: 'boolean', default: false },
|
|
907
1123
|
'skip-lint': { type: 'boolean', default: false },
|
|
908
1124
|
'skip-test': { type: 'boolean', default: false },
|
|
1125
|
+
progress: { type: 'boolean', default: false, description: 'Emit coarse verify progress events to stderr without changing JSON output' },
|
|
909
1126
|
json: { type: 'boolean', default: false },
|
|
910
1127
|
},
|
|
911
1128
|
async run({ args }) {
|
|
912
1129
|
const { store, fsm, workflowEngine } = getEngine();
|
|
1130
|
+
const emitProgress = (event, detail) => {
|
|
1131
|
+
if (isTruthyFlag(args.progress))
|
|
1132
|
+
console.error(`[progress] ${event}: ${detail}`);
|
|
1133
|
+
};
|
|
913
1134
|
// Validate task exists
|
|
914
1135
|
const task = await store.get(args['task-id']);
|
|
915
1136
|
if (!task || task.type !== 'Task') {
|
|
@@ -932,6 +1153,7 @@ export const phaseVerify = defineCommand({
|
|
|
932
1153
|
service: args.service,
|
|
933
1154
|
services: args.service ? undefined : taskServices,
|
|
934
1155
|
});
|
|
1156
|
+
emitProgress('verify:start', `task=${args['task-id']} profile=${resolvedVerification.profileName} targets=${resolvedVerification.targets.length}`);
|
|
935
1157
|
if (!args.json) {
|
|
936
1158
|
for (const warning of resolvedVerification.warnings)
|
|
937
1159
|
console.log(` [WARN] ${warning}`);
|
|
@@ -947,6 +1169,7 @@ export const phaseVerify = defineCommand({
|
|
|
947
1169
|
if (!args.json && resolvedVerification.targets.length > 1) {
|
|
948
1170
|
console.log(`\n Target: ${target.service?.name ?? 'root'}`);
|
|
949
1171
|
}
|
|
1172
|
+
emitProgress('target:start', target.service?.name ?? 'root');
|
|
950
1173
|
const targetResults = await workflowEngine.verify({
|
|
951
1174
|
cwd: target.config.cwd,
|
|
952
1175
|
build: args['build-cmd'] ?? target.config.build,
|
|
@@ -964,7 +1187,9 @@ export const phaseVerify = defineCommand({
|
|
|
964
1187
|
tddStrict: isTruthyFlag(args['tdd-strict']),
|
|
965
1188
|
});
|
|
966
1189
|
gateResults.push(...targetResults);
|
|
1190
|
+
emitProgress('target:done', `${target.service?.name ?? 'root'} gates=${targetResults.length}`);
|
|
967
1191
|
}
|
|
1192
|
+
emitProgress('verify:gates-complete', `gates=${gateResults.length}`);
|
|
968
1193
|
// Step 2: Display gate results
|
|
969
1194
|
if (!args.json) {
|
|
970
1195
|
console.log('\nGate Results:');
|
|
@@ -1095,6 +1320,16 @@ export const phaseVerify = defineCommand({
|
|
|
1095
1320
|
});
|
|
1096
1321
|
const workflowOpenTaskBlockers = blockingWorkflowOpenTasks(workflowState.openTasks, args['task-id']);
|
|
1097
1322
|
const workflowOpenTasksBlocked = workflowOpenTaskBlockers.length > 0;
|
|
1323
|
+
// P0+ (decision E1): resolve the originating Spec up-front so the executional
|
|
1324
|
+
// boundary / constraint checks can gate Task completion. Both reports are
|
|
1325
|
+
// advisory under default/auto and blocking under full/ci/strict; the
|
|
1326
|
+
// detection logic is identical, only the report mode and gating differ.
|
|
1327
|
+
const spec = await resolveSpecForTask(store, task);
|
|
1328
|
+
const boundaryEnforced = isEnforcedBoundaryProfile(resolvedVerification.profileName);
|
|
1329
|
+
const boundaryEnforcement = evaluateBoundaries(taskFiles, spec?.payload.boundaries, boundaryEnforced);
|
|
1330
|
+
const constraintCoverage = evaluateConstraints(spec?.payload.constraints, spec?.payload.verificationSurface, boundaryEnforced);
|
|
1331
|
+
const boundaryBlocked = boundaryEnforced &&
|
|
1332
|
+
countBoundaryBlockers(boundaryEnforcement, constraintCoverage) > 0;
|
|
1098
1333
|
// Attempt FSM transition to COMPLETED
|
|
1099
1334
|
// Guards: build_passed, lint_passed, tests_passed, open workflow tasks, and optional artifact policy.
|
|
1100
1335
|
const codePassed = results.buildStatus === 'success' &&
|
|
@@ -1109,9 +1344,15 @@ export const phaseVerify = defineCommand({
|
|
|
1109
1344
|
!skillInstallationBlocked &&
|
|
1110
1345
|
!engineeringStandards.blocked &&
|
|
1111
1346
|
!(toolEvidenceGate?.blocked ?? false) &&
|
|
1347
|
+
!boundaryBlocked &&
|
|
1112
1348
|
!workflowOpenTasksBlocked;
|
|
1113
1349
|
let transitionResult = null;
|
|
1114
|
-
|
|
1350
|
+
const alreadyCompleted = task.status === 'COMPLETED';
|
|
1351
|
+
if (completionEligible && alreadyCompleted) {
|
|
1352
|
+
if (!args.json)
|
|
1353
|
+
console.log('\n FSM: already COMPLETED');
|
|
1354
|
+
}
|
|
1355
|
+
else if (completionEligible) {
|
|
1115
1356
|
const completeResult = await fsm.canTransition(args['task-id'], 'complete');
|
|
1116
1357
|
if (!completeResult.allowed) {
|
|
1117
1358
|
if (!args.json) {
|
|
@@ -1147,10 +1388,13 @@ export const phaseVerify = defineCommand({
|
|
|
1147
1388
|
else if (!args.json && toolEvidenceGate?.blocked) {
|
|
1148
1389
|
console.log('\n Tool evidence gate blocked completion - required tools need passed execution evidence');
|
|
1149
1390
|
}
|
|
1391
|
+
else if (!args.json && boundaryBlocked) {
|
|
1392
|
+
console.log('\n Boundary enforcement blocked completion - keep edits inside Spec boundaries and guard every constraint (enforced profile)');
|
|
1393
|
+
}
|
|
1150
1394
|
else if (!args.json && workflowOpenTasksBlocked) {
|
|
1151
1395
|
console.log('\n Workflow open tasks blocked completion - finish required workflow commands first');
|
|
1152
1396
|
}
|
|
1153
|
-
const passed = completionEligible && (transitionResult?.success ?? false);
|
|
1397
|
+
const passed = completionEligible && (alreadyCompleted || (transitionResult?.success ?? false));
|
|
1154
1398
|
if (passed) {
|
|
1155
1399
|
workflowState = workflowWriter.updateCurrentState({
|
|
1156
1400
|
openTasks: removeWorkflowOpenTask(workflowState.openTasks, 'verification'),
|
|
@@ -1203,7 +1447,7 @@ export const phaseVerify = defineCommand({
|
|
|
1203
1447
|
toolEvidenceGatePassed: finalToolEvidenceGate ? !finalToolEvidenceGate.blocked : true,
|
|
1204
1448
|
};
|
|
1205
1449
|
await store.update(args['task-id'], { payload: finalPayload });
|
|
1206
|
-
const metricGateStatus = (finalArtifactGate.blocked || finalSkillGate?.blocked || skillInstallationBlocked || engineeringStandards.blocked || finalToolEvidenceGate?.blocked || workflowOpenTasksBlocked)
|
|
1450
|
+
const metricGateStatus = (finalArtifactGate.blocked || finalSkillGate?.blocked || skillInstallationBlocked || engineeringStandards.blocked || finalToolEvidenceGate?.blocked || boundaryBlocked || workflowOpenTasksBlocked)
|
|
1207
1451
|
? 'blocked'
|
|
1208
1452
|
: undefined;
|
|
1209
1453
|
const metricRecord = await recordVerificationMetric({
|
|
@@ -1215,6 +1459,30 @@ export const phaseVerify = defineCommand({
|
|
|
1215
1459
|
artifactCheck,
|
|
1216
1460
|
finalGateStatus: metricGateStatus,
|
|
1217
1461
|
});
|
|
1462
|
+
const cortexInstinctApplications = recordRuntimeInstinctApplications({
|
|
1463
|
+
projectDir: PROJECT_DIR,
|
|
1464
|
+
scaleDir: SCALE_DIR,
|
|
1465
|
+
phase: 'verify',
|
|
1466
|
+
success: passed,
|
|
1467
|
+
});
|
|
1468
|
+
// P0 (Decision C1): soft-map the Spec's verificationSurface against evidence.
|
|
1469
|
+
// Unmapped items are reported as warnings only — never blocking in P0.
|
|
1470
|
+
// (`spec`, `boundaryEnforcement` and `constraintCoverage` were resolved
|
|
1471
|
+
// above so the boundary checks could gate completion under enforced profiles.)
|
|
1472
|
+
const verificationCommands = resolvedVerification.targets.flatMap(target => [
|
|
1473
|
+
target.config.build, target.config.lint, target.config.test, target.config.coverage,
|
|
1474
|
+
]);
|
|
1475
|
+
const surfaceSignals = await gatherVerificationSignals(store, {
|
|
1476
|
+
evidenceIds: verificationEvidenceIds,
|
|
1477
|
+
commands: [
|
|
1478
|
+
...verificationCommands,
|
|
1479
|
+
args['build-cmd'], args['lint-cmd'], args['test-cmd'], args['coverage-cmd'],
|
|
1480
|
+
],
|
|
1481
|
+
files: taskFiles,
|
|
1482
|
+
});
|
|
1483
|
+
const surfaceCoverage = spec?.payload.verificationSurface?.length
|
|
1484
|
+
? computeSurfaceCoverage(spec.payload.verificationSurface, surfaceSignals)
|
|
1485
|
+
: undefined;
|
|
1218
1486
|
const result = {
|
|
1219
1487
|
phase: 'VERIFY',
|
|
1220
1488
|
taskId: args['task-id'],
|
|
@@ -1240,14 +1508,29 @@ export const phaseVerify = defineCommand({
|
|
|
1240
1508
|
blocked: skillInstallationBlocked,
|
|
1241
1509
|
},
|
|
1242
1510
|
metric: metricRecord,
|
|
1511
|
+
cortexInstinctApplications,
|
|
1512
|
+
verificationSurfaceCoverage: surfaceCoverage,
|
|
1513
|
+
boundaryEnforcement,
|
|
1514
|
+
constraintCoverage,
|
|
1243
1515
|
passed
|
|
1244
1516
|
};
|
|
1245
1517
|
if (args.json)
|
|
1246
1518
|
console.log(JSON.stringify(result, null, 2));
|
|
1247
1519
|
else {
|
|
1248
1520
|
console.log(`\nVERIFY: ${passed ? 'PASSED' : 'FAILED'}`);
|
|
1521
|
+
if (surfaceCoverage) {
|
|
1522
|
+
for (const line of formatSurfaceCoverageWarnings(surfaceCoverage))
|
|
1523
|
+
console.log(` ${line}`);
|
|
1524
|
+
}
|
|
1525
|
+
for (const line of formatBoundaryWarnings(boundaryEnforcement))
|
|
1526
|
+
console.log(` ${line}`);
|
|
1527
|
+
for (const line of formatConstraintWarnings(constraintCoverage))
|
|
1528
|
+
console.log(` ${line}`);
|
|
1249
1529
|
if (metricRecord)
|
|
1250
1530
|
console.log(` Metrics: ${metricRecord.taskId} ${metricRecord.finalGateStatus} (fix iterations: ${metricRecord.fixIterations})`);
|
|
1531
|
+
if (cortexInstinctApplications.recorded.length > 0) {
|
|
1532
|
+
console.log(` Cortex instincts: ${cortexInstinctApplications.recorded.length} outcome(s) recorded`);
|
|
1533
|
+
}
|
|
1251
1534
|
if (artifactCheck && !artifactCheck.complete) {
|
|
1252
1535
|
console.log(` Artifact gaps: ${artifactCheck.missing.length} missing, ${artifactCheck.incomplete.length} incomplete`);
|
|
1253
1536
|
}
|
|
@@ -1309,7 +1592,13 @@ function readUntrackedFileAsDiff(path) {
|
|
|
1309
1592
|
return '';
|
|
1310
1593
|
}
|
|
1311
1594
|
}
|
|
1312
|
-
|
|
1595
|
+
function normalizeMaxDiffFiles(value) {
|
|
1596
|
+
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
1597
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
1598
|
+
return 200;
|
|
1599
|
+
return Math.min(parsed, 1000);
|
|
1600
|
+
}
|
|
1601
|
+
async function reviewGitChanges(taskPayload, maxDiffFiles = 200) {
|
|
1313
1602
|
const status = await runGit(['status', '--short']);
|
|
1314
1603
|
const untracked = await runGit(['ls-files', '--others', '--exclude-standard']);
|
|
1315
1604
|
let statusOutput = mergeUntrackedFilesIntoStatus(status.stdout, untracked.stdout);
|
|
@@ -1332,7 +1621,7 @@ async function reviewGitChanges(taskPayload) {
|
|
|
1332
1621
|
const verificationEvidence = getVerificationEvidenceSummary(taskPayload?.verificationEvidenceIds);
|
|
1333
1622
|
const changedFiles = analyzeReview({ statusOutput, diffs: [], taskPayload, verificationEvidence }).changedFiles;
|
|
1334
1623
|
const diffs = [];
|
|
1335
|
-
for (const file of changedFiles.slice(0,
|
|
1624
|
+
for (const file of changedFiles.slice(0, maxDiffFiles)) {
|
|
1336
1625
|
if (file.status === '??') {
|
|
1337
1626
|
diffs.push({ file: file.path, text: readUntrackedFileAsDiff(file.path) });
|
|
1338
1627
|
}
|
|
@@ -1341,7 +1630,23 @@ async function reviewGitChanges(taskPayload) {
|
|
|
1341
1630
|
diffs.push({ file: file.path, text: diff.stdout });
|
|
1342
1631
|
}
|
|
1343
1632
|
}
|
|
1344
|
-
return analyzeReview({ statusOutput, diffs, taskPayload, verificationEvidence });
|
|
1633
|
+
return { ...analyzeReview({ statusOutput, diffs, taskPayload, verificationEvidence }), diffs };
|
|
1634
|
+
}
|
|
1635
|
+
function normalizeReviewMode(value) {
|
|
1636
|
+
return value === 'fresh-subagent' || value === 'hybrid' ? value : 'ai-self';
|
|
1637
|
+
}
|
|
1638
|
+
// Build a compact diff summary (file headers + added lines) for the advisory
|
|
1639
|
+
// LLM-as-Judge (P1.4). Capped so it never blows the model/context budget.
|
|
1640
|
+
function buildJudgeDiffSummary(diffs) {
|
|
1641
|
+
const parts = [];
|
|
1642
|
+
for (const diff of diffs) {
|
|
1643
|
+
const added = diff.text
|
|
1644
|
+
.split('\n')
|
|
1645
|
+
.filter(line => line.startsWith('+') && !line.startsWith('+++'))
|
|
1646
|
+
.map(line => line.slice(1));
|
|
1647
|
+
parts.push(`# ${diff.file}\n${added.join('\n')}`);
|
|
1648
|
+
}
|
|
1649
|
+
return parts.join('\n\n').slice(0, 6000);
|
|
1345
1650
|
}
|
|
1346
1651
|
function collectReviewedFiles(records) {
|
|
1347
1652
|
const reviewed = new Set();
|
|
@@ -1553,6 +1858,9 @@ export const phaseReview = defineCommand({
|
|
|
1553
1858
|
'check-style': { type: 'boolean', default: true },
|
|
1554
1859
|
format: { type: 'string', alias: 'f', description: 'Output format: html or md (default: html)' },
|
|
1555
1860
|
brand: { type: 'string', description: 'Brand theme for HTML output (vercel/stripe/notion/linear/github)' },
|
|
1861
|
+
judge: { type: 'boolean', default: true, description: 'Run the advisory LLM-as-Judge spec-conformance check (P1.4)' },
|
|
1862
|
+
mode: { type: 'string', default: 'ai-self', description: 'Review mode: ai-self (default) | fresh-subagent | hybrid (P2.2)' },
|
|
1863
|
+
'max-diff-files': { type: 'string', default: '200', description: 'Maximum changed files whose diffs are scanned during deterministic review' },
|
|
1556
1864
|
json: { type: 'boolean', default: false },
|
|
1557
1865
|
},
|
|
1558
1866
|
async run({ args }) {
|
|
@@ -1569,7 +1877,7 @@ export const phaseReview = defineCommand({
|
|
|
1569
1877
|
}
|
|
1570
1878
|
taskPayload = task.payload;
|
|
1571
1879
|
}
|
|
1572
|
-
const review = await reviewGitChanges(taskPayload);
|
|
1880
|
+
const review = await reviewGitChanges(taskPayload, normalizeMaxDiffFiles(args['max-diff-files']));
|
|
1573
1881
|
const karpathyContext = deriveReviewKarpathyContext(review, taskPayload);
|
|
1574
1882
|
const karpathyResult = workflowEngine.checkKarpathy(karpathyContext);
|
|
1575
1883
|
const karpathyReport = {
|
|
@@ -1586,12 +1894,44 @@ export const phaseReview = defineCommand({
|
|
|
1586
1894
|
const findings = review.findings;
|
|
1587
1895
|
const summary = summarizeFindings(findings);
|
|
1588
1896
|
const passed = summary.critical === 0 && summary.high === 0;
|
|
1897
|
+
const reviewMode = normalizeReviewMode(args.mode);
|
|
1898
|
+
// Resolve the originating Spec once; both the advisory judge (P1.4) and the
|
|
1899
|
+
// fresh-context verifier (P2.2) read its outcome / verificationSurface.
|
|
1900
|
+
const needsSpec = args.judge || reviewMode !== 'ai-self';
|
|
1901
|
+
const spec = needsSpec ? await resolveSpecForTask(store, task) : undefined;
|
|
1902
|
+
const diffSummary = needsSpec ? buildJudgeDiffSummary(review.diffs) : '';
|
|
1903
|
+
// P1.4 (decision K1): advisory LLM-as-Judge. Never part of `passed`.
|
|
1904
|
+
let judgeVerdict;
|
|
1905
|
+
if (args.judge) {
|
|
1906
|
+
const judge = new LlmJudge(new JsonLlmClient(), new JudgePromptStore(SCALE_DIR));
|
|
1907
|
+
judgeVerdict = await judge.judge({
|
|
1908
|
+
outcome: spec?.payload.what,
|
|
1909
|
+
verificationSurface: spec?.payload.verificationSurface ?? [],
|
|
1910
|
+
diffSummary,
|
|
1911
|
+
reviewFindings: summary,
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
// P2.2 (decisions M1/N1/O1): fresh-context verifier runs only for
|
|
1915
|
+
// fresh-subagent / hybrid modes, on isolated input (surface + diff + gate
|
|
1916
|
+
// summary, no build-agent history). Advisory only — never blocks ship.
|
|
1917
|
+
let freshVerifyVerdict;
|
|
1918
|
+
if (reviewMode !== 'ai-self') {
|
|
1919
|
+
freshVerifyVerdict = await new FreshContextVerifier(new JsonLlmClient()).verify({
|
|
1920
|
+
outcome: spec?.payload.what,
|
|
1921
|
+
verificationSurface: spec?.payload.verificationSurface ?? [],
|
|
1922
|
+
diffSummary,
|
|
1923
|
+
gateSummary: `critical=${summary.critical} high=${summary.high} medium=${summary.medium} low=${summary.low}`,
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1589
1926
|
const record = reviewStore.saveReview({
|
|
1590
1927
|
taskId: args['task-id'],
|
|
1591
1928
|
passed,
|
|
1592
1929
|
findings,
|
|
1593
1930
|
changedFiles: review.changedFiles.map(file => normalizeGitPath(file.path)),
|
|
1594
1931
|
summary,
|
|
1932
|
+
judge: judgeVerdict,
|
|
1933
|
+
reviewMode,
|
|
1934
|
+
freshVerify: freshVerifyVerdict,
|
|
1595
1935
|
});
|
|
1596
1936
|
if (task && taskPayload) {
|
|
1597
1937
|
const updatedPayload = {
|
|
@@ -1638,6 +1978,9 @@ export const phaseReview = defineCommand({
|
|
|
1638
1978
|
findings,
|
|
1639
1979
|
changedFiles: review.changedFiles.map(file => normalizeGitPath(file.path)),
|
|
1640
1980
|
summary,
|
|
1981
|
+
judge: judgeVerdict,
|
|
1982
|
+
reviewMode,
|
|
1983
|
+
freshVerify: freshVerifyVerdict,
|
|
1641
1984
|
karpathy: karpathyReport,
|
|
1642
1985
|
passed,
|
|
1643
1986
|
format: reviewOutputFormat,
|
|
@@ -1660,6 +2003,18 @@ export const phaseReview = defineCommand({
|
|
|
1660
2003
|
console.log(`LOW: ${summary.low} issues`);
|
|
1661
2004
|
console.log('----------------------------------------');
|
|
1662
2005
|
findings.slice(0, 10).forEach(f => console.log(` [${f.severity}] ${f.file ? `${f.file}: ` : ''}${f.description}`));
|
|
2006
|
+
if (judgeVerdict) {
|
|
2007
|
+
console.log(`\nJudge (advisory, ${judgeVerdict.modelUsed}): ${judgeVerdict.decision.toUpperCase()} (confidence ${judgeVerdict.confidence.toFixed(2)})`);
|
|
2008
|
+
console.log(` ${judgeVerdict.rationale}`);
|
|
2009
|
+
if (judgeVerdict.unmetSurfaces.length)
|
|
2010
|
+
console.log(` Unmet surfaces: ${judgeVerdict.unmetSurfaces.join('; ')}`);
|
|
2011
|
+
}
|
|
2012
|
+
if (freshVerifyVerdict) {
|
|
2013
|
+
console.log(`\nFresh-context verifier (advisory, ${freshVerifyVerdict.modelUsed}): ${freshVerifyVerdict.decision.toUpperCase()} (confidence ${freshVerifyVerdict.confidence.toFixed(2)})`);
|
|
2014
|
+
console.log(` ${freshVerifyVerdict.rationale}`);
|
|
2015
|
+
if (freshVerifyVerdict.unmetSurfaces.length)
|
|
2016
|
+
console.log(` Unmet surfaces: ${freshVerifyVerdict.unmetSurfaces.join('; ')}`);
|
|
2017
|
+
}
|
|
1663
2018
|
if (passed) {
|
|
1664
2019
|
console.log('\nReview passed (no CRITICAL issues)');
|
|
1665
2020
|
console.log('\n Next: scale ship ' + (args['task-id'] ?? '<task-id>') + '\n');
|
|
@@ -1679,6 +2034,11 @@ export const phaseShip = defineCommand({
|
|
|
1679
2034
|
message: { type: 'string', alias: 'm', description: 'Commit message' },
|
|
1680
2035
|
'no-commit': { type: 'boolean', default: false, description: 'Skip git commit' },
|
|
1681
2036
|
'skip-commit': { type: 'boolean', default: false, description: 'Skip git commit' },
|
|
2037
|
+
'record-deployment': { type: 'boolean', default: false, description: 'Record a deployment event after a successful ship commit' },
|
|
2038
|
+
'deploy-service': { type: 'string', default: 'scale-engine', description: 'Deployment service name' },
|
|
2039
|
+
'deploy-environment': { type: 'string', default: 'production', description: 'Deployment environment name' },
|
|
2040
|
+
'deploy-version': { type: 'string', description: 'Deployment version override' },
|
|
2041
|
+
'deployed-at': { type: 'string', description: 'Deployment completion timestamp override' },
|
|
1682
2042
|
json: { type: 'boolean', default: false },
|
|
1683
2043
|
},
|
|
1684
2044
|
async run({ args }) {
|
|
@@ -1755,10 +2115,12 @@ export const phaseShip = defineCommand({
|
|
|
1755
2115
|
process.exit(1);
|
|
1756
2116
|
}
|
|
1757
2117
|
// Git operations
|
|
2118
|
+
const skipCommit = shouldSkipCommit(args['skip-commit']);
|
|
1758
2119
|
let commitHash = null;
|
|
1759
2120
|
let stagedFiles = [];
|
|
1760
2121
|
let workspaceBoundary = null;
|
|
1761
|
-
|
|
2122
|
+
let deploymentRecord = null;
|
|
2123
|
+
if (!skipCommit) {
|
|
1762
2124
|
const commitMessage = args.message ?? `feat: ${task.title ?? args['task-id']}`;
|
|
1763
2125
|
try {
|
|
1764
2126
|
workspaceBoundary = await validateWorkspaceShipBoundary();
|
|
@@ -1789,7 +2151,11 @@ export const phaseShip = defineCommand({
|
|
|
1789
2151
|
}
|
|
1790
2152
|
}
|
|
1791
2153
|
else {
|
|
1792
|
-
|
|
2154
|
+
const head = await runGit(['rev-parse', 'HEAD']);
|
|
2155
|
+
if (head.exitCode !== 0 || !head.stdout.trim()) {
|
|
2156
|
+
throw new Error(head.stderr || 'git rev-parse HEAD failed after commit');
|
|
2157
|
+
}
|
|
2158
|
+
commitHash = head.stdout.trim();
|
|
1793
2159
|
}
|
|
1794
2160
|
}
|
|
1795
2161
|
catch (e) {
|
|
@@ -1802,12 +2168,64 @@ export const phaseShip = defineCommand({
|
|
|
1802
2168
|
if (task.parents.length > 0) {
|
|
1803
2169
|
const planId = task.parents[0];
|
|
1804
2170
|
try {
|
|
1805
|
-
await
|
|
2171
|
+
const plan = await store.get(planId);
|
|
2172
|
+
if (plan?.status !== 'DONE') {
|
|
2173
|
+
await fsm.transition(planId, 'complete', { actor: { kind: 'system', component: 'phase-ship' } });
|
|
2174
|
+
}
|
|
1806
2175
|
}
|
|
1807
2176
|
catch (e) {
|
|
1808
2177
|
console.error("Warning: Plan completion transition failed:", e.message);
|
|
1809
2178
|
}
|
|
1810
2179
|
}
|
|
2180
|
+
// P0 (Decision C1): soft-map the Spec's verificationSurface at ship time too.
|
|
2181
|
+
const shipSpec = await resolveSpecForTask(store, task);
|
|
2182
|
+
const shipSignals = await gatherVerificationSignals(store, {
|
|
2183
|
+
evidenceIds: payload.verificationEvidenceIds,
|
|
2184
|
+
files: payload.filesInvolved,
|
|
2185
|
+
});
|
|
2186
|
+
const shipSurfaceCoverage = shipSpec?.payload.verificationSurface?.length
|
|
2187
|
+
? computeSurfaceCoverage(shipSpec.payload.verificationSurface, shipSignals)
|
|
2188
|
+
: undefined;
|
|
2189
|
+
if (isTruthyFlag(args['record-deployment'])) {
|
|
2190
|
+
if (!commitHash) {
|
|
2191
|
+
process.stderr.write('\nDeployment recording requires a new ship commit. Re-run without --no-commit/--skip-commit, or record a real deployment explicitly with scale workflow deploy record.\n\n');
|
|
2192
|
+
process.exit(1);
|
|
2193
|
+
}
|
|
2194
|
+
try {
|
|
2195
|
+
deploymentRecord = await recordShipDeployment({
|
|
2196
|
+
taskId: args['task-id'],
|
|
2197
|
+
taskTitle: task.title,
|
|
2198
|
+
taskPayload: payload,
|
|
2199
|
+
commitHash,
|
|
2200
|
+
args,
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
catch (error) {
|
|
2204
|
+
process.stderr.write(`\nDeployment record failed: ${error.message}\n`);
|
|
2205
|
+
process.exit(1);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
const cortexInstinctApplications = recordRuntimeInstinctApplications({
|
|
2209
|
+
projectDir: PROJECT_DIR,
|
|
2210
|
+
scaleDir: SCALE_DIR,
|
|
2211
|
+
phase: 'ship',
|
|
2212
|
+
success: true,
|
|
2213
|
+
});
|
|
2214
|
+
const shippedAt = Date.now();
|
|
2215
|
+
const shipMode = skipCommit
|
|
2216
|
+
? 'no-commit'
|
|
2217
|
+
: commitHash
|
|
2218
|
+
? 'commit'
|
|
2219
|
+
: 'commit-skipped';
|
|
2220
|
+
const shippedPayload = {
|
|
2221
|
+
...payload,
|
|
2222
|
+
shipPassed: true,
|
|
2223
|
+
shippedAt,
|
|
2224
|
+
shipMode,
|
|
2225
|
+
shipCommitHash: commitHash ?? undefined,
|
|
2226
|
+
shipDeploymentRecordId: deploymentRecord?.id,
|
|
2227
|
+
};
|
|
2228
|
+
await store.update(task.id, { payload: shippedPayload });
|
|
1811
2229
|
// === WorkflowEngine Integration ===
|
|
1812
2230
|
// Generate HonestDelivery report
|
|
1813
2231
|
if (!args.json) {
|
|
@@ -1818,6 +2236,11 @@ export const phaseShip = defineCommand({
|
|
|
1818
2236
|
console.log(` - Status: COMPLETED`);
|
|
1819
2237
|
if (commitHash)
|
|
1820
2238
|
console.log(` - Commit: ${commitHash}`);
|
|
2239
|
+
if (deploymentRecord)
|
|
2240
|
+
process.stdout.write(` - Deployment evidence: ${deploymentRecord.id}\n`);
|
|
2241
|
+
if (cortexInstinctApplications.recorded.length > 0) {
|
|
2242
|
+
console.log(` - Cortex instinct outcomes: ${cortexInstinctApplications.recorded.length}`);
|
|
2243
|
+
}
|
|
1821
2244
|
if (stagedFiles.length)
|
|
1822
2245
|
console.log(` - Files committed: ${stagedFiles.length}`);
|
|
1823
2246
|
console.log('');
|
|
@@ -1844,6 +2267,10 @@ export const phaseShip = defineCommand({
|
|
|
1844
2267
|
unverifiedItems.forEach(item => console.log(` [UNVERIFIED] ${item}`));
|
|
1845
2268
|
console.log('');
|
|
1846
2269
|
}
|
|
2270
|
+
if (shipSurfaceCoverage) {
|
|
2271
|
+
for (const line of formatSurfaceCoverageWarnings(shipSurfaceCoverage))
|
|
2272
|
+
console.log(line);
|
|
2273
|
+
}
|
|
1847
2274
|
}
|
|
1848
2275
|
const result = {
|
|
1849
2276
|
phase: 'SHIP',
|
|
@@ -1854,7 +2281,10 @@ export const phaseShip = defineCommand({
|
|
|
1854
2281
|
reviewEvidenceIds: payload.reviewEvidenceIds ?? [],
|
|
1855
2282
|
reviewValidation,
|
|
1856
2283
|
commitHash,
|
|
2284
|
+
deploymentRecord,
|
|
1857
2285
|
stagedFiles,
|
|
2286
|
+
shippedAt,
|
|
2287
|
+
shipMode,
|
|
1858
2288
|
workspaceBoundary: workspaceBoundary ? {
|
|
1859
2289
|
topology: workspaceBoundary.report?.topology.topology ?? null,
|
|
1860
2290
|
configured: workspaceBoundary.report?.topology.configured ?? false,
|
|
@@ -1863,6 +2293,8 @@ export const phaseShip = defineCommand({
|
|
|
1863
2293
|
blockers: workspaceBoundary.blockers,
|
|
1864
2294
|
warnings: workspaceBoundary.warnings,
|
|
1865
2295
|
} : null,
|
|
2296
|
+
cortexInstinctApplications,
|
|
2297
|
+
verificationSurfaceCoverage: shipSurfaceCoverage,
|
|
1866
2298
|
};
|
|
1867
2299
|
if (args.json)
|
|
1868
2300
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1871,6 +2303,8 @@ export const phaseShip = defineCommand({
|
|
|
1871
2303
|
console.log('\nTask COMPLETED: ' + args['task-id']);
|
|
1872
2304
|
if (commitHash)
|
|
1873
2305
|
console.log(' Commit: ' + commitHash);
|
|
2306
|
+
if (deploymentRecord)
|
|
2307
|
+
process.stdout.write(' Deployment evidence: ' + deploymentRecord.id + '\n');
|
|
1874
2308
|
console.log('\nDone. Feature shipped.\n');
|
|
1875
2309
|
}
|
|
1876
2310
|
},
|