@planu/cli 4.11.1 → 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.
- package/CHANGELOG.md +9 -0
- package/dist/engine/cascade-hooks/registry.js +0 -2
- package/dist/engine/challenge-scenarios-loader.d.ts +3 -3
- package/dist/engine/compliance-injector.d.ts +3 -3
- package/dist/engine/dashboard/kanban-logic.d.ts +1 -1
- package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +1 -1
- package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +1 -1
- package/dist/engine/elicitation/answer-extractor.d.ts +1 -4
- package/dist/engine/elicitation/answer-extractor.js +197 -68
- package/dist/engine/elicitation/decision-gap-detector.js +59 -79
- package/dist/engine/elicitation/index.d.ts +0 -1
- package/dist/engine/elicitation/index.js +0 -1
- package/dist/engine/elicitation/question-grounding-gate.js +83 -34
- package/dist/engine/evidence-gates/lifecycle-gate.js +27 -14
- package/dist/engine/evidence-index/index-builder.js +7 -2
- package/dist/engine/frontmatter-sha/frozen-fields.d.ts +1 -1
- package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.d.ts +2 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +22 -22
- package/dist/engine/human-summary.js +3 -3
- package/dist/engine/implementation-contract/common.d.ts +1 -1
- package/dist/engine/keyword-matcher/stopwords.d.ts +2 -2
- package/dist/engine/provider-adapters/adapters/claude.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gemini.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gpt4.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/markdown.d.ts +1 -1
- package/dist/engine/safety/atomic-write-file.d.ts +1 -1
- package/dist/engine/safety/cross-process-lock.d.ts +1 -1
- package/dist/engine/safety/path-sanitizer.d.ts +1 -1
- package/dist/engine/security-analyzer/profile-detector.js +48 -16
- package/dist/engine/spec-format/copula-blacklist.d.ts +2 -2
- package/dist/engine/spec-format/metadata-value-policy.d.ts +20 -20
- package/dist/engine/text-signal-boundaries.js +45 -12
- package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
- package/dist/engine/well-known/manifest-schema.d.ts +33 -33
- package/dist/engine/workers/schema.d.ts +3 -3
- package/dist/hosts/claude-code/ux/mcp-resources.d.ts +4 -4
- package/dist/hosts/codex/rest-shim.d.ts +1 -1
- package/dist/native/lightweight-command-catalog.d.ts +30 -30
- package/dist/server/openapi-spec.d.ts +132 -132
- package/dist/tools/challenge-spec/event-challenge-scenarios.js +8 -27
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-a.js +16 -32
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-b.js +15 -20
- package/dist/tools/challenge-spec/scenario-collector.d.ts +13 -0
- package/dist/tools/challenge-spec/scenario-collector.js +86 -0
- package/dist/tools/challenge-spec/scenarios-data.js +10 -9
- package/dist/tools/challenge-spec/scenarios-failure.js +38 -54
- package/dist/tools/challenge-spec/scenarios-scale.js +14 -15
- package/dist/tools/challenge-spec/scenarios-security.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-security.js +18 -11
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +31 -5
- package/dist/tools/challenge-spec/scenarios-utils.js +102 -6
- package/dist/tools/challenge-spec/security-challenge-scenarios.d.ts +1 -1
- package/dist/tools/challenge-spec/security-challenge-scenarios.js +20 -25
- package/dist/tools/challenge-spec.js +39 -118
- package/dist/tools/compliance-gate-handler.d.ts +1 -1
- package/dist/tools/create-spec.js +4 -24
- package/dist/tools/delete-first/suggest-deletions.d.ts +1 -1
- package/dist/tools/generate-batch-script.d.ts +2 -2
- package/dist/tools/init-project/rules-generator.js +0 -1
- package/dist/tools/oauth-handler.d.ts +1 -1
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/render-spec-for-provider.d.ts +2 -2
- package/dist/tools/scan-orphan-spec-refs.d.ts +6 -6
- package/dist/tools/schemas/agents.d.ts +7 -7
- package/dist/tools/schemas/analysis.d.ts +11 -11
- package/dist/tools/schemas/delete.d.ts +5 -5
- package/dist/tools/schemas/github.d.ts +4 -4
- package/dist/tools/schemas/ide-config.d.ts +12 -12
- package/dist/tools/schemas/infra.d.ts +9 -9
- package/dist/tools/schemas/lifecycle.d.ts +50 -50
- package/dist/tools/schemas/llm-provider-schemas.d.ts +7 -7
- package/dist/tools/schemas/plugins-schemas.d.ts +12 -12
- package/dist/tools/schemas/project-graph.d.ts +6 -6
- package/dist/tools/schemas/rbac.d.ts +2 -2
- package/dist/tools/schemas/registry.d.ts +3 -3
- package/dist/tools/schemas/runtime-security.d.ts +2 -2
- package/dist/tools/schemas/session.d.ts +2 -2
- package/dist/tools/schemas/spec.d.ts +15 -15
- package/dist/tools/schemas/token-intelligence.d.ts +6 -6
- package/dist/tools/schemas/token-optimization.d.ts +2 -2
- package/dist/tools/schemas/workers-schema.d.ts +4 -4
- package/dist/tools/skill-bootstrap-handler.d.ts +7 -7
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +1 -1
- package/dist/tools/triage-request.d.ts +3 -3
- package/dist/tools/update-status/file-sync.d.ts +1 -1
- package/dist/tools/update-status/file-sync.js +10 -45
- package/dist/tools/update-status/index.js +28 -27
- package/dist/tools/update-status/response-builder.d.ts +1 -1
- package/dist/tools/update-status/response-builder.js +11 -19
- package/dist/tools/update-status/transition-guard.js +1 -1
- package/dist/tools/update-status-actions.d.ts +0 -1
- package/dist/tools/update-status-actions.js +10 -26
- package/dist/tools/update-status-convention-gate.d.ts +1 -6
- package/dist/tools/update-status-convention-gate.js +9 -33
- package/dist/types/docs.d.ts +0 -4
- package/dist/types/env.d.ts +0 -4
- package/dist/types/estimation.d.ts +0 -2
- package/dist/types/multi-teammate-review.d.ts +4 -4
- package/dist/types/plugin-install.d.ts +7 -7
- package/package.json +21 -18
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
- package/src/i18n/messages/en.json +0 -72
- package/src/i18n/messages/es.json +0 -72
- package/src/i18n/messages/pt.json +0 -72
- package/dist/config/elicitation-dimensions.json +0 -51
- package/dist/engine/actuals-estimator.d.ts +0 -14
- package/dist/engine/actuals-estimator.js +0 -64
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.d.ts +0 -3
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.js +0 -19
- package/dist/engine/elicitation/option-builder.d.ts +0 -9
- package/dist/engine/elicitation/option-builder.js +0 -85
- package/dist/engine/progress-writer.d.ts +0 -10
- package/dist/engine/progress-writer.js +0 -63
- package/dist/tools/update-status/mode-hints.d.ts +0 -4
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 —
|
|
165
|
-
'
|
|
166
|
-
'
|
|
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: '
|
|
173
|
-
|
|
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: '
|
|
168
|
+
userExperience: 'Use the dependency-failure response and latency bound declared by the client contract.',
|
|
180
169
|
},
|
|
181
170
|
{
|
|
182
|
-
scenario: `[${spec.id}] Resilience —
|
|
183
|
-
'If
|
|
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: '
|
|
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: '
|
|
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
|
|
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
|
|
16
|
-
requiredHandling: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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: '
|
|
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
|
|
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: '
|
|
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
|
|
131
|
-
'If
|
|
132
|
-
'how long
|
|
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
|
-
'
|
|
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: '
|
|
24
|
-
dataConsistency: '
|
|
25
|
-
userExperience: '
|
|
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: '
|
|
38
|
-
dataConsistency: 'Define
|
|
39
|
-
userExperience: '
|
|
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: '
|
|
52
|
-
dataConsistency: '
|
|
53
|
-
userExperience: '
|
|
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
|
|
5
|
+
const capabilities = detectChallengeCapabilities(spec, content);
|
|
6
6
|
// Network failures
|
|
7
|
-
if (
|
|
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(
|
|
12
|
+
currentHandling: contentMentions(content, ['timeout', 'retry', 'fallback'])
|
|
17
13
|
? 'Partially addressed in spec'
|
|
18
14
|
: 'Not addressed in spec',
|
|
19
|
-
requiredHandling: '
|
|
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: '
|
|
17
|
+
userExperience: 'Use the failure behavior defined by the client contract.',
|
|
22
18
|
});
|
|
23
19
|
}
|
|
24
20
|
// Database failures
|
|
25
|
-
if (
|
|
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: '
|
|
37
|
-
dataConsistency: '
|
|
38
|
-
userExperience: 'Return
|
|
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(
|
|
35
|
+
currentHandling: contentMentions(content, ['migration', 'rollback', 'reversible'])
|
|
45
36
|
? 'Partially addressed'
|
|
46
37
|
: 'Not addressed',
|
|
47
|
-
requiredHandling: '
|
|
48
|
-
dataConsistency: 'Partial schema changes
|
|
49
|
-
userExperience: '
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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 (
|
|
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
|
|
60
|
+
scenario: 'Third-party service declared by the spec becomes unavailable',
|
|
73
61
|
probability: 'medium',
|
|
74
62
|
impact: 'critical',
|
|
75
|
-
currentHandling: contentMentions(
|
|
63
|
+
currentHandling: contentMentions(content, ['fallback', 'queue', 'retry', 'circuit'])
|
|
76
64
|
? 'Partially addressed'
|
|
77
65
|
: 'Not addressed',
|
|
78
|
-
requiredHandling: '
|
|
79
|
-
dataConsistency: '
|
|
80
|
-
userExperience: '
|
|
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 (
|
|
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(
|
|
77
|
+
currentHandling: contentMentions(content, ['limit', 'stream', 'chunk', 'pagination'])
|
|
94
78
|
? 'Partially addressed'
|
|
95
79
|
: 'Not addressed',
|
|
96
|
-
requiredHandling: '
|
|
97
|
-
dataConsistency: '
|
|
98
|
-
userExperience: '
|
|
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
|
|
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
|
|
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(
|
|
14
|
+
currentHandling: contentMentions(content, ['cache', 'cdn', 'queue', 'scale', 'auto-scale'])
|
|
12
15
|
? 'Some scaling strategy mentioned'
|
|
13
16
|
: 'Not addressed',
|
|
14
|
-
requiredHandling: '
|
|
15
|
-
dataConsistency: '
|
|
16
|
-
userExperience: '
|
|
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 (
|
|
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
|
|
24
|
+
scenario: 'Database grows beyond the query-performance envelope',
|
|
26
25
|
probability: 'medium',
|
|
27
26
|
impact: 'medium',
|
|
28
|
-
currentHandling: contentMentions(
|
|
27
|
+
currentHandling: contentMentions(content, ['index', 'pagination', 'cursor', 'partition'])
|
|
29
28
|
? 'Some query optimization mentioned'
|
|
30
29
|
: 'Not addressed',
|
|
31
|
-
requiredHandling: '
|
|
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(
|
|
2
|
+
export declare function generateSecurityScenarios(spec: Spec, content: string, _knowledge: ProjectKnowledge): FailureScenario[];
|
|
3
3
|
//# sourceMappingURL=scenarios-security.d.ts.map
|