@planu/cli 4.11.0 → 4.11.2

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 (116) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/engine/cascade-hooks/registry.js +0 -2
  3. package/dist/engine/challenge-scenarios-loader.d.ts +3 -3
  4. package/dist/engine/compliance-injector.d.ts +3 -3
  5. package/dist/engine/dashboard/kanban-logic.d.ts +1 -1
  6. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +1 -1
  7. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +1 -1
  8. package/dist/engine/elicitation/answer-extractor.d.ts +1 -4
  9. package/dist/engine/elicitation/answer-extractor.js +197 -68
  10. package/dist/engine/elicitation/decision-gap-detector.js +59 -79
  11. package/dist/engine/elicitation/index.d.ts +0 -1
  12. package/dist/engine/elicitation/index.js +0 -1
  13. package/dist/engine/elicitation/question-grounding-gate.js +83 -34
  14. package/dist/engine/evidence-gates/lifecycle-gate.js +27 -14
  15. package/dist/engine/evidence-index/index-builder.js +7 -2
  16. package/dist/engine/frontmatter-sha/frozen-fields.d.ts +1 -1
  17. package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.d.ts +2 -2
  18. package/dist/engine/handoff-artifacts/schemas.d.ts +22 -22
  19. package/dist/engine/human-summary.js +3 -3
  20. package/dist/engine/implementation-contract/common.d.ts +1 -1
  21. package/dist/engine/keyword-matcher/stopwords.d.ts +2 -2
  22. package/dist/engine/provider-adapters/adapters/claude.d.ts +1 -1
  23. package/dist/engine/provider-adapters/adapters/gemini.d.ts +1 -1
  24. package/dist/engine/provider-adapters/adapters/gpt4.d.ts +1 -1
  25. package/dist/engine/provider-adapters/adapters/markdown.d.ts +1 -1
  26. package/dist/engine/safety/atomic-write-file.d.ts +1 -1
  27. package/dist/engine/safety/cross-process-lock.d.ts +1 -1
  28. package/dist/engine/safety/path-sanitizer.d.ts +1 -1
  29. package/dist/engine/security-analyzer/profile-detector.js +48 -16
  30. package/dist/engine/spec-format/copula-blacklist.d.ts +2 -2
  31. package/dist/engine/spec-format/metadata-value-policy.d.ts +20 -20
  32. package/dist/engine/text-signal-boundaries.js +45 -12
  33. package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
  34. package/dist/engine/well-known/manifest-schema.d.ts +33 -33
  35. package/dist/engine/workers/schema.d.ts +3 -3
  36. package/dist/hosts/claude-code/ux/mcp-resources.d.ts +4 -4
  37. package/dist/hosts/codex/rest-shim.d.ts +1 -1
  38. package/dist/native/lightweight-command-catalog.d.ts +30 -30
  39. package/dist/server/openapi-spec.d.ts +132 -132
  40. package/dist/tools/challenge-spec/event-challenge-scenarios.js +8 -27
  41. package/dist/tools/challenge-spec/resilience-challenge-scenarios-a.js +16 -32
  42. package/dist/tools/challenge-spec/resilience-challenge-scenarios-b.js +15 -20
  43. package/dist/tools/challenge-spec/scenario-collector.d.ts +13 -0
  44. package/dist/tools/challenge-spec/scenario-collector.js +86 -0
  45. package/dist/tools/challenge-spec/scenarios-data.js +10 -9
  46. package/dist/tools/challenge-spec/scenarios-failure.js +38 -54
  47. package/dist/tools/challenge-spec/scenarios-scale.js +14 -15
  48. package/dist/tools/challenge-spec/scenarios-security.d.ts +1 -1
  49. package/dist/tools/challenge-spec/scenarios-security.js +18 -11
  50. package/dist/tools/challenge-spec/scenarios-utils.d.ts +31 -5
  51. package/dist/tools/challenge-spec/scenarios-utils.js +102 -6
  52. package/dist/tools/challenge-spec/security-challenge-scenarios.d.ts +1 -1
  53. package/dist/tools/challenge-spec/security-challenge-scenarios.js +20 -25
  54. package/dist/tools/challenge-spec.js +39 -118
  55. package/dist/tools/compliance-gate-handler.d.ts +1 -1
  56. package/dist/tools/create-spec.js +4 -24
  57. package/dist/tools/delete-first/suggest-deletions.d.ts +1 -1
  58. package/dist/tools/generate-batch-script.d.ts +2 -2
  59. package/dist/tools/init-project/rules-generator.js +0 -1
  60. package/dist/tools/oauth-handler.d.ts +1 -1
  61. package/dist/tools/register-sdd-tools.d.ts +1 -1
  62. package/dist/tools/render-spec-for-provider.d.ts +2 -2
  63. package/dist/tools/scan-orphan-spec-refs.d.ts +6 -6
  64. package/dist/tools/schemas/agents.d.ts +7 -7
  65. package/dist/tools/schemas/analysis.d.ts +11 -11
  66. package/dist/tools/schemas/delete.d.ts +5 -5
  67. package/dist/tools/schemas/github.d.ts +4 -4
  68. package/dist/tools/schemas/ide-config.d.ts +12 -12
  69. package/dist/tools/schemas/infra.d.ts +9 -9
  70. package/dist/tools/schemas/lifecycle.d.ts +50 -50
  71. package/dist/tools/schemas/llm-provider-schemas.d.ts +7 -7
  72. package/dist/tools/schemas/plugins-schemas.d.ts +12 -12
  73. package/dist/tools/schemas/project-graph.d.ts +6 -6
  74. package/dist/tools/schemas/rbac.d.ts +2 -2
  75. package/dist/tools/schemas/registry.d.ts +3 -3
  76. package/dist/tools/schemas/runtime-security.d.ts +2 -2
  77. package/dist/tools/schemas/session.d.ts +2 -2
  78. package/dist/tools/schemas/spec.d.ts +15 -15
  79. package/dist/tools/schemas/token-intelligence.d.ts +6 -6
  80. package/dist/tools/schemas/token-optimization.d.ts +2 -2
  81. package/dist/tools/schemas/workers-schema.d.ts +4 -4
  82. package/dist/tools/skill-bootstrap-handler.d.ts +7 -7
  83. package/dist/tools/tool-registry/deprecated-stubs.d.ts +1 -1
  84. package/dist/tools/triage-request.d.ts +3 -3
  85. package/dist/tools/update-status/file-sync.d.ts +1 -1
  86. package/dist/tools/update-status/file-sync.js +10 -45
  87. package/dist/tools/update-status/index.js +28 -27
  88. package/dist/tools/update-status/response-builder.d.ts +1 -1
  89. package/dist/tools/update-status/response-builder.js +11 -19
  90. package/dist/tools/update-status/transition-guard.js +1 -1
  91. package/dist/tools/update-status-actions.d.ts +0 -1
  92. package/dist/tools/update-status-actions.js +10 -26
  93. package/dist/tools/update-status-convention-gate.d.ts +1 -6
  94. package/dist/tools/update-status-convention-gate.js +9 -33
  95. package/dist/types/docs.d.ts +0 -4
  96. package/dist/types/env.d.ts +0 -4
  97. package/dist/types/estimation.d.ts +0 -2
  98. package/dist/types/multi-teammate-review.d.ts +4 -4
  99. package/dist/types/plugin-install.d.ts +7 -7
  100. package/package.json +21 -18
  101. package/planu-native.json +1 -1
  102. package/planu-plugin.json +1 -1
  103. package/src/i18n/messages/en.json +0 -72
  104. package/src/i18n/messages/es.json +0 -72
  105. package/src/i18n/messages/pt.json +0 -72
  106. package/dist/config/elicitation-dimensions.json +0 -51
  107. package/dist/engine/actuals-estimator.d.ts +0 -14
  108. package/dist/engine/actuals-estimator.js +0 -64
  109. package/dist/engine/cascade-hooks/hooks/living-specs.hook.d.ts +0 -3
  110. package/dist/engine/cascade-hooks/hooks/living-specs.hook.js +0 -19
  111. package/dist/engine/elicitation/option-builder.d.ts +0 -9
  112. package/dist/engine/elicitation/option-builder.js +0 -85
  113. package/dist/engine/progress-writer.d.ts +0 -10
  114. package/dist/engine/progress-writer.js +0 -63
  115. package/dist/tools/update-status/mode-hints.d.ts +0 -4
  116. package/dist/tools/update-status/mode-hints.js +0 -24
@@ -1,5 +1,6 @@
1
1
  // tools/challenge-spec/resilience-challenge-scenarios-a.ts — SPEC-034
2
2
  // Signal detectors + validation boundary + cascade failure scenarios
3
+ import { contentMentions } from './scenarios-utils.js';
3
4
  // Keywords indicating external service calls or shared state
4
5
  export const EXTERNAL_CALL_KEYWORDS = [
5
6
  'api',
@@ -70,36 +71,31 @@ export const SAGA_KEYWORDS = [
70
71
  * Returns true when the spec describes an external service call.
71
72
  */
72
73
  export function hasExternalCallSignal(spec, specContent) {
73
- const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
74
- return EXTERNAL_CALL_KEYWORDS.some((kw) => lower.includes(kw));
74
+ return contentMentions(`${spec.title}\n${spec.tags.join(' ')}\n${specContent}`, EXTERNAL_CALL_KEYWORDS);
75
75
  }
76
76
  /**
77
77
  * Returns true when the spec describes input validation.
78
78
  */
79
79
  export function hasValidationSignal(spec, specContent) {
80
- const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
81
- return VALIDATION_KEYWORDS.some((kw) => lower.includes(kw));
80
+ return contentMentions(`${spec.title}\n${spec.tags.join(' ')}\n${specContent}`, VALIDATION_KEYWORDS);
82
81
  }
83
82
  /**
84
83
  * Returns true when the spec is a rate-limit-sensitive endpoint.
85
84
  */
86
85
  export function hasRateLimitSignal(spec, specContent) {
87
- const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
88
- return RATE_LIMIT_KEYWORDS.some((kw) => lower.includes(kw));
86
+ return contentMentions(`${spec.title}\n${spec.tags.join(' ')}\n${specContent}`, RATE_LIMIT_KEYWORDS);
89
87
  }
90
88
  /**
91
89
  * Returns true when the spec involves shared state.
92
90
  */
93
91
  export function hasConcurrencySignal(spec, specContent) {
94
- const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
95
- return CONCURRENCY_KEYWORDS.some((kw) => lower.includes(kw));
92
+ return contentMentions(`${spec.title}\n${spec.tags.join(' ')}\n${specContent}`, CONCURRENCY_KEYWORDS);
96
93
  }
97
94
  /**
98
95
  * Returns true when the spec describes a distributed saga.
99
96
  */
100
97
  export function hasSagaSignal(spec, specContent) {
101
- const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
102
- return SAGA_KEYWORDS.some((kw) => lower.includes(kw));
98
+ return contentMentions(`${spec.title}\n${spec.tags.join(' ')}\n${specContent}`, SAGA_KEYWORDS);
103
99
  }
104
100
  /**
105
101
  * Generate validation boundary challenge scenarios.
@@ -161,38 +157,26 @@ export function generateValidationBoundaryScenarios(spec) {
161
157
  export function generateCascadeFailureScenarios(spec) {
162
158
  return [
163
159
  {
164
- scenario: `[${spec.id}] Cascade Failure — Connection Pool Exhaustion: ` +
165
- 'If the external service starts responding in 30 seconds (OS timeout) instead of the expected 200ms, ' +
166
- 'how many simultaneous requests accumulate in the connection pool before the circuit breaker activates? ' +
167
- 'If the pool has size N and each request holds a connection for T seconds, the pool fills in N * T seconds. ' +
168
- 'Calculate this with the actual pool size and timeout values. ' +
169
- 'Does the circuit breaker open before the pool is exhausted?',
160
+ scenario: `[${spec.id}] Cascade Failure — Dependency Latency Exhausts Local Capacity: ` +
161
+ 'Which latency and capacity envelope is declared for the dependency? ' +
162
+ 'Does failure isolation activate before workers, connections, or queued work are exhausted?',
170
163
  probability: 'medium',
171
164
  impact: 'critical',
172
- currentHandling: 'Without a circuit breaker, slow external services cause connection pool exhaustion, ' +
173
- 'blocking all requests to this service (not just those calling the external service).',
174
- requiredHandling: 'Set connect_timeout (2s) and read_timeout (10s) — never rely on OS timeout (30s+). ' +
175
- 'Circuit breaker must open before pool exhaustion: threshold 50% failures in 10 calls, open duration 60s. ' +
176
- 'When pool is at 80% capacity, emit a warning metric. ' +
177
- 'When circuit is open, return 503 immediately without touching the pool.',
165
+ currentHandling: 'Slow external services can exhaust local capacity and block unrelated work.',
166
+ requiredHandling: 'Define measurable connect/read bounds, local capacity, failure-isolation behavior, and alert thresholds from the dependency contract.',
178
167
  dataConsistency: 'Pool exhaustion can cascade: if this service pool is exhausted, callers of this service also accumulate requests.',
179
- userExperience: 'Without fast-fail (circuit open), users wait for OS timeout (30s) instead of receiving a 503 in under 1s.',
168
+ userExperience: 'Use the dependency-failure response and latency bound declared by the client contract.',
180
169
  },
181
170
  {
182
- scenario: `[${spec.id}] Resilience — Retry Thundering Herd: ` +
183
- 'If the external service recovers after a 60-second outage and the circuit breaker transitions to HALF-OPEN, ' +
184
- 'all queued retries fire simultaneously. ' +
185
- 'Does the retry strategy use jitter (random delay offset) to spread the load, ' +
186
- 'or do all retries hit the recovering service at the same time (thundering herd)?',
171
+ scenario: `[${spec.id}] Resilience — Coordinated Retries Overload a Recovering Dependency: ` +
172
+ 'If automatic retries are declared, what prevents queued work from retrying simultaneously when the dependency recovers?',
187
173
  probability: 'medium',
188
174
  impact: 'high',
189
175
  currentHandling: 'Retry without jitter causes thundering herd: all retries fire simultaneously after backoff, ' +
190
176
  'potentially re-overloading the service as it recovers.',
191
- requiredHandling: 'Exponential backoff with jitter: delay = random(0, base * 2^attempt). ' +
192
- 'In HALF-OPEN state: limit to 1-3 probe requests, not all queued requests simultaneously. ' +
193
- 'If probes succeed, circuit closes gradually.',
177
+ requiredHandling: 'Define bounded retry, jitter, and recovery-probe behavior from the dependency contract, or confirm that automatic retries are not used.',
194
178
  dataConsistency: 'Thundering herd can cause the recovering service to fail again immediately after recovery.',
195
- userExperience: 'Jitter is invisible to the user but prevents the oscillation pattern where the service flaps between healthy and degraded.',
179
+ userExperience: 'Use the recovery feedback and maximum wait declared by the client contract.',
196
180
  },
197
181
  ];
198
182
  }
@@ -7,17 +7,15 @@ export function generateRateLimitBypassScenarios(spec) {
7
7
  return [
8
8
  {
9
9
  scenario: `[${spec.id}] Rate Limit Bypass — Distributed Attack: ` +
10
- 'The rate limit is configured per-IP. An attacker uses 1000 different IPs (botnet or distributed scraper) ' +
10
+ 'The rate limit is configured per-IP. An attacker uses many independent sources ' +
11
11
  'to bypass the per-IP limit. What is the effective global rate of requests the system receives? ' +
12
12
  'Does the system have any IP-independent rate limiting (per-account, global circuit breaker) to handle this?',
13
13
  probability: 'medium',
14
14
  impact: 'high',
15
- currentHandling: 'Per-IP rate limiting is ineffective against distributed attacks — an attacker with 1000 IPs gets 1000x the per-IP limit.',
16
- requiredHandling: 'Layer rate limits: per-IP (coarse), per-account/API-key (fine-grained), and global per-endpoint. ' +
17
- 'For authentication endpoints: per-account lockout after N failed attempts (regardless of source IP). ' +
18
- 'Use Redis-backed distributed rate limiting for consistency across multiple instances.',
15
+ currentHandling: 'Per-IP rate limiting alone may be ineffective against distributed attacks.',
16
+ requiredHandling: 'Define enforcement dimensions, shared-state boundaries, and rejection behavior from the rate-limit contract.',
19
17
  dataConsistency: 'In-memory rate limiting (local to one instance) allows the effective limit to be multiplied by the number of instances.',
20
- userExperience: 'Rate limit responses must include Retry-After header so legitimate clients can back off gracefully.',
18
+ userExperience: 'Use the retry guidance declared by the client contract.',
21
19
  },
22
20
  ];
23
21
  }
@@ -39,12 +37,12 @@ export function generateConcurrencyScenarios(spec, concurrencyModel) {
39
37
  'Use sync/atomic for simple counters. ' +
40
38
  'Always run `go test -race ./...` in CI to detect race conditions before production.',
41
39
  dataConsistency: 'Race conditions on shared state lead to inconsistent reads and corrupted writes that are hard to reproduce.',
42
- userExperience: 'A runtime panic from a race condition kills the entire goroutine group, causing an unexpected 500.',
40
+ userExperience: 'Use the failure response declared by the service contract.',
43
41
  },
44
42
  'async-await': {
45
43
  scenario: `[${spec.id}] Concurrency — Event Loop Blocking: ` +
46
44
  'Does this feature perform any synchronous CPU-intensive or I/O-blocking operation inside an async function? ' +
47
- 'Examples: JSON.parse on large payloads (> 1MB), crypto operations (bcrypt without async), ' +
45
+ 'Examples include parsing payloads above the declared size envelope or synchronous cryptography. ' +
48
46
  'RegExp with catastrophic backtracking. ' +
49
47
  'These block the single-threaded event loop and degrade ALL concurrent requests, not just this one.',
50
48
  probability: 'medium',
@@ -66,10 +64,10 @@ export function generateConcurrencyScenarios(spec, concurrencyModel) {
66
64
  impact: 'critical',
67
65
  currentHandling: 'Inconsistent lock ordering causes deadlocks that hang threads indefinitely, eventually exhausting the thread pool.',
68
66
  requiredHandling: 'Establish a canonical lock ordering (always A before B, never B before A). Document this ordering. ' +
69
- 'Use tryLock(timeout) in tests to detect deadlocks: if the lock is not acquired in N seconds, the test fails. ' +
67
+ 'Use bounded lock acquisition in tests with the timeout declared by the concurrency contract. ' +
70
68
  'Consider using ReentrantLock over synchronized blocks for more control.',
71
69
  dataConsistency: 'Deadlocked threads hold locks, preventing other threads from accessing the protected resources.',
72
- userExperience: 'A deadlock causes the request to hang until the timeout is reached, resulting in a 503 or 504 for the user.',
70
+ userExperience: 'Use the timeout response declared by the service contract.',
73
71
  },
74
72
  'rust-ownership': {
75
73
  scenario: `[${spec.id}] Concurrency — Rust Logical Deadlock: ` +
@@ -95,9 +93,7 @@ export function generateConcurrencyScenarios(spec, concurrencyModel) {
95
93
  probability: 'medium',
96
94
  impact: 'high',
97
95
  currentHandling: 'Unbounded mailboxes cause memory exhaustion under load. Message drops without metrics cause silent data loss.',
98
- requiredHandling: 'Configure a bounded mailbox with a drop strategy (drop-oldest or drop-newest). ' +
99
- 'Emit a metric when mailbox exceeds 80% capacity. ' +
100
- 'Consider back-pressure: if the producer is too fast, slow it down at the source instead of dropping at the destination.',
96
+ requiredHandling: 'Define mailbox bounds, overflow behavior, and observability from the actor contract.',
101
97
  dataConsistency: 'Dropped messages cause data loss that is not recoverable without event sourcing or DLQ.',
102
98
  userExperience: 'Mailbox overflow is invisible to users until they notice missing data or failed operations.',
103
99
  },
@@ -113,9 +109,9 @@ export function generateConcurrencyScenarios(spec, concurrencyModel) {
113
109
  requiredHandling: 'Choose the backpressure strategy explicitly based on business requirements. ' +
114
110
  'For critical events: use ERROR or bounded BUFFER to prevent loss. ' +
115
111
  'For metrics/logging: DROP or LATEST is acceptable. ' +
116
- 'Test with a producer that emits 10x faster than the subscriber to verify the strategy holds.',
112
+ 'Test above the producer/subscriber rate envelope declared by the stream contract.',
117
113
  dataConsistency: 'Backpressure strategy determines whether data loss is acceptable or whether the stream must be slowed down.',
118
- userExperience: 'Backpressure overflow can cause request failures for the publisher, which surfaces as 503 or timeout.',
114
+ userExperience: 'Use the overflow response declared by the stream contract.',
119
115
  },
120
116
  };
121
117
  const scenario = modelScenarios[concurrencyModel];
@@ -127,9 +123,9 @@ export function generateConcurrencyScenarios(spec, concurrencyModel) {
127
123
  export function generateSagaFailureScenarios(spec) {
128
124
  return [
129
125
  {
130
- scenario: `[${spec.id}] Distributed Saga — Partial Failure at Step N: ` +
131
- 'If step 3 of 5 in the saga fails after steps 1 and 2 already executed in external services, ' +
132
- 'how long do the compensation events (rollback for steps 1 and 2) take to complete? ' +
126
+ scenario: `[${spec.id}] Distributed Saga — Partial Failure at a Later Step: ` +
127
+ 'If a later saga step fails after earlier external actions completed, ' +
128
+ 'how long may compensation take according to the saga contract? ' +
133
129
  'How is it verified that the system is in a consistent state after compensation, ' +
134
130
  'rather than in a partially-reverted state (step 1 rolled back but step 2 not yet)?',
135
131
  probability: 'medium',
@@ -138,8 +134,7 @@ export function generateSagaFailureScenarios(spec) {
138
134
  '(some external services have committed while others have not).',
139
135
  requiredHandling: 'Each saga step must have a defined compensation operation. ' +
140
136
  'Compensation must be idempotent (can be retried safely). ' +
141
- 'Track saga state in a durable store: if the orchestrator crashes mid-saga, it must resume from the last known state. ' +
142
- 'Add a saga status endpoint: GET /sagas/{id} returns the current step, status, and any failed compensations.',
137
+ 'Persist enough saga state to resume from the last known step and expose status only through the interface declared by the contract.',
143
138
  dataConsistency: 'Partial compensation leaves distributed state inconsistent. The saga log must be the source of truth for rollback status.',
144
139
  userExperience: 'Users must receive a clear error explaining that the operation was rolled back and what state the system is in. ' +
145
140
  'If rollback also failed, the user must be told that manual resolution is required.',
@@ -0,0 +1,13 @@
1
+ import type { ChallengeSpecFocus, FailureScenario, ProjectKnowledge, Spec } from '../../types/index.js';
2
+ import { type ChallengeCapabilities } from './scenarios-utils.js';
3
+ interface ScenarioCollectionInput {
4
+ spec: Spec;
5
+ specContent: string;
6
+ knowledge: ProjectKnowledge;
7
+ focusAreas: ChallengeSpecFocus[];
8
+ capabilities: ChallengeCapabilities;
9
+ }
10
+ /** Select scenario families before generation so unsupported templates never reach risk scoring. */
11
+ export declare function collectCapabilityScenarios(input: ScenarioCollectionInput): FailureScenario[];
12
+ export {};
13
+ //# sourceMappingURL=scenario-collector.d.ts.map
@@ -0,0 +1,86 @@
1
+ import { generateDataConsistencyScenarios, generateDesktopAutoUpdateChallenges, generateFailureScenarios, generateGameChallenges, generateScaleScenarios, generateSecurityScenarios, } from '../challenge-spec-scenarios.js';
2
+ import { generateAgentChallengeScenarios, isAgentSpec } from './agent-challenge-scenarios.js';
3
+ import { generateDddChallenges, generateXpPairClarityChallenge, } from './architecture-challenge-scenarios.js';
4
+ import { generateEventChallengeScenarios } from './event-challenge-scenarios.js';
5
+ import { generateImplementationContractChallengeScenarios } from './implementation-contract-challenge-scenarios.js';
6
+ import { getPlatformChallenges } from './platform-challenge-scenarios.js';
7
+ import { generatePrivacyChallengeScenarios } from './privacy-challenge-scenarios.js';
8
+ import { generateResilienceChallengeScenarios } from './resilience-challenge-scenarios.js';
9
+ import { generateSecurityChallengeScenarios } from './security-challenge-scenarios.js';
10
+ import { hasAnyChallengeCapability } from './scenarios-utils.js';
11
+ /** Select scenario families before generation so unsupported templates never reach risk scoring. */
12
+ export function collectCapabilityScenarios(input) {
13
+ const { spec, specContent, knowledge, focusAreas, capabilities } = input;
14
+ const scenarios = [];
15
+ const hasFocus = (focus) => focusAreas.includes(focus);
16
+ const hasCapability = (names) => hasAnyChallengeCapability(capabilities, names);
17
+ if (hasFocus('failures') &&
18
+ hasCapability([
19
+ 'networkApi',
20
+ 'database',
21
+ 'userInput',
22
+ 'externalService',
23
+ 'payment',
24
+ 'fileTransfer',
25
+ ])) {
26
+ scenarios.push(...generateFailureScenarios(spec, specContent, knowledge));
27
+ }
28
+ if (hasFocus('security') &&
29
+ hasCapability(['authentication', 'authorization', 'userInput', 'database', 'payment'])) {
30
+ scenarios.push(...generateSecurityScenarios(spec, specContent, knowledge));
31
+ }
32
+ if (hasFocus('scale') && capabilities.scale) {
33
+ scenarios.push(...generateScaleScenarios(spec, specContent, knowledge));
34
+ }
35
+ if (hasFocus('data-consistency') && hasCapability(['dataWrite', 'cache', 'duplicateOperation'])) {
36
+ scenarios.push(...generateDataConsistencyScenarios(spec, specContent, knowledge));
37
+ }
38
+ const isGameProject = knowledge.projectCategory === 'game' ||
39
+ (knowledge.framework !== null &&
40
+ ['unity', 'godot', 'unreal', 'bevy', 'phaser', 'pygame'].includes(knowledge.framework));
41
+ if (isGameProject) {
42
+ scenarios.push(...generateGameChallenges(spec, specContent, knowledge));
43
+ }
44
+ if (capabilities.desktopUpdate) {
45
+ scenarios.push(...generateDesktopAutoUpdateChallenges(spec, specContent, knowledge));
46
+ }
47
+ if (capabilities.agent && isAgentSpec(spec, specContent)) {
48
+ scenarios.push(...generateAgentChallengeScenarios(spec, specContent, knowledge));
49
+ }
50
+ if (hasFocus('failures') && capabilities.implementationContract) {
51
+ scenarios.push(...generateImplementationContractChallengeScenarios(spec, specContent));
52
+ }
53
+ if (capabilities.platform) {
54
+ scenarios.push(...getPlatformChallenges(spec, specContent, knowledge));
55
+ }
56
+ if (hasFocus('security') &&
57
+ hasCapability(['authentication', 'authorization', 'database', 'payment'])) {
58
+ scenarios.push(...generateSecurityChallengeScenarios(spec, specContent, knowledge));
59
+ }
60
+ if (capabilities.privacy) {
61
+ scenarios.push(...generatePrivacyChallengeScenarios(spec, specContent, knowledge));
62
+ }
63
+ if (capabilities.events) {
64
+ scenarios.push(...generateEventChallengeScenarios(spec, specContent, knowledge));
65
+ }
66
+ const hasDdd = knowledge.architecture.primary === 'ddd' || knowledge.architecture.secondary.includes('ddd');
67
+ if (hasDdd && capabilities.ddd) {
68
+ scenarios.push(...generateDddChallenges(spec, specContent));
69
+ }
70
+ if (hasCapability([
71
+ 'externalService',
72
+ 'networkApi',
73
+ 'validation',
74
+ 'rateLimit',
75
+ 'concurrency',
76
+ 'saga',
77
+ ])) {
78
+ scenarios.push(...generateResilienceChallengeScenarios(spec, specContent, knowledge));
79
+ }
80
+ const agileConfig = knowledge.agileConfig;
81
+ if (agileConfig?.process === 'xp' && capabilities.xp) {
82
+ scenarios.push(...generateXpPairClarityChallenge(spec, specContent));
83
+ }
84
+ return scenarios;
85
+ }
86
+ //# sourceMappingURL=scenario-collector.js.map
@@ -7,6 +7,7 @@ const WRITE_SIGNALS = [
7
7
  const CACHE_SIGNALS = [/\b(cache|real-time|realtime|concurrent|websocket|stale data)\b/i];
8
8
  const DUPLICATE_OPERATION_SIGNALS = [
9
9
  /\b(submit|form|checkout|order|payment|create record|mutation|button click)\b/i,
10
+ /\bduplicate\s+(?:request|submission|operation|record)\b/i,
10
11
  ];
11
12
  export function generateDataConsistencyScenarios(_spec, content, _knowledge) {
12
13
  const scenarios = [];
@@ -20,9 +21,9 @@ export function generateDataConsistencyScenarios(_spec, content, _knowledge) {
20
21
  currentHandling: contentMentions(lower, ['transaction', 'atomic', 'rollback'])
21
22
  ? 'Transactions mentioned'
22
23
  : 'Not addressed',
23
- requiredHandling: 'Wrap multi-step writes in database transactions. Implement saga pattern for distributed operations.',
24
- dataConsistency: 'ACID properties must be maintained. No orphaned records.',
25
- userExperience: 'Transparent: user sees either success or clean failure with clear error message.',
24
+ requiredHandling: 'Define atomicity, rollback, and partial-failure behavior for the declared write boundary.',
25
+ dataConsistency: 'The write contract must state which states are valid after interruption.',
26
+ userExperience: 'Use the success and failure behavior declared by the interaction contract.',
26
27
  });
27
28
  }
28
29
  // Stale data
@@ -34,9 +35,9 @@ export function generateDataConsistencyScenarios(_spec, content, _knowledge) {
34
35
  currentHandling: contentMentions(lower, ['invalidat', 'ttl', 'websocket', 'real-time'])
35
36
  ? 'Some cache strategy mentioned'
36
37
  : 'Not addressed',
37
- requiredHandling: 'Implement cache invalidation on writes. Use TTL for time-sensitive data. Consider event-driven invalidation.',
38
- dataConsistency: 'Define acceptable staleness window per data type. Document cache strategy.',
39
- userExperience: 'Show "last updated" timestamp. Offer manual refresh option.',
38
+ requiredHandling: 'Define invalidation and freshness behavior from the declared cache contract.',
39
+ dataConsistency: 'Define and verify the acceptable staleness boundary.',
40
+ userExperience: 'Use the freshness feedback declared by the interaction contract.',
40
41
  });
41
42
  }
42
43
  // Duplicate operations only apply to explicit user-triggered mutations.
@@ -48,9 +49,9 @@ export function generateDataConsistencyScenarios(_spec, content, _knowledge) {
48
49
  currentHandling: contentMentions(lower, ['idempoten', 'debounce', 'disable', 'lock'])
49
50
  ? 'Some prevention mentioned'
50
51
  : 'Not addressed',
51
- requiredHandling: 'Implement idempotency keys for mutations. Disable submit button on click. Use unique constraints in DB.',
52
- dataConsistency: 'No duplicate records from repeated submissions.',
53
- userExperience: 'Button disabled during submission. Clear success/error feedback.',
52
+ requiredHandling: 'Define duplicate-submission semantics and enforce them at the declared mutation boundary.',
53
+ dataConsistency: 'Repeated submissions must produce the result declared by the mutation contract.',
54
+ userExperience: 'Use the in-progress and completion feedback declared by the interaction contract.',
54
55
  });
55
56
  }
56
57
  return scenarios;
@@ -1,101 +1,85 @@
1
1
  // tools/challenge-spec/scenarios-failure.ts — Network, database, and resource failure scenarios
2
- import { contentMentions } from './scenarios-utils.js';
2
+ import { contentMentions, detectChallengeCapabilities } from './scenarios-utils.js';
3
3
  export function generateFailureScenarios(spec, content, _knowledge) {
4
4
  const scenarios = [];
5
- const lower = content.toLowerCase();
5
+ const capabilities = detectChallengeCapabilities(spec, content);
6
6
  // Network failures
7
- if (spec.target === 'fullstack' ||
8
- spec.target === 'frontend' ||
9
- lower.includes('api') ||
10
- lower.includes('fetch') ||
11
- lower.includes('request')) {
7
+ if (capabilities.networkApi) {
12
8
  scenarios.push({
13
9
  scenario: 'API endpoint becomes unreachable (network timeout)',
14
10
  probability: 'medium',
15
11
  impact: 'high',
16
- currentHandling: contentMentions(lower, ['timeout', 'retry', 'fallback'])
12
+ currentHandling: contentMentions(content, ['timeout', 'retry', 'fallback'])
17
13
  ? 'Partially addressed in spec'
18
14
  : 'Not addressed in spec',
19
- requiredHandling: 'Implement request timeout (e.g. 10s), exponential backoff retry (3 attempts), and graceful fallback UI',
15
+ requiredHandling: 'Define the request timeout, retry policy, and degraded behavior from the endpoint contract.',
20
16
  dataConsistency: 'Ensure idempotent operations to prevent duplicate actions on retry',
21
- userExperience: 'Show offline indicator, queue actions for retry, display stale data if available',
17
+ userExperience: 'Use the failure behavior defined by the client contract.',
22
18
  });
23
19
  }
24
20
  // Database failures
25
- if (spec.target === 'backend' ||
26
- spec.target === 'fullstack' ||
27
- spec.target === 'database' ||
28
- lower.includes('database') ||
29
- lower.includes('query') ||
30
- lower.includes('schema')) {
21
+ if (capabilities.database) {
31
22
  scenarios.push({
32
23
  scenario: 'Database connection pool exhausted',
33
24
  probability: 'low',
34
25
  impact: 'critical',
35
26
  currentHandling: 'Not addressed in spec',
36
- requiredHandling: 'Configure connection pool limits, implement circuit breaker, add health check endpoint',
37
- dataConsistency: 'In-flight transactions will fail; ensure rollback handling',
38
- userExperience: 'Return 503 Service Unavailable with retry-after header',
27
+ requiredHandling: 'Define connection-pool bounds, exhaustion behavior, and recovery from the database contract.',
28
+ dataConsistency: 'Define how in-flight transactions remain consistent when capacity is exhausted.',
29
+ userExperience: 'Return the database-unavailable response defined by the service contract.',
39
30
  });
40
31
  scenarios.push({
41
32
  scenario: 'Migration fails halfway through',
42
33
  probability: 'medium',
43
34
  impact: 'high',
44
- currentHandling: contentMentions(lower, ['migration', 'rollback', 'reversible'])
35
+ currentHandling: contentMentions(content, ['migration', 'rollback', 'reversible'])
45
36
  ? 'Partially addressed'
46
37
  : 'Not addressed',
47
- requiredHandling: 'Use transactional migrations. Every migration must have a reverse. Test migrations on staging first.',
48
- dataConsistency: 'Partial schema changes can leave database in inconsistent state',
49
- userExperience: 'Deploy downtime or maintenance mode during migration',
38
+ requiredHandling: 'Define migration atomicity, recovery, rollback expectations, and verification evidence.',
39
+ dataConsistency: 'Partial schema changes must have an explicit recovery contract.',
40
+ userExperience: 'Use the availability behavior declared by the deployment contract.',
50
41
  });
51
42
  }
52
43
  // Input validation failures
53
- scenarios.push({
54
- scenario: 'Malformed or oversized input data',
55
- probability: 'high',
56
- impact: 'medium',
57
- currentHandling: contentMentions(lower, ['validat', 'sanitiz', 'schema', 'zod'])
58
- ? 'Validation mentioned in spec'
59
- : 'Not explicitly addressed',
60
- requiredHandling: 'Validate all inputs at API boundary. Use schema validation (Zod, Joi, etc.). Set payload size limits.',
61
- dataConsistency: 'Invalid data must never reach the database',
62
- userExperience: 'Clear validation error messages with field-level detail',
63
- });
44
+ if (capabilities.userInput) {
45
+ scenarios.push({
46
+ scenario: 'Malformed or oversized input data',
47
+ probability: 'high',
48
+ impact: 'medium',
49
+ currentHandling: contentMentions(content, ['validat', 'sanitiz', 'schema', 'zod'])
50
+ ? 'Validation mentioned in spec'
51
+ : 'Not explicitly addressed',
52
+ requiredHandling: 'Validate input at the declared boundary and define payload limits.',
53
+ dataConsistency: 'Invalid input must not reach the declared persistence boundary.',
54
+ userExperience: 'Return the validation feedback defined by the interaction contract.',
55
+ });
56
+ }
64
57
  // Third-party service failures
65
- if (lower.includes('stripe') ||
66
- lower.includes('payment') ||
67
- lower.includes('email') ||
68
- lower.includes('auth') ||
69
- lower.includes('external') ||
70
- lower.includes('webhook')) {
58
+ if (capabilities.externalService || capabilities.payment) {
71
59
  scenarios.push({
72
- scenario: 'Third-party service (payment/auth/email) goes down',
60
+ scenario: 'Third-party service declared by the spec becomes unavailable',
73
61
  probability: 'medium',
74
62
  impact: 'critical',
75
- currentHandling: contentMentions(lower, ['fallback', 'queue', 'retry', 'circuit'])
63
+ currentHandling: contentMentions(content, ['fallback', 'queue', 'retry', 'circuit'])
76
64
  ? 'Partially addressed'
77
65
  : 'Not addressed',
78
- requiredHandling: 'Implement circuit breaker pattern. Queue operations for retry. Have degraded-mode behavior.',
79
- dataConsistency: 'Use idempotency keys for payment operations. Log all attempts.',
80
- userExperience: 'Inform user of degraded state. Offer alternative or queue the action.',
66
+ requiredHandling: 'Define observable failure and recovery behavior from the declared dependency contract.',
67
+ dataConsistency: 'Preserve idempotence for any operation the spec marks as retryable.',
68
+ userExperience: 'Use the degraded behavior defined by the dependency contract.',
81
69
  });
82
70
  }
83
71
  // Memory/resource exhaustion
84
- if (lower.includes('upload') ||
85
- lower.includes('file') ||
86
- lower.includes('stream') ||
87
- lower.includes('batch') ||
88
- lower.includes('bulk')) {
72
+ if (capabilities.fileTransfer) {
89
73
  scenarios.push({
90
74
  scenario: 'Memory exhaustion from large file upload or batch operation',
91
75
  probability: 'medium',
92
76
  impact: 'high',
93
- currentHandling: contentMentions(lower, ['limit', 'stream', 'chunk', 'pagination'])
77
+ currentHandling: contentMentions(content, ['limit', 'stream', 'chunk', 'pagination'])
94
78
  ? 'Partially addressed'
95
79
  : 'Not addressed',
96
- requiredHandling: 'Use streaming for large files. Implement file size limits. Process batches in chunks.',
97
- dataConsistency: 'Partial batch processing must be resumable. Track progress.',
98
- userExperience: 'Show progress indicator for long operations. Allow cancellation.',
80
+ requiredHandling: 'Define resource bounds and processing behavior for over-limit files or batches.',
81
+ dataConsistency: 'Define the consistency guarantee for interrupted file or batch processing.',
82
+ userExperience: 'Use the progress and cancellation behavior declared by the interaction contract.',
99
83
  });
100
84
  }
101
85
  return scenarios;
@@ -1,34 +1,33 @@
1
1
  // tools/challenge-spec/scenarios-scale.ts — Traffic spike and data growth scenarios
2
- import { contentMentions } from './scenarios-utils.js';
2
+ import { contentMentions, detectChallengeCapabilities } from './scenarios-utils.js';
3
3
  export function generateScaleScenarios(spec, content, _knowledge) {
4
4
  const scenarios = [];
5
- const lower = content.toLowerCase();
5
+ const capabilities = detectChallengeCapabilities(spec, content);
6
+ if (!capabilities.scale) {
7
+ return scenarios;
8
+ }
6
9
  // High traffic
7
10
  scenarios.push({
8
- scenario: 'Traffic spike: 10x normal load',
11
+ scenario: 'Traffic spike exceeds the load envelope defined by the spec',
9
12
  probability: spec.scope === 'architectural' ? 'medium' : 'low',
10
13
  impact: 'high',
11
- currentHandling: contentMentions(lower, ['cache', 'cdn', 'queue', 'scale', 'auto-scale'])
14
+ currentHandling: contentMentions(content, ['cache', 'cdn', 'queue', 'scale', 'auto-scale'])
12
15
  ? 'Some scaling strategy mentioned'
13
16
  : 'Not addressed',
14
- requiredHandling: 'Implement caching (Redis/CDN). Use connection pooling. Consider auto-scaling. Load test before launch.',
15
- dataConsistency: 'Ensure cache invalidation strategy. Handle stale reads gracefully.',
16
- userExperience: 'Graceful degradation: serve cached content, queue non-critical operations.',
17
+ requiredHandling: 'Define a measurable load envelope and verify the declared scaling strategy.',
18
+ dataConsistency: 'Verify consistency behavior at the declared capacity boundary.',
19
+ userExperience: 'Use the degradation behavior stated in the service contract.',
17
20
  });
18
21
  // Data growth
19
- if (lower.includes('database') ||
20
- lower.includes('table') ||
21
- lower.includes('query') ||
22
- lower.includes('list') ||
23
- lower.includes('search')) {
22
+ if (capabilities.database) {
24
23
  scenarios.push({
25
- scenario: 'Database grows to millions of rows, queries slow down',
24
+ scenario: 'Database grows beyond the query-performance envelope',
26
25
  probability: 'medium',
27
26
  impact: 'medium',
28
- currentHandling: contentMentions(lower, ['index', 'pagination', 'cursor', 'partition'])
27
+ currentHandling: contentMentions(content, ['index', 'pagination', 'cursor', 'partition'])
29
28
  ? 'Some query optimization mentioned'
30
29
  : 'Not addressed',
31
- requiredHandling: 'Add database indexes on query columns. Use cursor-based pagination. Consider archiving old data.',
30
+ requiredHandling: 'Define expected data growth and verify the stated indexing and pagination plan.',
32
31
  dataConsistency: 'Indexes must be maintained. Monitor query plans.',
33
32
  userExperience: 'Consistent response times through pagination. No full-table scans.',
34
33
  });
@@ -1,3 +1,3 @@
1
1
  import type { FailureScenario, Spec, ProjectKnowledge } from '../../types/index.js';
2
- export declare function generateSecurityScenarios(_spec: Spec, content: string, _knowledge: ProjectKnowledge): FailureScenario[];
2
+ export declare function generateSecurityScenarios(spec: Spec, content: string, _knowledge: ProjectKnowledge): FailureScenario[];
3
3
  //# sourceMappingURL=scenarios-security.d.ts.map