gsdd-cli 0.18.5 → 0.19.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 (101) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +610 -608
  3. package/agents/DISTILLATION.md +421 -421
  4. package/agents/README.md +62 -62
  5. package/agents/approach-explorer.md +370 -361
  6. package/agents/debugger.md +82 -82
  7. package/agents/executor.md +473 -394
  8. package/agents/integration-checker.md +318 -318
  9. package/agents/mapper.md +103 -103
  10. package/agents/planner.md +342 -313
  11. package/agents/researcher.md +84 -84
  12. package/agents/roadmapper.md +296 -296
  13. package/agents/synthesizer.md +236 -236
  14. package/agents/verifier.md +337 -337
  15. package/bin/adapters/agents.mjs +34 -34
  16. package/bin/adapters/claude.mjs +193 -191
  17. package/bin/adapters/codex.mjs +85 -85
  18. package/bin/adapters/index.mjs +20 -20
  19. package/bin/adapters/opencode.mjs +280 -278
  20. package/bin/gsdd.mjs +123 -116
  21. package/bin/lib/cli-utils.mjs +28 -28
  22. package/bin/lib/evidence-contract.mjs +325 -112
  23. package/bin/lib/file-ops.mjs +186 -144
  24. package/bin/lib/health-truth.mjs +196 -178
  25. package/bin/lib/health.mjs +246 -226
  26. package/bin/lib/init-flow.mjs +247 -231
  27. package/bin/lib/init-prompts.mjs +248 -247
  28. package/bin/lib/init-runtime.mjs +193 -190
  29. package/bin/lib/init.mjs +17 -17
  30. package/bin/lib/lifecycle-preflight.mjs +760 -326
  31. package/bin/lib/lifecycle-state.mjs +356 -267
  32. package/bin/lib/manifest.mjs +116 -114
  33. package/bin/lib/models.mjs +411 -411
  34. package/bin/lib/phase.mjs +365 -358
  35. package/bin/lib/plan-constants.mjs +35 -30
  36. package/bin/lib/provenance.mjs +109 -106
  37. package/bin/lib/rendering.mjs +119 -83
  38. package/bin/lib/runtime-freshness.mjs +214 -214
  39. package/bin/lib/session-fingerprint.mjs +91 -14
  40. package/bin/lib/templates.mjs +225 -224
  41. package/bin/lib/workspace-root.mjs +2 -1
  42. package/distilled/DESIGN.md +2461 -2323
  43. package/distilled/EVIDENCE-INDEX.md +418 -392
  44. package/distilled/README.md +196 -193
  45. package/distilled/SKILL.md +86 -85
  46. package/distilled/templates/agents.block.md +21 -21
  47. package/distilled/templates/agents.md +6 -6
  48. package/distilled/templates/approach.md +272 -232
  49. package/distilled/templates/auth-matrix.md +78 -78
  50. package/distilled/templates/brownfield-change/CHANGE.md +99 -0
  51. package/distilled/templates/brownfield-change/HANDOFF.md +38 -0
  52. package/distilled/templates/brownfield-change/VERIFICATION.md +56 -0
  53. package/distilled/templates/codebase/architecture.md +110 -110
  54. package/distilled/templates/codebase/concerns.md +95 -95
  55. package/distilled/templates/codebase/conventions.md +193 -193
  56. package/distilled/templates/codebase/stack.md +96 -96
  57. package/distilled/templates/delegates/approach-explorer.md +28 -25
  58. package/distilled/templates/delegates/mapper-arch.md +26 -26
  59. package/distilled/templates/delegates/mapper-concerns.md +27 -27
  60. package/distilled/templates/delegates/mapper-quality.md +28 -28
  61. package/distilled/templates/delegates/mapper-tech.md +25 -25
  62. package/distilled/templates/delegates/plan-checker.md +78 -68
  63. package/distilled/templates/delegates/researcher-architecture.md +30 -30
  64. package/distilled/templates/delegates/researcher-features.md +30 -30
  65. package/distilled/templates/delegates/researcher-pitfalls.md +30 -30
  66. package/distilled/templates/delegates/researcher-stack.md +30 -30
  67. package/distilled/templates/delegates/researcher-synthesizer.md +31 -31
  68. package/distilled/templates/research/architecture.md +57 -57
  69. package/distilled/templates/research/features.md +23 -23
  70. package/distilled/templates/research/pitfalls.md +46 -46
  71. package/distilled/templates/research/stack.md +45 -45
  72. package/distilled/templates/research/summary.md +67 -67
  73. package/distilled/templates/roadmap.md +74 -62
  74. package/distilled/templates/spec.md +110 -110
  75. package/distilled/workflows/audit-milestone.md +303 -271
  76. package/distilled/workflows/complete-milestone.md +349 -332
  77. package/distilled/workflows/execute.md +457 -450
  78. package/distilled/workflows/map-codebase.md +253 -253
  79. package/distilled/workflows/new-milestone.md +242 -238
  80. package/distilled/workflows/new-project.md +398 -398
  81. package/distilled/workflows/pause.md +160 -156
  82. package/distilled/workflows/plan-milestone-gaps.md +183 -183
  83. package/distilled/workflows/plan.md +454 -448
  84. package/distilled/workflows/progress.md +227 -223
  85. package/distilled/workflows/quick.md +351 -347
  86. package/distilled/workflows/resume.md +220 -212
  87. package/distilled/workflows/verify-work.md +260 -260
  88. package/distilled/workflows/verify.md +431 -429
  89. package/docs/BROWNFIELD-PROOF.md +95 -95
  90. package/docs/RUNTIME-SUPPORT.md +80 -69
  91. package/docs/USER-GUIDE.md +394 -386
  92. package/docs/VERIFICATION-DISCIPLINE.md +59 -59
  93. package/docs/claude/context-monitor.md +98 -98
  94. package/docs/proof/consumer-node-cli/README.md +37 -37
  95. package/docs/proof/consumer-node-cli/ROADMAP.md +14 -14
  96. package/docs/proof/consumer-node-cli/SPEC.md +17 -17
  97. package/docs/proof/consumer-node-cli/brief.md +9 -9
  98. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-PLAN.md +34 -34
  99. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-SUMMARY.md +10 -10
  100. package/docs/proof/consumer-node-cli/phases/01-foundation/01-VERIFICATION.md +30 -30
  101. package/package.json +62 -61
@@ -1,112 +1,325 @@
1
- const EVIDENCE_KINDS = Object.freeze(['code', 'test', 'runtime', 'delivery', 'human']);
2
- const DELIVERY_POSTURES = Object.freeze(['repo_only', 'delivery_sensitive']);
3
- const CLOSURE_SURFACES = Object.freeze(['verify', 'audit-milestone', 'complete-milestone']);
4
-
5
- const LEGACY_EVIDENCE_ALIASES = Object.freeze({
6
- code: 'code',
7
- test: 'test',
8
- runtime: 'runtime',
9
- delivery: 'delivery',
10
- human: 'human',
11
- 'code-evidence': 'code',
12
- 'repo-test': 'test',
13
- 'runtime-check': 'runtime',
14
- 'user-confirmation': 'human',
15
- });
16
-
17
- const EVIDENCE_MATRIX = Object.freeze({
18
- verify: Object.freeze({
19
- repo_only: Object.freeze({
20
- requiredKinds: Object.freeze(['code']),
21
- recommendedKinds: Object.freeze(['test']),
22
- blockedSoloKinds: Object.freeze(['human', 'delivery']),
23
- }),
24
- delivery_sensitive: Object.freeze({
25
- requiredKinds: Object.freeze(['code', 'runtime']),
26
- recommendedKinds: Object.freeze(['test', 'delivery', 'human']),
27
- blockedSoloKinds: Object.freeze(['code', 'human']),
28
- }),
29
- }),
30
- 'audit-milestone': Object.freeze({
31
- repo_only: Object.freeze({
32
- requiredKinds: Object.freeze(['code', 'test']),
33
- recommendedKinds: Object.freeze(['runtime', 'human']),
34
- blockedSoloKinds: Object.freeze(['human', 'delivery']),
35
- }),
36
- delivery_sensitive: Object.freeze({
37
- requiredKinds: Object.freeze(['code', 'test', 'runtime', 'delivery']),
38
- recommendedKinds: Object.freeze(['human']),
39
- blockedSoloKinds: Object.freeze(['code', 'human']),
40
- }),
41
- }),
42
- 'complete-milestone': Object.freeze({
43
- repo_only: Object.freeze({
44
- requiredKinds: Object.freeze(['code', 'test']),
45
- recommendedKinds: Object.freeze(['runtime']),
46
- blockedSoloKinds: Object.freeze(['human', 'delivery']),
47
- }),
48
- delivery_sensitive: Object.freeze({
49
- requiredKinds: Object.freeze(['code', 'test', 'runtime', 'delivery']),
50
- recommendedKinds: Object.freeze(['human']),
51
- blockedSoloKinds: Object.freeze(['code', 'human']),
52
- }),
53
- }),
54
- });
55
-
56
- export { CLOSURE_SURFACES, DELIVERY_POSTURES, EVIDENCE_KINDS };
57
-
58
- export function normalizeEvidenceKind(kind) {
59
- if (!kind) {
60
- return null;
61
- }
62
-
63
- return LEGACY_EVIDENCE_ALIASES[kind] ?? null;
64
- }
65
-
66
- export function normalizeEvidenceKinds(kinds = []) {
67
- const normalized = [];
68
- for (const kind of kinds) {
69
- const resolved = normalizeEvidenceKind(kind);
70
- if (resolved && !normalized.includes(resolved)) {
71
- normalized.push(resolved);
72
- }
73
- }
74
- return normalized;
75
- }
76
-
77
- export function isClosureSurface(surface) {
78
- return CLOSURE_SURFACES.includes(surface);
79
- }
80
-
81
- export function getEvidenceContract(surface, deliveryPosture) {
82
- const matrix = EVIDENCE_MATRIX[surface];
83
- if (!matrix) {
84
- throw new Error(`Unsupported closure evidence surface: ${surface}`);
85
- }
86
-
87
- const posture = matrix[deliveryPosture];
88
- if (!posture) {
89
- throw new Error(`Unsupported delivery posture for ${surface}: ${deliveryPosture}`);
90
- }
91
-
92
- return {
93
- surface,
94
- deliveryPosture,
95
- supportedKinds: [...EVIDENCE_KINDS],
96
- requiredKinds: [...posture.requiredKinds],
97
- recommendedKinds: [...posture.recommendedKinds],
98
- blockedSoloKinds: [...posture.blockedSoloKinds],
99
- };
100
- }
101
-
102
- export function describeEvidenceSurface(surface) {
103
- if (!isClosureSurface(surface)) {
104
- return null;
105
- }
106
-
107
- return {
108
- surface,
109
- supportedKinds: [...EVIDENCE_KINDS],
110
- deliveryPostures: DELIVERY_POSTURES.map((deliveryPosture) => getEvidenceContract(surface, deliveryPosture)),
111
- };
112
- }
1
+ const EVIDENCE_KINDS = Object.freeze(['code', 'test', 'runtime', 'delivery', 'human']);
2
+ const DELIVERY_POSTURES = Object.freeze(['repo_only', 'delivery_sensitive']);
3
+ const CLOSURE_SURFACES = Object.freeze(['verify', 'audit-milestone', 'complete-milestone']);
4
+ const RELEASE_CLAIM_POSTURES = Object.freeze([
5
+ 'repo_closeout',
6
+ 'runtime_validated_closeout',
7
+ 'delivery_supported_closeout',
8
+ ]);
9
+
10
+ const LEGACY_EVIDENCE_ALIASES = Object.freeze({
11
+ code: 'code',
12
+ test: 'test',
13
+ runtime: 'runtime',
14
+ delivery: 'delivery',
15
+ human: 'human',
16
+ 'code-evidence': 'code',
17
+ 'repo-test': 'test',
18
+ 'runtime-check': 'runtime',
19
+ 'user-confirmation': 'human',
20
+ });
21
+
22
+ const EVIDENCE_MATRIX = Object.freeze({
23
+ verify: Object.freeze({
24
+ repo_only: Object.freeze({
25
+ requiredKinds: Object.freeze(['code']),
26
+ recommendedKinds: Object.freeze(['test']),
27
+ blockedSoloKinds: Object.freeze(['human', 'delivery']),
28
+ }),
29
+ delivery_sensitive: Object.freeze({
30
+ requiredKinds: Object.freeze(['code', 'runtime', 'delivery']),
31
+ recommendedKinds: Object.freeze(['test', 'human']),
32
+ blockedSoloKinds: Object.freeze(['code', 'human']),
33
+ }),
34
+ }),
35
+ 'audit-milestone': Object.freeze({
36
+ repo_only: Object.freeze({
37
+ requiredKinds: Object.freeze(['code', 'test']),
38
+ recommendedKinds: Object.freeze(['runtime', 'human']),
39
+ blockedSoloKinds: Object.freeze(['human', 'delivery']),
40
+ }),
41
+ delivery_sensitive: Object.freeze({
42
+ requiredKinds: Object.freeze(['code', 'test', 'runtime', 'delivery']),
43
+ recommendedKinds: Object.freeze(['human']),
44
+ blockedSoloKinds: Object.freeze(['code', 'human']),
45
+ }),
46
+ }),
47
+ 'complete-milestone': Object.freeze({
48
+ repo_only: Object.freeze({
49
+ requiredKinds: Object.freeze(['code', 'test']),
50
+ recommendedKinds: Object.freeze(['runtime']),
51
+ blockedSoloKinds: Object.freeze(['human', 'delivery']),
52
+ }),
53
+ delivery_sensitive: Object.freeze({
54
+ requiredKinds: Object.freeze(['code', 'test', 'runtime', 'delivery']),
55
+ recommendedKinds: Object.freeze(['human']),
56
+ blockedSoloKinds: Object.freeze(['code', 'human']),
57
+ }),
58
+ }),
59
+ });
60
+
61
+ const CONTRADICTION_CATEGORIES = Object.freeze([
62
+ 'evidence',
63
+ 'public_surface',
64
+ 'runtime',
65
+ 'delivery',
66
+ 'planning_drift',
67
+ 'generated_surface',
68
+ ]);
69
+
70
+ const CONTRADICTION_STATUSES = Object.freeze(['passed', 'failed', 'not_applicable']);
71
+
72
+ const RELEASE_CLAIM_MATRIX = Object.freeze({
73
+ repo_closeout: Object.freeze({
74
+ deliveryPosture: 'repo_only',
75
+ requiredClaimKinds: Object.freeze([]),
76
+ allowedClaim: 'Repo-local milestone or phase closeout is supported by planning and repository artifacts only.',
77
+ invalidClaim: 'Do not imply runtime validation, delivery, publication, or public support from repo-local closeout alone.',
78
+ }),
79
+ runtime_validated_closeout: Object.freeze({
80
+ deliveryPosture: 'repo_only',
81
+ requiredClaimKinds: Object.freeze(['runtime']),
82
+ allowedClaim: 'Runtime behavior or a runtime surface was directly executed and observed for the named runtime or surface.',
83
+ invalidClaim: 'Do not generalize validation from one runtime or generated surface to another.',
84
+ }),
85
+ delivery_supported_closeout: Object.freeze({
86
+ deliveryPosture: 'delivery_sensitive',
87
+ requiredClaimKinds: Object.freeze([]),
88
+ allowedClaim: 'Externally consumed release, support, install, or delivery claims are supported by the delivery-sensitive evidence bar.',
89
+ invalidClaim: 'Do not imply merge, package, tag, GitHub Release, publication, generated-surface freshness, or public support without matching delivery evidence.',
90
+ }),
91
+ });
92
+
93
+ const CONTRADICTION_BLOCKERS_BY_POSTURE = Object.freeze({
94
+ repo_closeout: Object.freeze(['evidence', 'public_surface', 'planning_drift']),
95
+ runtime_validated_closeout: Object.freeze(['evidence', 'runtime', 'generated_surface', 'planning_drift']),
96
+ delivery_supported_closeout: CONTRADICTION_CATEGORIES,
97
+ });
98
+
99
+ export { CLOSURE_SURFACES, DELIVERY_POSTURES, EVIDENCE_KINDS, RELEASE_CLAIM_POSTURES };
100
+
101
+ export function normalizeEvidenceKind(kind) {
102
+ if (!kind) {
103
+ return null;
104
+ }
105
+
106
+ return LEGACY_EVIDENCE_ALIASES[kind] ?? null;
107
+ }
108
+
109
+ export function normalizeEvidenceKinds(kinds = []) {
110
+ const normalized = [];
111
+ for (const kind of kinds) {
112
+ const resolved = normalizeEvidenceKind(kind);
113
+ if (resolved && !normalized.includes(resolved)) {
114
+ normalized.push(resolved);
115
+ }
116
+ }
117
+ return normalized;
118
+ }
119
+
120
+ export function isClosureSurface(surface) {
121
+ return CLOSURE_SURFACES.includes(surface);
122
+ }
123
+
124
+ export function normalizeReleaseClaimPosture(posture) {
125
+ if (!posture) return 'repo_closeout';
126
+ return RELEASE_CLAIM_POSTURES.includes(posture) ? posture : null;
127
+ }
128
+
129
+ export function getEvidenceContract(surface, deliveryPosture) {
130
+ const matrix = EVIDENCE_MATRIX[surface];
131
+ if (!matrix) {
132
+ throw new Error(`Unsupported closure evidence surface: ${surface}`);
133
+ }
134
+
135
+ const posture = matrix[deliveryPosture];
136
+ if (!posture) {
137
+ throw new Error(`Unsupported delivery posture for ${surface}: ${deliveryPosture}`);
138
+ }
139
+
140
+ return {
141
+ surface,
142
+ deliveryPosture,
143
+ supportedKinds: [...EVIDENCE_KINDS],
144
+ requiredKinds: [...posture.requiredKinds],
145
+ recommendedKinds: [...posture.recommendedKinds],
146
+ blockedSoloKinds: [...posture.blockedSoloKinds],
147
+ };
148
+ }
149
+
150
+ export function describeEvidenceSurface(surface) {
151
+ if (!isClosureSurface(surface)) {
152
+ return null;
153
+ }
154
+
155
+ return {
156
+ surface,
157
+ supportedKinds: [...EVIDENCE_KINDS],
158
+ deliveryPostures: DELIVERY_POSTURES.map((deliveryPosture) => getEvidenceContract(surface, deliveryPosture)),
159
+ releaseClaimPostures: RELEASE_CLAIM_POSTURES.map((releaseClaimPosture) => getReleaseClaimContract(surface, releaseClaimPosture)),
160
+ };
161
+ }
162
+
163
+ function uniqueKinds(kinds) {
164
+ return [...new Set(kinds)];
165
+ }
166
+
167
+ function getDowngradePosture(observedKinds) {
168
+ if (observedKinds.includes('runtime')) {
169
+ return 'runtime_validated_closeout';
170
+ }
171
+ return 'repo_closeout';
172
+ }
173
+
174
+ export function getReleaseClaimContract(surface, releaseClaimPosture = 'repo_closeout') {
175
+ const posture = normalizeReleaseClaimPosture(releaseClaimPosture);
176
+ if (!posture) {
177
+ throw new Error(`Unsupported release claim posture: ${releaseClaimPosture}`);
178
+ }
179
+ const claim = RELEASE_CLAIM_MATRIX[posture];
180
+ const evidence = getEvidenceContract(surface, claim.deliveryPosture);
181
+ const requiredKinds = uniqueKinds([...evidence.requiredKinds, ...claim.requiredClaimKinds]);
182
+
183
+ return {
184
+ surface,
185
+ releaseClaimPosture: posture,
186
+ deliveryPosture: claim.deliveryPosture,
187
+ supportedKinds: [...EVIDENCE_KINDS],
188
+ requiredKinds,
189
+ requiredClaimKinds: [...claim.requiredClaimKinds],
190
+ allowedClaim: claim.allowedClaim,
191
+ invalidClaim: claim.invalidClaim,
192
+ waiverRule: 'Waivers may only narrow the release claim posture or defer an unsupported claim; they never satisfy missing required evidence for the stronger claim.',
193
+ deferralRule: 'Deferrals must name the unsupported claim, missing evidence kinds, and later workflow or milestone candidate when known.',
194
+ contradictionCategories: [...CONTRADICTION_CATEGORIES],
195
+ };
196
+ }
197
+
198
+ export function evaluateReleaseClaimPosture({
199
+ surface,
200
+ releaseClaimPosture = 'repo_closeout',
201
+ observedKinds = [],
202
+ waivedKinds = [],
203
+ } = {}) {
204
+ const contract = getReleaseClaimContract(surface, releaseClaimPosture);
205
+ const observed = normalizeEvidenceKinds(observedKinds);
206
+ const waived = normalizeEvidenceKinds(waivedKinds);
207
+ const missingKinds = contract.requiredKinds.filter((kind) => !observed.includes(kind));
208
+ const invalidWaivers = waived.filter((kind) => missingKinds.includes(kind));
209
+ const hasUnsupportedStrongClaim = contract.releaseClaimPosture !== 'repo_closeout' && missingKinds.length > 0;
210
+
211
+ return {
212
+ surface: contract.surface,
213
+ releaseClaimPosture: contract.releaseClaimPosture,
214
+ deliveryPosture: contract.deliveryPosture,
215
+ requiredKinds: [...contract.requiredKinds],
216
+ observedKinds: observed,
217
+ missingKinds,
218
+ invalidWaivers,
219
+ status: missingKinds.length === 0 && invalidWaivers.length === 0 ? 'supported' : 'unsupported',
220
+ disposition: hasUnsupportedStrongClaim ? 'downgrade_or_defer' : missingKinds.length > 0 ? 'block_or_defer' : 'proceed',
221
+ downgradeTo: hasUnsupportedStrongClaim ? getDowngradePosture(observed) : null,
222
+ deferredClaims: hasUnsupportedStrongClaim
223
+ ? [{ claim: contract.releaseClaimPosture, missingKinds }]
224
+ : [],
225
+ };
226
+ }
227
+
228
+ export function evaluateReleaseClaimCloseoutContract({
229
+ surface,
230
+ deliveryPosture = null,
231
+ releaseClaimPosture = 'repo_closeout',
232
+ observedKinds = [],
233
+ waivedKinds = [],
234
+ unsupportedClaims = [],
235
+ deferrals = [],
236
+ contradictionChecks = {},
237
+ } = {}) {
238
+ const posture = evaluateReleaseClaimPosture({
239
+ surface,
240
+ releaseClaimPosture,
241
+ observedKinds,
242
+ waivedKinds,
243
+ });
244
+ const missingContradictionChecks = CONTRADICTION_CATEGORIES.filter((name) => !(name in contradictionChecks));
245
+ const failedContradictionChecks = Object.entries(contradictionChecks)
246
+ .filter(([, status]) => status === 'failed')
247
+ .map(([name]) => name);
248
+ const unknownContradictionChecks = Object.keys(contradictionChecks)
249
+ .filter((name) => !CONTRADICTION_CATEGORIES.includes(name));
250
+ const invalidContradictionChecks = Object.entries(contradictionChecks)
251
+ .filter(([, status]) => !CONTRADICTION_STATUSES.includes(status))
252
+ .map(([name]) => name);
253
+ const blockingContradictionChecks = failedContradictionChecks.filter((name) =>
254
+ CONTRADICTION_BLOCKERS_BY_POSTURE[posture.releaseClaimPosture].includes(name)
255
+ );
256
+ const unresolvedUnsupportedClaims = unsupportedClaims.filter((claim) =>
257
+ !deferrals.some((deferral) => namesUnsupportedClaim(deferral, claim))
258
+ );
259
+ const blockers = [];
260
+
261
+ if (deliveryPosture && deliveryPosture !== posture.deliveryPosture) {
262
+ blockers.push({
263
+ code: 'incompatible_release_claim_posture',
264
+ details: [`${deliveryPosture} cannot support ${posture.releaseClaimPosture}; expected ${posture.deliveryPosture}`],
265
+ });
266
+ }
267
+
268
+ if (posture.missingKinds.length > 0) {
269
+ blockers.push({ code: 'missing_required_release_evidence', details: posture.missingKinds });
270
+ }
271
+ if (posture.invalidWaivers.length > 0) {
272
+ blockers.push({ code: 'invalid_release_waivers', details: posture.invalidWaivers });
273
+ }
274
+ if (unresolvedUnsupportedClaims.length > 0) {
275
+ blockers.push({ code: 'unsupported_release_claims', details: unresolvedUnsupportedClaims });
276
+ }
277
+ if (missingContradictionChecks.length > 0) {
278
+ blockers.push({ code: 'missing_release_contradiction_checks', details: missingContradictionChecks });
279
+ }
280
+ if (unknownContradictionChecks.length > 0) {
281
+ blockers.push({ code: 'unknown_release_contradiction_checks', details: unknownContradictionChecks });
282
+ }
283
+ if (invalidContradictionChecks.length > 0) {
284
+ blockers.push({ code: 'invalid_release_contradiction_checks', details: invalidContradictionChecks });
285
+ }
286
+ if (blockingContradictionChecks.length > 0) {
287
+ blockers.push({ code: 'failed_release_contradiction_checks', details: blockingContradictionChecks });
288
+ }
289
+
290
+ return {
291
+ ...posture,
292
+ unsupportedClaims: [...unsupportedClaims],
293
+ deferrals: [...deferrals],
294
+ failedContradictionChecks: blockingContradictionChecks,
295
+ allFailedContradictionChecks: failedContradictionChecks,
296
+ missingContradictionChecks,
297
+ unknownContradictionChecks,
298
+ invalidContradictionChecks,
299
+ unresolvedUnsupportedClaims,
300
+ blockers,
301
+ status: blockers.length === 0 ? 'supported' : 'unsupported',
302
+ };
303
+ }
304
+
305
+ function namesUnsupportedClaim(deferral, claim) {
306
+ const normalizedDeferral = normalizeClaimText(deferral);
307
+ const normalizedClaim = normalizeClaimText(claim);
308
+ if (!normalizedDeferral || !normalizedClaim) return false;
309
+ return normalizedDeferral.includes(normalizedClaim) && isStructuredDeferral(normalizedDeferral);
310
+ }
311
+
312
+ function isStructuredDeferral(normalizedDeferral) {
313
+ const namesEvidenceKind = EVIDENCE_KINDS.some((kind) => normalizedDeferral.includes(kind));
314
+ const namesLaterTarget = /\b(later|next|future|workflow|milestone|phase|gsdd)\b/.test(normalizedDeferral);
315
+ return namesEvidenceKind && namesLaterTarget;
316
+ }
317
+
318
+ function normalizeClaimText(value) {
319
+ return String(value || '')
320
+ .trim()
321
+ .toLowerCase()
322
+ .replace(/[^a-z0-9]+/g, ' ')
323
+ .replace(/\s+/g, ' ')
324
+ .trim();
325
+ }