@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.
Files changed (242) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/agents/evidenceDiscipline.d.ts +7 -0
  4. package/dist/agents/evidenceDiscipline.js +21 -0
  5. package/dist/agents/evidenceDiscipline.js.map +1 -0
  6. package/dist/agents/profiles.js +8 -1
  7. package/dist/agents/profiles.js.map +1 -1
  8. package/dist/agents/types.d.ts +1 -0
  9. package/dist/api/DashboardHttpConfig.d.ts +28 -0
  10. package/dist/api/DashboardHttpConfig.js +110 -0
  11. package/dist/api/DashboardHttpConfig.js.map +1 -0
  12. package/dist/api/cli.js +102 -11
  13. package/dist/api/cli.js.map +1 -1
  14. package/dist/api/http.d.ts +1 -0
  15. package/dist/api/http.js +50 -0
  16. package/dist/api/http.js.map +1 -0
  17. package/dist/artifact/types.d.ts +64 -0
  18. package/dist/artifact/types.js.map +1 -1
  19. package/dist/bootstrap/DependencyBootstrap.d.ts +1 -0
  20. package/dist/bootstrap/DependencyBootstrap.js +14 -3
  21. package/dist/bootstrap/DependencyBootstrap.js.map +1 -1
  22. package/dist/cli/cortexApplyCommand.d.ts +26 -0
  23. package/dist/cli/cortexApplyCommand.js +74 -0
  24. package/dist/cli/cortexApplyCommand.js.map +1 -0
  25. package/dist/cli/cortexCandidateCommands.d.ts +42 -0
  26. package/dist/cli/cortexCandidateCommands.js +119 -0
  27. package/dist/cli/cortexCandidateCommands.js.map +1 -0
  28. package/dist/cli/cortexCommands.d.ts +51 -0
  29. package/dist/cli/cortexCommands.js +127 -13
  30. package/dist/cli/cortexCommands.js.map +1 -1
  31. package/dist/cli/engineBootstrap.d.ts +1 -1
  32. package/dist/cli/engineBootstrap.js +2 -0
  33. package/dist/cli/engineBootstrap.js.map +1 -1
  34. package/dist/cli/evalCommands.js +13 -1
  35. package/dist/cli/evalCommands.js.map +1 -1
  36. package/dist/cli/phaseCommands.d.ts +81 -1
  37. package/dist/cli/phaseCommands.js +465 -31
  38. package/dist/cli/phaseCommands.js.map +1 -1
  39. package/dist/cli/runtimeSkillCommands.js +12 -2
  40. package/dist/cli/runtimeSkillCommands.js.map +1 -1
  41. package/dist/cli/shieldCommands.d.ts +1 -0
  42. package/dist/cli/shieldCommands.js +20 -7
  43. package/dist/cli/shieldCommands.js.map +1 -1
  44. package/dist/cli/workflowEvidenceCommands.d.ts +120 -0
  45. package/dist/cli/workflowEvidenceCommands.js +228 -2
  46. package/dist/cli/workflowEvidenceCommands.js.map +1 -1
  47. package/dist/cortex/AutoFixEventObservations.d.ts +11 -0
  48. package/dist/cortex/AutoFixEventObservations.js +72 -0
  49. package/dist/cortex/AutoFixEventObservations.js.map +1 -0
  50. package/dist/cortex/GateEvidenceObservations.d.ts +22 -0
  51. package/dist/cortex/GateEvidenceObservations.js +179 -0
  52. package/dist/cortex/GateEvidenceObservations.js.map +1 -0
  53. package/dist/cortex/GovernanceMetrics.d.ts +2 -0
  54. package/dist/cortex/GovernanceMetrics.js +112 -22
  55. package/dist/cortex/GovernanceMetrics.js.map +1 -1
  56. package/dist/cortex/InstinctApplicationRecorder.d.ts +28 -0
  57. package/dist/cortex/InstinctApplicationRecorder.js +145 -0
  58. package/dist/cortex/InstinctApplicationRecorder.js.map +1 -0
  59. package/dist/cortex/InstinctCandidateAudit.d.ts +3 -0
  60. package/dist/cortex/InstinctCandidateAudit.js +39 -0
  61. package/dist/cortex/InstinctCandidateAudit.js.map +1 -0
  62. package/dist/cortex/InstinctCandidateReview.d.ts +32 -0
  63. package/dist/cortex/InstinctCandidateReview.js +125 -0
  64. package/dist/cortex/InstinctCandidateReview.js.map +1 -0
  65. package/dist/cortex/InstinctExtractor.d.ts +1 -0
  66. package/dist/cortex/InstinctExtractor.js +24 -17
  67. package/dist/cortex/InstinctExtractor.js.map +1 -1
  68. package/dist/cortex/InstinctRuntimeEvidence.d.ts +14 -0
  69. package/dist/cortex/InstinctRuntimeEvidence.js +120 -0
  70. package/dist/cortex/InstinctRuntimeEvidence.js.map +1 -0
  71. package/dist/cortex/InstinctStore.d.ts +50 -4
  72. package/dist/cortex/InstinctStore.js +262 -48
  73. package/dist/cortex/InstinctStore.js.map +1 -1
  74. package/dist/cortex/InstinctValidation.d.ts +9 -0
  75. package/dist/cortex/InstinctValidation.js +55 -0
  76. package/dist/cortex/InstinctValidation.js.map +1 -0
  77. package/dist/cortex/SessionInjector.d.ts +1 -0
  78. package/dist/cortex/SessionInjector.js +28 -8
  79. package/dist/cortex/SessionInjector.js.map +1 -1
  80. package/dist/dashboard/DashboardServer.d.ts +79 -0
  81. package/dist/dashboard/DashboardServer.js +330 -6
  82. package/dist/dashboard/DashboardServer.js.map +1 -1
  83. package/dist/dashboard/spa/app.js +515 -0
  84. package/dist/dashboard/spa/components/DataTable.js +53 -0
  85. package/dist/dashboard/spa/components/EventStream.js +66 -0
  86. package/dist/dashboard/spa/components/LoadingState.js +39 -0
  87. package/dist/dashboard/spa/components/MetricCard.js +30 -0
  88. package/dist/dashboard/spa/components/Panel.js +27 -0
  89. package/dist/dashboard/spa/components/StatusBadge.js +51 -0
  90. package/dist/dashboard/spa/i18n.js +767 -0
  91. package/dist/dashboard/spa/index.html +463 -0
  92. package/dist/dashboard/spa/pages/costs.js +522 -0
  93. package/dist/dashboard/spa/pages/documents.js +540 -0
  94. package/dist/dashboard/spa/pages/knowledge.js +457 -0
  95. package/dist/dashboard/spa/pages/monitoring.js +361 -0
  96. package/dist/dashboard/spa/pages/overview.js +301 -0
  97. package/dist/dashboard/spa/pages/topology-renderers.js +251 -0
  98. package/dist/dashboard/spa/pages/topology.js +370 -0
  99. package/dist/dashboard/spa/pages/workflow-renderers.js +239 -0
  100. package/dist/dashboard/spa/pages/workflow.js +217 -0
  101. package/dist/env/EnvironmentDoctor.js +12 -7
  102. package/dist/env/EnvironmentDoctor.js.map +1 -1
  103. package/dist/eval/BenchmarkPublisher.d.ts +2 -0
  104. package/dist/eval/BenchmarkPublisher.js +43 -0
  105. package/dist/eval/BenchmarkPublisher.js.map +1 -1
  106. package/dist/eval/WorkflowEval.d.ts +9 -0
  107. package/dist/eval/WorkflowEval.js +348 -2
  108. package/dist/eval/WorkflowEval.js.map +1 -1
  109. package/dist/guardrails/ast/confirmers.d.ts +18 -0
  110. package/dist/guardrails/ast/confirmers.js +69 -0
  111. package/dist/guardrails/ast/confirmers.js.map +1 -0
  112. package/dist/guardrails/ast/parse.d.ts +20 -0
  113. package/dist/guardrails/ast/parse.js +51 -0
  114. package/dist/guardrails/ast/parse.js.map +1 -0
  115. package/dist/memory/MemoryBrain.d.ts +13 -0
  116. package/dist/memory/MemoryBrain.js +47 -0
  117. package/dist/memory/MemoryBrain.js.map +1 -1
  118. package/dist/memory/MemoryFabric.d.ts +1 -0
  119. package/dist/memory/MemoryFabric.js +12 -8
  120. package/dist/memory/MemoryFabric.js.map +1 -1
  121. package/dist/memory/MemoryLearning.d.ts +1 -0
  122. package/dist/memory/MemoryLearning.js +6 -3
  123. package/dist/memory/MemoryLearning.js.map +1 -1
  124. package/dist/memory/MemoryProviders.d.ts +8 -1
  125. package/dist/memory/MemoryProviders.js +143 -29
  126. package/dist/memory/MemoryProviders.js.map +1 -1
  127. package/dist/output/HTMLDocumentRenderer.d.ts +9 -0
  128. package/dist/output/HTMLDocumentRenderer.js +19 -0
  129. package/dist/output/HTMLDocumentRenderer.js.map +1 -1
  130. package/dist/review/FreshContextVerifier.d.ts +35 -0
  131. package/dist/review/FreshContextVerifier.js +120 -0
  132. package/dist/review/FreshContextVerifier.js.map +1 -0
  133. package/dist/review/JsonLlmClient.d.ts +37 -0
  134. package/dist/review/JsonLlmClient.js +94 -0
  135. package/dist/review/JsonLlmClient.js.map +1 -0
  136. package/dist/review/LlmJudge.d.ts +61 -0
  137. package/dist/review/LlmJudge.js +167 -0
  138. package/dist/review/LlmJudge.js.map +1 -0
  139. package/dist/runtime/AiOsRuntime.d.ts +14 -1
  140. package/dist/runtime/AiOsRuntime.js +59 -3
  141. package/dist/runtime/AiOsRuntime.js.map +1 -1
  142. package/dist/runtime/RuntimeDoctor.js +3 -1
  143. package/dist/runtime/RuntimeDoctor.js.map +1 -1
  144. package/dist/runtime/RuntimeEvidenceLedger.d.ts +6 -0
  145. package/dist/runtime/RuntimeEvidenceLedger.js +52 -1
  146. package/dist/runtime/RuntimeEvidenceLedger.js.map +1 -1
  147. package/dist/runtime/SessionLedger.d.ts +2 -0
  148. package/dist/runtime/SessionLedger.js +4 -0
  149. package/dist/runtime/SessionLedger.js.map +1 -1
  150. package/dist/setup/SetupVerification.js +53 -5
  151. package/dist/setup/SetupVerification.js.map +1 -1
  152. package/dist/shield/PolicyCompiler.js +73 -12
  153. package/dist/shield/PolicyCompiler.js.map +1 -1
  154. package/dist/shield/ProtectedPaths.js +4 -2
  155. package/dist/shield/ProtectedPaths.js.map +1 -1
  156. package/dist/skills/SkillCatalog.d.ts +2 -0
  157. package/dist/skills/SkillCatalog.js +8 -0
  158. package/dist/skills/SkillCatalog.js.map +1 -1
  159. package/dist/skills/SkillDoctor.d.ts +19 -2
  160. package/dist/skills/SkillDoctor.js +163 -13
  161. package/dist/skills/SkillDoctor.js.map +1 -1
  162. package/dist/tools/SafeCommandRunner.d.ts +1 -0
  163. package/dist/tools/SafeCommandRunner.js +1 -0
  164. package/dist/tools/SafeCommandRunner.js.map +1 -1
  165. package/dist/tools/ToolCapabilityRegistry.js +25 -3
  166. package/dist/tools/ToolCapabilityRegistry.js.map +1 -1
  167. package/dist/tools/ToolOrchestrator.js +21 -0
  168. package/dist/tools/ToolOrchestrator.js.map +1 -1
  169. package/dist/version.d.ts +1 -1
  170. package/dist/version.js +1 -1
  171. package/dist/workflow/AgentLoopReadiness.d.ts +103 -0
  172. package/dist/workflow/AgentLoopReadiness.js +371 -0
  173. package/dist/workflow/AgentLoopReadiness.js.map +1 -0
  174. package/dist/workflow/BoundaryEnforcement.d.ts +60 -0
  175. package/dist/workflow/BoundaryEnforcement.js +182 -0
  176. package/dist/workflow/BoundaryEnforcement.js.map +1 -0
  177. package/dist/workflow/EcosystemReadinessGate.d.ts +46 -0
  178. package/dist/workflow/EcosystemReadinessGate.js +126 -0
  179. package/dist/workflow/EcosystemReadinessGate.js.map +1 -0
  180. package/dist/workflow/EngineeringStandards.js +67 -12
  181. package/dist/workflow/EngineeringStandards.js.map +1 -1
  182. package/dist/workflow/GateCatalog.js +21 -2
  183. package/dist/workflow/GateCatalog.js.map +1 -1
  184. package/dist/workflow/GovernanceTemplatePacks.js +2 -26
  185. package/dist/workflow/GovernanceTemplatePacks.js.map +1 -1
  186. package/dist/workflow/GovernanceTemplates.js +8 -1
  187. package/dist/workflow/GovernanceTemplates.js.map +1 -1
  188. package/dist/workflow/ProfileEnforcement.d.ts +7 -0
  189. package/dist/workflow/ProfileEnforcement.js +12 -0
  190. package/dist/workflow/ProfileEnforcement.js.map +1 -0
  191. package/dist/workflow/ReleaseDeploymentLedger.d.ts +63 -0
  192. package/dist/workflow/ReleaseDeploymentLedger.js +154 -0
  193. package/dist/workflow/ReleaseDeploymentLedger.js.map +1 -0
  194. package/dist/workflow/ReviewAnalyzer.js +50 -3
  195. package/dist/workflow/ReviewAnalyzer.js.map +1 -1
  196. package/dist/workflow/ReviewStore.d.ts +10 -0
  197. package/dist/workflow/ReviewStore.js.map +1 -1
  198. package/dist/workflow/SessionPreamble.d.ts +7 -0
  199. package/dist/workflow/SessionPreamble.js +48 -9
  200. package/dist/workflow/SessionPreamble.js.map +1 -1
  201. package/dist/workflow/SurfaceCoverage.d.ts +19 -0
  202. package/dist/workflow/SurfaceCoverage.js +57 -0
  203. package/dist/workflow/SurfaceCoverage.js.map +1 -0
  204. package/dist/workflow/VerificationCommands.d.ts +1 -0
  205. package/dist/workflow/VerificationCommands.js.map +1 -1
  206. package/dist/workflow/VerificationProfile.d.ts +5 -0
  207. package/dist/workflow/VerificationProfile.js +26 -0
  208. package/dist/workflow/VerificationProfile.js.map +1 -1
  209. package/dist/workflow/VerificationSchema.d.ts +3 -0
  210. package/dist/workflow/VerificationSchema.js +6 -0
  211. package/dist/workflow/VerificationSchema.js.map +1 -1
  212. package/dist/workflow/WorkflowEffectiveness.d.ts +97 -0
  213. package/dist/workflow/WorkflowEffectiveness.js +302 -0
  214. package/dist/workflow/WorkflowEffectiveness.js.map +1 -0
  215. package/dist/workflow/WorkflowEffectivenessRenderer.d.ts +2 -0
  216. package/dist/workflow/WorkflowEffectivenessRenderer.js +67 -0
  217. package/dist/workflow/WorkflowEffectivenessRenderer.js.map +1 -0
  218. package/dist/workflow/WorkflowEffectivenessScoring.d.ts +6 -0
  219. package/dist/workflow/WorkflowEffectivenessScoring.js +243 -0
  220. package/dist/workflow/WorkflowEffectivenessScoring.js.map +1 -0
  221. package/dist/workflow/gates/EnhancedGates.js +2 -0
  222. package/dist/workflow/gates/EnhancedGates.js.map +1 -1
  223. package/dist/workflow/gates/GateSystem.d.ts +16 -0
  224. package/dist/workflow/gates/GateSystem.js +208 -41
  225. package/dist/workflow/gates/GateSystem.js.map +1 -1
  226. package/dist/workflow/gates/MetaGovernanceGates.js +269 -8
  227. package/dist/workflow/gates/MetaGovernanceGates.js.map +1 -1
  228. package/dist/workflow/gates/TestIntegrityGate.d.ts +51 -0
  229. package/dist/workflow/gates/TestIntegrityGate.js +175 -0
  230. package/dist/workflow/gates/TestIntegrityGate.js.map +1 -0
  231. package/dist/workflow/types.d.ts +1 -1
  232. package/docs/guides/DEVELOPMENT_WORKFLOW.md +28 -0
  233. package/docs/reference/cli.md +2 -1
  234. package/docs/start/agent-governance-demo.md +1 -1
  235. package/docs/workflow/E2E_EXAMPLE.md +133 -0
  236. package/docs/workflow/README.md +7 -1
  237. package/docs/workflow/TEMPLATE_GUIDE.md +162 -0
  238. package/docs/workflow/templates/github-actions-scale-preflight.yml +4 -1
  239. package/docs/workflow/templates/plan.md +26 -0
  240. package/docs/workflow/templates/spec.md +28 -0
  241. package/package.json +7 -3
  242. package/scripts/workflow/run-vitest.mjs +123 -0
@@ -0,0 +1,182 @@
1
+ // SCALE Engine - P0+ Boundary & Constraint enforcement
2
+ //
3
+ // Two checks layered on top of the P0 six-element Spec contract:
4
+ //
5
+ // 1. Boundary enforcement — compares the files a task actually changed against
6
+ // the Spec's executional `boundaries` (allowed `files` globs + explicitly
7
+ // `forbidden` globs). Touching a forbidden path, or editing a file outside
8
+ // the declared allow-list, is reported as a violation.
9
+ //
10
+ // 2. Constraint coverage — checks each declared `constraint` (an invariant that
11
+ // must not regress) against the Spec's `verificationSurface`. A constraint
12
+ // with no surface guarding it is a silent-regression risk.
13
+ //
14
+ // Enforcement is profile-gated (mirrors G23's E1 escalation): under `default`
15
+ // and `auto` both checks are advisory — violations are surfaced as warnings and
16
+ // recorded as evidence but never flip `passed` or block ship. Under an enforced
17
+ // profile (`full`/`ci`/`strict`), the same findings are blocking: any boundary
18
+ // violation or unguarded constraint stops Task completion. The `enforced` flag
19
+ // threaded into the evaluators only sets the report's `advisory` mode; the
20
+ // detection logic is identical, so a re-verify under `default` cannot silence a
21
+ // finding, only downgrade how it is reported.
22
+ import { isEnforcedProfile } from './ProfileEnforcement.js';
23
+ /**
24
+ * Profiles in which boundary/constraint findings hard-block Task completion
25
+ * (decision E1, shared with G23). `default`/`auto` stay advisory; a profile
26
+ * whose name is or ends with `full`/`ci`/`strict` enforces.
27
+ */
28
+ export function isEnforcedBoundaryProfile(profileName) {
29
+ return isEnforcedProfile(profileName);
30
+ }
31
+ export { isEnforcedProfile };
32
+ function norm(value) {
33
+ return value.replace(/\\/g, '/').trim().toLowerCase();
34
+ }
35
+ /**
36
+ * Translate a minimatch-style glob to an anchored, full-path RegExp.
37
+ * `**` matches across `/`, `*` matches within a segment, `?` matches one
38
+ * non-`/` char. All other regex metacharacters are escaped.
39
+ */
40
+ function globToRegExp(glob) {
41
+ const body = glob
42
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape specials, leaving * and ?
43
+ .replace(/\*\*/g, '\u0000') // globstar placeholder
44
+ .replace(/\*/g, '[^/]*')
45
+ .replace(/\u0000/g, '.*')
46
+ .replace(/\?/g, '[^/]');
47
+ return new RegExp(`^${body}$`);
48
+ }
49
+ /**
50
+ * Does a (normalised) changed-file path match a boundary glob?
51
+ * Supports exact paths, bare directory prefixes (`src` ⇒ `src/**`) and globs.
52
+ */
53
+ export function pathMatchesGlob(file, glob) {
54
+ const f = norm(file);
55
+ const g = norm(glob);
56
+ if (!f || !g)
57
+ return false;
58
+ if (f === g)
59
+ return true;
60
+ // Bare directory (no wildcards) acts as a recursive prefix.
61
+ if (!/[*?]/.test(g)) {
62
+ const dir = g.endsWith('/') ? g : `${g}/`;
63
+ if (f.startsWith(dir))
64
+ return true;
65
+ }
66
+ try {
67
+ return globToRegExp(g).test(f);
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ /**
74
+ * Compare the changed-file set against a Spec's executional boundaries.
75
+ * Returns `undefined` when there is nothing to enforce (no boundaries, or
76
+ * neither `files` nor `forbidden` declared).
77
+ */
78
+ export function evaluateBoundaries(changedFiles, boundaries, enforced = false) {
79
+ if (!boundaries)
80
+ return undefined;
81
+ const allowed = (boundaries.files ?? []).map(norm).filter(Boolean);
82
+ const forbidden = (boundaries.forbidden ?? []).map(norm).filter(Boolean);
83
+ if (allowed.length === 0 && forbidden.length === 0)
84
+ return undefined;
85
+ const files = [...new Set((changedFiles ?? []).map(f => norm(f ?? '')).filter(Boolean))];
86
+ const violations = [];
87
+ for (const file of files) {
88
+ const hitForbidden = forbidden.find(glob => pathMatchesGlob(file, glob));
89
+ if (hitForbidden) {
90
+ violations.push({ file, kind: 'forbidden-touched', matchedGlob: hitForbidden });
91
+ continue;
92
+ }
93
+ if (allowed.length > 0 && !allowed.some(glob => pathMatchesGlob(file, glob))) {
94
+ violations.push({ file, kind: 'outside-allowed' });
95
+ }
96
+ }
97
+ return {
98
+ declaredAllowed: allowed.length,
99
+ declaredForbidden: forbidden.length,
100
+ changedFiles: files.length,
101
+ violations,
102
+ advisory: !enforced,
103
+ };
104
+ }
105
+ function significantTokens(value) {
106
+ return [
107
+ ...new Set(norm(value)
108
+ .split(/[^a-z0-9]+/)
109
+ .filter(token => token.length >= 4)),
110
+ ];
111
+ }
112
+ /**
113
+ * A constraint is "covered" when at least one of its significant tokens appears
114
+ * in some verificationSurface item — i.e. there is a declared check that could
115
+ * catch a regression of that invariant.
116
+ */
117
+ function constraintCovered(constraint, surfaces) {
118
+ const tokens = significantTokens(constraint);
119
+ if (tokens.length === 0)
120
+ return false;
121
+ return surfaces.some(surface => tokens.some(token => surface.includes(token)));
122
+ }
123
+ /**
124
+ * Check that every declared constraint is guarded by some verificationSurface
125
+ * item. Returns `undefined` when no constraints are declared.
126
+ */
127
+ export function evaluateConstraints(constraints, verificationSurface, enforced = false) {
128
+ const declared = (constraints ?? []).map(c => c.trim()).filter(Boolean);
129
+ if (declared.length === 0)
130
+ return undefined;
131
+ const surfaces = (verificationSurface ?? []).map(norm).filter(Boolean);
132
+ const uncovered = declared.filter(constraint => !constraintCovered(constraint, surfaces));
133
+ return {
134
+ declared: declared.length,
135
+ covered: declared.length - uncovered.length,
136
+ uncovered,
137
+ advisory: !enforced,
138
+ };
139
+ }
140
+ /**
141
+ * Count the findings that block Task completion under an enforced profile:
142
+ * every boundary violation plus every unguarded constraint. Returns 0 when both
143
+ * reports are clean or undefined. The caller decides whether to act on the
144
+ * count based on the active profile (advisory reports are not blocked even when
145
+ * this count is non-zero).
146
+ */
147
+ export function countBoundaryBlockers(boundary, constraint) {
148
+ return (boundary?.violations.length ?? 0) + (constraint?.uncovered.length ?? 0);
149
+ }
150
+ /** Render boundary warning lines (empty when there is nothing to warn about). */
151
+ export function formatBoundaryWarnings(report) {
152
+ if (!report || report.violations.length === 0)
153
+ return [];
154
+ const tag = report.advisory ? '[WARN]' : '[BLOCKER]';
155
+ const mode = report.advisory ? 'advisory, not blocking' : 'blocking under enforced profile';
156
+ const lines = [
157
+ `${tag} boundary enforcement: ${report.violations.length} violation(s) (${mode})`,
158
+ ];
159
+ for (const v of report.violations) {
160
+ lines.push(v.kind === 'forbidden-touched'
161
+ ? ` [FORBIDDEN] ${v.file} (matched ${v.matchedGlob})`
162
+ : ` [OUTSIDE-ALLOWED] ${v.file}`);
163
+ }
164
+ lines.push(' Keep edits inside the Spec boundaries (or widen them in the Spec).');
165
+ return lines;
166
+ }
167
+ /** Render constraint-coverage warning lines (empty when fully covered). */
168
+ export function formatConstraintWarnings(report) {
169
+ if (!report || report.uncovered.length === 0)
170
+ return [];
171
+ const tag = report.advisory ? '[WARN]' : '[BLOCKER]';
172
+ const mode = report.advisory ? 'advisory, not blocking' : 'blocking under enforced profile';
173
+ const lines = [
174
+ `${tag} constraint coverage: ${report.covered}/${report.declared} guarded by verificationSurface (${mode})`,
175
+ ];
176
+ for (const constraint of report.uncovered) {
177
+ lines.push(` [UNGUARDED] ${constraint}`);
178
+ }
179
+ lines.push(' Add a verificationSurface item that would catch a regression of each constraint.');
180
+ return lines;
181
+ }
182
+ //# sourceMappingURL=BoundaryEnforcement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoundaryEnforcement.js","sourceRoot":"","sources":["../../src/workflow/BoundaryEnforcement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { verifySetup, type SetupVerificationReport } from '../setup/SetupVerification.js';
2
+ import { inspectWorkflowSkills, type SkillDoctorReport } from '../skills/SkillDoctor.js';
3
+ import type { VerificationPolicy } from './VerificationProfile.js';
4
+ export type EcosystemReadinessGateMode = 'off' | 'warn' | 'block';
5
+ export type EcosystemReadinessSkillScope = 'required' | 'recommended' | 'all';
6
+ export interface EcosystemReadinessGateReport {
7
+ mode: EcosystemReadinessGateMode;
8
+ checked: boolean;
9
+ ok: boolean;
10
+ blocked: boolean;
11
+ packIds: string[];
12
+ summary: {
13
+ blockingIssues: string[];
14
+ warningCount: number;
15
+ installedTools: number;
16
+ totalTools: number;
17
+ missingTools: string[];
18
+ installedWorkflowSkills: number;
19
+ totalWorkflowSkills: number;
20
+ missingWorkflowSkills: string[];
21
+ missingRequiredWorkflowSkills: string[];
22
+ missingRecommendedWorkflowSkills: string[];
23
+ missingOptionalWorkflowSkills: string[];
24
+ blockingMissingWorkflowSkills: string[];
25
+ skillScope: EcosystemReadinessSkillScope;
26
+ };
27
+ setup?: SetupVerificationReport;
28
+ skillDoctor?: SkillDoctorReport;
29
+ warnings: string[];
30
+ recommendations: string[];
31
+ }
32
+ export interface EcosystemReadinessGateOptions {
33
+ projectDir: string;
34
+ scaleDir: string;
35
+ policy: VerificationPolicy;
36
+ checked?: boolean;
37
+ skipReason?: string;
38
+ deps?: {
39
+ verifySetup?: typeof verifySetup;
40
+ inspectWorkflowSkills?: typeof inspectWorkflowSkills;
41
+ };
42
+ }
43
+ export declare function evaluateEcosystemReadinessGate(options: EcosystemReadinessGateOptions): Promise<EcosystemReadinessGateReport>;
44
+ export declare function normalizeEcosystemReadinessGateMode(value: unknown): EcosystemReadinessGateMode;
45
+ export declare function normalizeEcosystemReadinessPacks(value: unknown): string[];
46
+ export declare function normalizeEcosystemReadinessSkillScope(value: unknown): EcosystemReadinessSkillScope;
@@ -0,0 +1,126 @@
1
+ import { verifySetup } from '../setup/SetupVerification.js';
2
+ import { inspectWorkflowSkills } from '../skills/SkillDoctor.js';
3
+ const DEFAULT_ECOSYSTEM_PACKS = ['full'];
4
+ const VALID_ECOSYSTEM_PACKS = new Set(['ui', 'memory', 'knowledge', 'external-cli', 'full']);
5
+ export async function evaluateEcosystemReadinessGate(options) {
6
+ const mode = normalizeEcosystemReadinessGateMode(options.policy.ecosystemReadinessGate);
7
+ const packIds = normalizeEcosystemReadinessPacks(options.policy.ecosystemReadinessPacks);
8
+ const skillScope = normalizeEcosystemReadinessSkillScope(options.policy.ecosystemReadinessSkillScope);
9
+ if (mode === 'off' || options.checked === false) {
10
+ const reason = options.skipReason ?? (mode === 'off' ? 'ecosystem readiness gate is disabled by policy' : 'ecosystem readiness gate skipped for this preflight profile');
11
+ return {
12
+ mode,
13
+ checked: false,
14
+ ok: true,
15
+ blocked: false,
16
+ packIds,
17
+ summary: {
18
+ blockingIssues: [],
19
+ warningCount: 0,
20
+ installedTools: 0,
21
+ totalTools: 0,
22
+ missingTools: [],
23
+ installedWorkflowSkills: 0,
24
+ totalWorkflowSkills: 0,
25
+ missingWorkflowSkills: [],
26
+ missingRequiredWorkflowSkills: [],
27
+ missingRecommendedWorkflowSkills: [],
28
+ missingOptionalWorkflowSkills: [],
29
+ blockingMissingWorkflowSkills: [],
30
+ skillScope,
31
+ },
32
+ warnings: [reason],
33
+ recommendations: [],
34
+ };
35
+ }
36
+ const setup = await (options.deps?.verifySetup ?? verifySetup)({
37
+ projectDir: options.projectDir,
38
+ scaleDir: options.scaleDir,
39
+ packIds,
40
+ });
41
+ const skillDoctor = (options.deps?.inspectWorkflowSkills ?? inspectWorkflowSkills)({
42
+ projectDir: options.projectDir,
43
+ scaleDir: options.scaleDir,
44
+ });
45
+ const missingTools = setup.toolCapabilities.tools
46
+ .filter(tool => !tool.installed)
47
+ .map(tool => tool.id);
48
+ const missingWorkflowSkills = skillDoctor.skills
49
+ .filter(skill => skill.status === 'missing')
50
+ .map(skill => skill.id);
51
+ const missingRequiredWorkflowSkills = skillDoctor.missingByReadiness.required;
52
+ const missingRecommendedWorkflowSkills = skillDoctor.missingByReadiness.recommended;
53
+ const missingOptionalWorkflowSkills = skillDoctor.missingByReadiness.optional;
54
+ const blockingMissingWorkflowSkills = missingWorkflowSkillsForScope(skillDoctor, skillScope);
55
+ const workflowSkillsReadyForScope = blockingMissingWorkflowSkills.length === 0;
56
+ const blockingMissingWorkflowSkillSet = new Set(blockingMissingWorkflowSkills);
57
+ const nonBlockingMissingWorkflowSkills = missingWorkflowSkills
58
+ .filter(skill => !blockingMissingWorkflowSkillSet.has(skill));
59
+ const blockingIssues = [
60
+ ...setup.summary.blockingIssues,
61
+ ...(workflowSkillsReadyForScope ? [] : [`Missing ${skillScope} workflow skills: ${blockingMissingWorkflowSkills.join(', ')}`]),
62
+ ];
63
+ const ok = setup.ok && workflowSkillsReadyForScope;
64
+ const warnings = [
65
+ ...setup.warnings,
66
+ ...(setup.ok ? [] : [`Setup readiness is not OK for pack(s): ${packIds.join(', ')}`]),
67
+ ...(workflowSkillsReadyForScope ? [] : [`Workflow skill readiness is not OK for ${skillScope} scope: ${blockingMissingWorkflowSkills.join(', ')}`]),
68
+ ...(nonBlockingMissingWorkflowSkills.length === 0 ? [] : [`Non-blocking workflow skills missing: ${nonBlockingMissingWorkflowSkills.join(', ')}`]),
69
+ ];
70
+ return {
71
+ mode,
72
+ checked: true,
73
+ ok,
74
+ blocked: mode === 'block' && (!setup.ok || !workflowSkillsReadyForScope),
75
+ packIds,
76
+ summary: {
77
+ blockingIssues,
78
+ warningCount: warnings.length,
79
+ installedTools: setup.summary.installedTools,
80
+ totalTools: setup.summary.totalTools,
81
+ missingTools,
82
+ installedWorkflowSkills: skillDoctor.installed,
83
+ totalWorkflowSkills: skillDoctor.total,
84
+ missingWorkflowSkills,
85
+ missingRequiredWorkflowSkills,
86
+ missingRecommendedWorkflowSkills,
87
+ missingOptionalWorkflowSkills,
88
+ blockingMissingWorkflowSkills,
89
+ skillScope,
90
+ },
91
+ setup,
92
+ skillDoctor,
93
+ warnings,
94
+ recommendations: setup.recommendations,
95
+ };
96
+ }
97
+ export function normalizeEcosystemReadinessGateMode(value) {
98
+ if (value === 'off' || value === 'warn' || value === 'block')
99
+ return value;
100
+ return 'warn';
101
+ }
102
+ export function normalizeEcosystemReadinessPacks(value) {
103
+ if (!Array.isArray(value))
104
+ return [...DEFAULT_ECOSYSTEM_PACKS];
105
+ const packs = value
106
+ .map(item => String(item).trim())
107
+ .filter(item => VALID_ECOSYSTEM_PACKS.has(item));
108
+ return packs.length > 0 ? [...new Set(packs)] : [...DEFAULT_ECOSYSTEM_PACKS];
109
+ }
110
+ export function normalizeEcosystemReadinessSkillScope(value) {
111
+ if (value === 'required' || value === 'recommended' || value === 'all')
112
+ return value;
113
+ return 'required';
114
+ }
115
+ function missingWorkflowSkillsForScope(skillDoctor, scope) {
116
+ if (scope === 'all')
117
+ return skillDoctor.skills.filter(skill => skill.status === 'missing').map(skill => skill.id);
118
+ if (scope === 'recommended') {
119
+ return [
120
+ ...skillDoctor.missingByReadiness.required,
121
+ ...skillDoctor.missingByReadiness.recommended,
122
+ ];
123
+ }
124
+ return skillDoctor.missingByReadiness.required;
125
+ }
126
+ //# sourceMappingURL=EcosystemReadinessGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EcosystemReadinessGate.js","sourceRoot":"","sources":["../../src/workflow/EcosystemReadinessGate.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
+ import { createAstConfirmer } from '../guardrails/ast/confirmers.js';
3
4
  const DEFAULT_SOURCE_DIRECTORIES = ['src', 'app', 'packages', 'services', 'cmd', 'internal', 'pkg'];
4
5
  const DEFAULT_IGNORED_DIRECTORIES = [
5
6
  '.git',
@@ -379,6 +380,11 @@ function scanFile(projectDir, absolutePath, policy, frameworks) {
379
380
  const content = readFileSync(absolutePath, 'utf-8');
380
381
  const lines = content.split(/\r?\n/);
381
382
  const findings = [];
383
+ // AST confirmation layer (P1.1): parse once per file so the regex pre-filter
384
+ // hits below can be confirmed against the real AST. null = parse failed, in
385
+ // which case every confirmer falls back to the raw regex result.
386
+ const ext = extname(absolutePath);
387
+ const astConfirmer = createAstConfirmer(content, { jsx: ext === '.tsx' || ext === '.jsx' });
382
388
  if (lines.length > policy.maxFileLines) {
383
389
  findings.push({
384
390
  severity: 'warn',
@@ -394,15 +400,15 @@ function scanFile(projectDir, absolutePath, policy, frameworks) {
394
400
  const line = lines[index];
395
401
  const startedInsideTemplateLiteral = insideTemplateLiteral;
396
402
  insideTemplateLiteral = updateTemplateLiteralState(line, insideTemplateLiteral);
397
- if (startedInsideTemplateLiteral || isNonExecutablePatternLine(line))
403
+ if (startedInsideTemplateLiteral || isNonExecutablePatternLine(line) || isSecurityRuleDefinitionLine(path, line))
398
404
  continue;
399
405
  const lineNumber = index + 1;
400
- findings.push(...scanLine(path, line, lineNumber, policy, frameworks));
406
+ findings.push(...scanLine(path, line, lineNumber, policy, frameworks, astConfirmer));
401
407
  }
402
- findings.push(...findEmptyCatchBlocks(path, lines));
408
+ findings.push(...findEmptyCatchBlocks(path, lines, astConfirmer));
403
409
  return dedupeFindings(findings);
404
410
  }
405
- function scanLine(path, line, lineNumber, policy, frameworks) {
411
+ function scanLine(path, line, lineNumber, policy, frameworks, ast) {
406
412
  const findings = [];
407
413
  const sensitiveMatcher = sensitiveFieldPattern(policy);
408
414
  const evidence = line.trim().slice(0, 160);
@@ -467,7 +473,7 @@ function scanLine(path, line, lineNumber, policy, frameworks) {
467
473
  fix: 'Use text rendering or sanitize trusted HTML with an approved sanitizer.',
468
474
  });
469
475
  }
470
- if (/\beval\s*\(|new\s+Function\s*\(/.test(line)) {
476
+ if (/\beval\s*\(|new\s+Function\s*\(/.test(line) && (!ast || ast.hasUnsafeCodeExecution(lineNumber))) {
471
477
  findings.push({
472
478
  severity: 'fail',
473
479
  category: 'security',
@@ -479,7 +485,7 @@ function scanLine(path, line, lineNumber, policy, frameworks) {
479
485
  fix: 'Replace eval or Function with a typed parser, dispatch table, or safe interpreter.',
480
486
  });
481
487
  }
482
- if (/^\s*(?:\/\/|\/\*)\s*@ts-ignore\b/.test(line)) {
488
+ if (/^\s*(?:\/\/|\/\*)\s*@ts-ignore\b/.test(line) && (!ast || ast.hasTsIgnore(lineNumber))) {
483
489
  findings.push({
484
490
  severity: 'fail',
485
491
  category: 'code-quality',
@@ -491,7 +497,7 @@ function scanLine(path, line, lineNumber, policy, frameworks) {
491
497
  fix: 'Fix the type boundary or use a narrow typed adapter with a documented reason.',
492
498
  });
493
499
  }
494
- if (/\bas\s+any\b|:\s*any\b|<any\b|Array<any>|Promise<any>|Record<[^>]+,\s*any>/.test(line)) {
500
+ if (/\bas\s+any\b|:\s*any\b|<any\b|Array<any>|Promise<any>|Record<[^>]+,\s*any>/.test(line) && (!ast || ast.hasAnyType(lineNumber))) {
495
501
  findings.push({
496
502
  severity: 'warn',
497
503
  category: 'code-quality',
@@ -529,12 +535,16 @@ function scanLine(path, line, lineNumber, policy, frameworks) {
529
535
  }
530
536
  return findings;
531
537
  }
532
- function findEmptyCatchBlocks(path, lines) {
538
+ function findEmptyCatchBlocks(path, lines, ast) {
533
539
  const findings = [];
540
+ // AST confirmation: only emit when the catch block genuinely has zero
541
+ // statements. Falls back to the regex result when the file did not parse.
542
+ const confirm = (line) => !ast || ast.hasEmptyCatch(line);
534
543
  for (let index = 0; index < lines.length; index += 1) {
535
544
  const line = lines[index];
536
545
  if (/catch\s*(?:\([^)]*\))?\s*\{\s*(?:\/\*.*?\*\/|\/\/.*)?\s*\}/.test(line)) {
537
- findings.push(emptyCatchFinding(path, index + 1, line));
546
+ if (confirm(index + 1))
547
+ findings.push(emptyCatchFinding(path, index + 1, line));
538
548
  continue;
539
549
  }
540
550
  if (!/catch\s*(?:\([^)]*\))?\s*\{\s*$/.test(line))
@@ -543,7 +553,7 @@ function findEmptyCatchBlocks(path, lines) {
543
553
  const trimmed = next.trim();
544
554
  if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*'))
545
555
  continue;
546
- if (/^}\s*[),;]?$/.test(trimmed))
556
+ if (/^}\s*[),;]?$/.test(trimmed) && confirm(index + 1))
547
557
  findings.push(emptyCatchFinding(path, index + 1, line));
548
558
  break;
549
559
  }
@@ -944,7 +954,14 @@ function sensitiveFieldPattern(policy) {
944
954
  return new RegExp(`\\b(?:${fields})\\b`, 'i');
945
955
  }
946
956
  function isLogCall(line) {
947
- return /\b(?:console\.(?:log|debug|info|warn|error)|logger\.(?:trace|debug|info|warn|error|fatal)|log(?:ger)?\.(?:trace|debug|info|warn|error|fatal)|log[A-Za-z0-9_]*\s*\()\b/.test(line);
957
+ for (const match of line.matchAll(/\b(?:console\.(?:log|debug|info|warn|error)|logger\.(?:trace|debug|info|warn|error|fatal)|log(?:ger)?\.(?:trace|debug|info|warn|error|fatal)|log[A-Za-z0-9_]*\s*\()/g)) {
958
+ if (match.index !== undefined &&
959
+ !isInsideQuotedString(line, match.index) &&
960
+ !isInsideRegexLiteral(line, match.index)) {
961
+ return true;
962
+ }
963
+ }
964
+ return false;
948
965
  }
949
966
  function isAdHocOutputCall(line) {
950
967
  for (const match of line.matchAll(/\bconsole\.(?:log|debug|info|warn|error)\s*\(|\bfmt\.Print(?:f|ln)?\s*\(|\bprint(?:ln)?\s*\(|\bSystem\.out\.print(?:ln)?\s*\(|\bConsole\.Write(?:Line)?\s*\(|\bprintln!\s*\(/g)) {
@@ -961,7 +978,16 @@ function isHardcodedSecret(line, policy) {
961
978
  const match = new RegExp(`\\b\\w*(?:${fields})\\w*\\b\\s*[:=]\\s*(['"\`])([^'"\`]{12,})\\1`, 'i').exec(line);
962
979
  if (!match)
963
980
  return false;
964
- return !/\b(example|sample|placeholder|changeme|replace-me|dummy|test-value)\b/i.test(match[2]);
981
+ return !isAllowedSecretLiteralReference(match[2]);
982
+ }
983
+ function isAllowedSecretLiteralReference(value) {
984
+ if (/\b(example|sample|placeholder|changeme|replace-me|dummy|test-value)\b/i.test(value))
985
+ return true;
986
+ if (/\b(?:process\.env|import\.meta\.env|Deno\.env|getenv)\b/.test(value))
987
+ return true;
988
+ if (value.includes('${'))
989
+ return true;
990
+ return /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(value);
965
991
  }
966
992
  function isRawSqlConstruction(line) {
967
993
  return /\b(?:query|execute|exec|raw|rawQuery)\s*\(/i.test(line) &&
@@ -994,6 +1020,35 @@ function isNonExecutablePatternLine(line) {
994
1020
  /^pattern:\s*\/.*\/[dgimsuy]*,?$/.test(trimmed) ||
995
1021
  /\(\s*\/.*\/[dgimsuy]*\.(?:test|exec)\(/.test(trimmed);
996
1022
  }
1023
+ function isSecurityRuleDefinitionLine(path, line) {
1024
+ if (!isSecurityRuleSource(path))
1025
+ return false;
1026
+ const trimmed = line.trim();
1027
+ if (/^id:\s*['"`][^'"`]+['"`],?$/.test(trimmed))
1028
+ return true;
1029
+ if (/^\{?\s*pattern:\s*\/.*\/[dgimsuy]*,?/.test(trimmed))
1030
+ return true;
1031
+ if (/^patterns:\s*\[/.test(trimmed))
1032
+ return true;
1033
+ if (/^\/.*\/[dgimsuy]*,?(?:\s*\/\/.*)?$/.test(trimmed))
1034
+ return true;
1035
+ if (/^(?:const|let|var)\s+\w*pattern\w*\s*=\s*\/.*\/[dgimsuy]*/i.test(trimmed))
1036
+ return true;
1037
+ if (/^(?:name|title|description|recommendation|remediation):\s*['"`].*(?:dangerouslySetInnerHTML|innerHTML|document\.write|eval\(|new Function|curl pipe|shell:\s*true)/i.test(trimmed))
1038
+ return true;
1039
+ return /^(?:if\s*\(|return\s+)?\/?.*(?:dangerouslySetInnerHTML|innerHTML|document\.write|eval\(|new Function|curl pipe|shell:\s*true).*\/[dgimsuy]*\.test\(/i.test(trimmed) ||
1040
+ /^(?:\/\/|\/\*\*?|\*)\s*.*(?:dangerouslySetInnerHTML|innerHTML|document\.write|eval\(|new Function|curl pipe|shell:\s*true)/i.test(trimmed);
1041
+ }
1042
+ function isSecurityRuleSource(path) {
1043
+ return [
1044
+ 'src/guardrails/advancedDetectors.ts',
1045
+ 'src/guardrails/ast/confirmers.ts',
1046
+ 'src/guardrails/OWASPDetector.ts',
1047
+ 'src/workflow/gates/GateSystem.ts',
1048
+ 'src/workflow/ReviewAnalyzer.ts',
1049
+ 'src/workflow/SecurityAudit.ts',
1050
+ ].some(ruleSource => path.endsWith(ruleSource));
1051
+ }
997
1052
  function updateTemplateLiteralState(line, currentlyInside) {
998
1053
  let inside = currentlyInside;
999
1054
  let escaped = false;