@cassiomc1/forgeloop 1.11.1 → 1.13.0
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/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +90 -0
- package/DOCS_INDEX.md +13 -11
- package/ENG/c-development-eng.md +112 -0
- package/ENG/cpp-development-eng.md +109 -0
- package/ENG/dotnet-aspnetcore-development-eng.md +401 -0
- package/ENG/flutter-development-eng.md +2106 -0
- package/ENG/go-development-eng.md +103 -0
- package/ENG/java-development-eng.md +125 -0
- package/ENG/nodejs-backend-development-eng.md +605 -0
- package/ENG/php-development-eng.md +104 -0
- package/ENG/rust-development-eng.md +422 -0
- package/ENG/sql-development-eng.md +108 -0
- package/ENG/swift-development-eng.md +111 -0
- package/ENG/typescript-development-eng.md +108 -0
- package/GUIDE_ROUTER.md +455 -4
- package/LOOP_SYSTEM_DESIGN.md +10 -6
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +50 -38
- package/THIRD_PARTY_NOTICES.md +19 -7
- package/completions/_forgeloop +3 -3
- package/completions/forgeloop.bash +3 -3
- package/completions/forgeloop.fish +7 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +55 -2
- package/docs/CLI_REFERENCE.md +28 -6
- package/docs/DOCUMENTATION_GUIDE.md +2 -1
- package/docs/GETTING_STARTED.md +59 -0
- package/docs/MCP.md +1 -1
- package/docs/PACKAGE_CONTENTS.md +29 -8
- package/docs/RECIPES.md +23 -0
- package/docs/RELEASE_CHECKLIST.md +51 -9
- package/docs/TROUBLESHOOTING.md +100 -2
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +19 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5 -5
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +12 -2
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +3 -3
- package/docs/documentation-manifest.json +652 -0
- package/docs/protocol-requirements.json +77 -0
- package/package.json +19 -4
- package/schemas/routing-input.schema.json +14 -1
- package/scripts/CI_VALIDATORS.md +84 -11
- package/scripts/generate-agent-protocol-summary.mjs +36 -0
- package/src/commands/next.js +19 -7
- package/src/commands/route.js +5 -0
- package/src/commands/task-create.js +84 -25
- package/src/commands/task-list.js +22 -2
- package/src/config/guides.json +48 -0
- package/src/core/build-script.js +151 -0
- package/src/core/c-cpp-project.js +143 -0
- package/src/core/cli-command-definitions.js +8 -1
- package/src/core/command-executors.js +5 -3
- package/src/core/command-input.js +140 -102
- package/src/core/contract-presets.js +82 -0
- package/src/core/error-codes.js +3 -3
- package/src/core/filesystem.js +1 -10
- package/src/core/go-project.js +206 -0
- package/src/core/java-project.js +403 -0
- package/src/core/multi-language-project.js +117 -0
- package/src/core/next-explanation.js +63 -0
- package/src/core/php-project.js +85 -0
- package/src/core/project-detection.js +2227 -0
- package/src/core/reconcile-closure.js +4 -1
- package/src/core/router.js +233 -7
- package/src/core/rust-project.js +400 -0
- package/src/core/sql-project.js +141 -0
- package/src/core/swift-project.js +200 -0
- package/src/core/typescript-project.js +349 -0
- package/src/core/xml-structure.js +123 -0
|
@@ -7,6 +7,7 @@ import { createReceipt } from "./receipt.js";
|
|
|
7
7
|
import { currentRepositoryFingerprint } from "./repository.js";
|
|
8
8
|
import { taskArtifactPath } from "./task-paths.js";
|
|
9
9
|
import { classifyLoadedWorkState, readWorkState, mutateWorkState } from "./work-state.js";
|
|
10
|
+
import { classifyRequirement } from "./evidence-readiness.js";
|
|
10
11
|
|
|
11
12
|
export const RECONCILE_EVENT = "CHECKPOINT_RECONCILED";
|
|
12
13
|
|
|
@@ -133,7 +134,9 @@ export async function runReconcileClosure({
|
|
|
133
134
|
if (typeof item === "string") {
|
|
134
135
|
return item === requirement;
|
|
135
136
|
}
|
|
136
|
-
return item.type === "VERIFICATION"
|
|
137
|
+
return classifyRequirement(item).type === "VERIFICATION"
|
|
138
|
+
&& item.id === checkId
|
|
139
|
+
&& item.text === requirement;
|
|
137
140
|
});
|
|
138
141
|
if (!verificationItem) {
|
|
139
142
|
throw reconcileError(
|
package/src/core/router.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GUIDE_IDS, PROTOCOL_VERSION } from "./protocol.js";
|
|
2
2
|
import { assertExecutionProfile, resolveExecutionProfile } from "./execution-profile.js";
|
|
3
|
+
import { PROJECT_EVIDENCE_SCOPES } from "./project-detection.js";
|
|
3
4
|
|
|
4
5
|
export const ROUTING_SCHEMA_VERSION = 1;
|
|
5
6
|
|
|
@@ -61,6 +62,22 @@ const SIGNALS = Object.freeze({
|
|
|
61
62
|
platforms: new Set(["web", "mobile", "desktop", "server", "ci", "cross-platform"]),
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
const PROJECT_FRAMEWORKS = new Set([
|
|
66
|
+
"flutter", "dotnet", "aspnetcore", "abp", "nodejs", "rust",
|
|
67
|
+
"c", "cpp", "java", "sql", "go", "typescript", "php", "swift",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const LANGUAGE_PROJECT_GUIDES = Object.freeze([
|
|
71
|
+
["c", "PROJECT_C_CONFIRMED"],
|
|
72
|
+
["cpp", "PROJECT_CPP_CONFIRMED"],
|
|
73
|
+
["java", "PROJECT_JAVA_CONFIRMED"],
|
|
74
|
+
["go", "PROJECT_GO_CONFIRMED"],
|
|
75
|
+
["typescript", "PROJECT_TYPESCRIPT_CONFIRMED"],
|
|
76
|
+
["php", "PROJECT_PHP_CONFIRMED"],
|
|
77
|
+
["swift", "PROJECT_SWIFT_CONFIRMED"],
|
|
78
|
+
["sql", "PROJECT_SQL_CONFIRMED"],
|
|
79
|
+
]);
|
|
80
|
+
|
|
64
81
|
export const PLATFORM_SEMANTICS = Object.freeze({
|
|
65
82
|
web: Object.freeze({
|
|
66
83
|
mode: "informational-only",
|
|
@@ -121,6 +138,165 @@ const PRIMARY_GUIDES = Object.freeze({
|
|
|
121
138
|
"ui-copy": "design",
|
|
122
139
|
});
|
|
123
140
|
|
|
141
|
+
function hasFlutterWorkContext(workType) {
|
|
142
|
+
return !["documentation", "ui-copy"].includes(workType);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function addFlutterProjectGuides(input, add) {
|
|
146
|
+
const projectEvidence = input.projectEvidence;
|
|
147
|
+
if (!projectEvidence?.frameworks.includes("flutter")
|
|
148
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
149
|
+
|| !hasFlutterWorkContext(input.workType)) return;
|
|
150
|
+
add("flutter", "PROJECT_FLUTTER_SDK_DEPENDENCY");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function hasDotNetWorkContext(workType) {
|
|
154
|
+
return !["documentation", "ui-copy"].includes(workType);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function addDotNetProjectGuides(input, add) {
|
|
158
|
+
const projectEvidence = input.projectEvidence;
|
|
159
|
+
if (!projectEvidence?.frameworks.includes("dotnet")
|
|
160
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
161
|
+
|| !hasDotNetWorkContext(input.workType)) return;
|
|
162
|
+
add("dotnet", "PROJECT_DOTNET_SDK_PROJECT");
|
|
163
|
+
if (projectEvidence.frameworks.includes("aspnetcore")) add("dotnet", "PROJECT_ASPNETCORE_CONFIRMED");
|
|
164
|
+
if (projectEvidence.frameworks.includes("abp")) add("dotnet", "PROJECT_ABP_CONFIRMED");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function hasNodeJsWorkContext(workType) {
|
|
168
|
+
return !["documentation", "ui-copy", "mobile-ui"].includes(workType);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function addNodeJsProjectGuides(input, add) {
|
|
172
|
+
const projectEvidence = input.projectEvidence;
|
|
173
|
+
if (!projectEvidence?.frameworks.includes("nodejs")
|
|
174
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
175
|
+
|| !hasNodeJsWorkContext(input.workType)) return;
|
|
176
|
+
add("nodejs", "PROJECT_NODEJS_CONFIRMED");
|
|
177
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":dependencies.") || signal.includes(":optionalDependencies."))) {
|
|
178
|
+
add("nodejs", "PROJECT_NODEJS_BACKEND_FRAMEWORK");
|
|
179
|
+
}
|
|
180
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":scripts."))) {
|
|
181
|
+
add("nodejs", "PROJECT_NODEJS_RUNTIME_SCRIPT");
|
|
182
|
+
}
|
|
183
|
+
if (projectEvidence.primarySignals.some((signal) => signal.includes(":import="))) {
|
|
184
|
+
add("nodejs", "PROJECT_NODEJS_SERVER_RUNTIME");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function hasRustWorkContext(workType) {
|
|
189
|
+
return !["documentation", "ui-copy", "mobile-ui"].includes(workType);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function addRustProjectGuides(input, add) {
|
|
193
|
+
const projectEvidence = input.projectEvidence;
|
|
194
|
+
if (!projectEvidence?.frameworks.includes("rust")
|
|
195
|
+
|| !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
196
|
+
|| !hasRustWorkContext(input.workType)) return;
|
|
197
|
+
add("rust", "PROJECT_RUST_CONFIRMED");
|
|
198
|
+
if (projectEvidence.primarySignals.some((signal) => signal.endsWith(":package"))) {
|
|
199
|
+
add("rust", "PROJECT_RUST_CARGO_PACKAGE");
|
|
200
|
+
}
|
|
201
|
+
if (projectEvidence.primarySignals.some((signal) => signal.endsWith(":workspace"))) {
|
|
202
|
+
add("rust", "PROJECT_RUST_CARGO_WORKSPACE");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function hasLanguageWorkContext(workType) {
|
|
207
|
+
return !["documentation", "ui-copy"].includes(workType);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function addLanguageProjectGuides(input, add) {
|
|
211
|
+
const projectEvidence = input.projectEvidence;
|
|
212
|
+
if (!projectEvidence || !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)
|
|
213
|
+
|| !hasLanguageWorkContext(input.workType)) return;
|
|
214
|
+
for (const [guide, reason] of LANGUAGE_PROJECT_GUIDES) {
|
|
215
|
+
if (projectEvidence.frameworks.includes(guide)) add(guide, reason);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function addProjectBaselineGuides(input, add) {
|
|
220
|
+
const projectEvidence = input.projectEvidence;
|
|
221
|
+
if (!projectEvidence || !["MATCH", "UNSCOPED"].includes(projectEvidence.scope)) return;
|
|
222
|
+
if (projectEvidence.frameworks.includes("flutter") && hasFlutterWorkContext(input.workType)) {
|
|
223
|
+
add("clean", "PROJECT_FLUTTER_BASELINE");
|
|
224
|
+
add("test", "PROJECT_FLUTTER_BASELINE");
|
|
225
|
+
}
|
|
226
|
+
if (projectEvidence.frameworks.includes("dotnet") && hasDotNetWorkContext(input.workType)) {
|
|
227
|
+
add("clean", "PROJECT_DOTNET_BASELINE");
|
|
228
|
+
add("test", "PROJECT_DOTNET_BASELINE");
|
|
229
|
+
}
|
|
230
|
+
if (projectEvidence.frameworks.includes("nodejs") && hasNodeJsWorkContext(input.workType)) {
|
|
231
|
+
add("clean", "PROJECT_NODEJS_BASELINE");
|
|
232
|
+
add("test", "PROJECT_NODEJS_BASELINE");
|
|
233
|
+
}
|
|
234
|
+
if (projectEvidence.frameworks.includes("rust") && hasRustWorkContext(input.workType)) {
|
|
235
|
+
add("clean", "PROJECT_RUST_BASELINE");
|
|
236
|
+
add("test", "PROJECT_RUST_BASELINE");
|
|
237
|
+
}
|
|
238
|
+
if (hasLanguageWorkContext(input.workType)) {
|
|
239
|
+
for (const [language] of LANGUAGE_PROJECT_GUIDES) {
|
|
240
|
+
if (!projectEvidence.frameworks.includes(language)) continue;
|
|
241
|
+
add("clean", `PROJECT_${language.toUpperCase()}_BASELINE`);
|
|
242
|
+
add("test", `PROJECT_${language.toUpperCase()}_BASELINE`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function flutterExclusionReason(projectEvidence, workType) {
|
|
248
|
+
if (!projectEvidence) return "NO_FLUTTER_PROJECT_EVIDENCE";
|
|
249
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_FLUTTER_SCOPE_MATCH";
|
|
250
|
+
if (!projectEvidence.frameworks.includes("flutter")) return "NO_FLUTTER_PRIMARY_EVIDENCE";
|
|
251
|
+
if (!hasFlutterWorkContext(workType)) return "NO_FLUTTER_EXECUTABLE_WORK";
|
|
252
|
+
return "NO_FLUTTER_PRIMARY_EVIDENCE";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function dotNetExclusionReason(projectEvidence, workType) {
|
|
256
|
+
if (!projectEvidence) return "NO_DOTNET_PROJECT_EVIDENCE";
|
|
257
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_DOTNET_SCOPE_MATCH";
|
|
258
|
+
if (!projectEvidence.frameworks.includes("dotnet")) return "NO_DOTNET_PRIMARY_EVIDENCE";
|
|
259
|
+
if (!hasDotNetWorkContext(workType)) return "NO_DOTNET_EXECUTABLE_WORK";
|
|
260
|
+
return "NO_DOTNET_PRIMARY_EVIDENCE";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function nodeJsExclusionReason(projectEvidence, workType) {
|
|
264
|
+
if (!projectEvidence) return "NO_NODEJS_PROJECT_EVIDENCE";
|
|
265
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_NODEJS_SCOPE_MATCH";
|
|
266
|
+
if (!projectEvidence.frameworks.includes("nodejs")) return "NO_NODEJS_PRIMARY_EVIDENCE";
|
|
267
|
+
if (!hasNodeJsWorkContext(workType)) return "NO_NODEJS_EXECUTABLE_WORK";
|
|
268
|
+
return "NO_NODEJS_PRIMARY_EVIDENCE";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function rustExclusionReason(projectEvidence, workType) {
|
|
272
|
+
if (!projectEvidence) return "NO_RUST_PROJECT_EVIDENCE";
|
|
273
|
+
if (projectEvidence.scope === "NO_MATCH") return "NO_RUST_SCOPE_MATCH";
|
|
274
|
+
if (!projectEvidence.frameworks.includes("rust")) return "NO_RUST_PRIMARY_EVIDENCE";
|
|
275
|
+
if (!hasRustWorkContext(workType)) return "NO_RUST_EXECUTABLE_WORK";
|
|
276
|
+
return "NO_RUST_PRIMARY_EVIDENCE";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function exclusionReasonForGuide(guide, projectEvidence, workType) {
|
|
280
|
+
if (guide === "security") return "NO_TRUST_BOUNDARY";
|
|
281
|
+
if (guide === "performance") return "NO_MEASURABLE_PERFORMANCE_RISK";
|
|
282
|
+
if (guide === "design" || guide === "accessibility") return "NO_UI_SURFACE";
|
|
283
|
+
if (guide === "premium" || guide === "games") return "NO_PRIMARY_WORK_TYPE";
|
|
284
|
+
if (guide === "taste") return "NO_TASTE_FRONTEND_CONTEXT";
|
|
285
|
+
if (guide === "documentation") return "NO_DOCUMENTATION_SURFACE";
|
|
286
|
+
if (guide === "flutter") return flutterExclusionReason(projectEvidence, workType);
|
|
287
|
+
if (guide === "dotnet") return dotNetExclusionReason(projectEvidence, workType);
|
|
288
|
+
if (guide === "nodejs") return nodeJsExclusionReason(projectEvidence, workType);
|
|
289
|
+
if (guide === "rust") return rustExclusionReason(projectEvidence, workType);
|
|
290
|
+
if (LANGUAGE_PROJECT_GUIDES.some(([language]) => language === guide)) {
|
|
291
|
+
if (!projectEvidence) return `NO_${guide.toUpperCase()}_PROJECT_EVIDENCE`;
|
|
292
|
+
if (projectEvidence.scope === "NO_MATCH") return `NO_${guide.toUpperCase()}_SCOPE_MATCH`;
|
|
293
|
+
if (!projectEvidence.frameworks.includes(guide)) return `NO_${guide.toUpperCase()}_PRIMARY_EVIDENCE`;
|
|
294
|
+
if (!hasLanguageWorkContext(workType)) return `NO_${guide.toUpperCase()}_EXECUTABLE_WORK`;
|
|
295
|
+
return `NO_${guide.toUpperCase()}_PRIMARY_EVIDENCE`;
|
|
296
|
+
}
|
|
297
|
+
return "NO_BEHAVIOR_OR_EXECUTABLE_CHANGE";
|
|
298
|
+
}
|
|
299
|
+
|
|
124
300
|
export class RouteInputError extends Error {
|
|
125
301
|
constructor(message) {
|
|
126
302
|
super(message);
|
|
@@ -151,6 +327,50 @@ function normalizeArray(value, name, allowed) {
|
|
|
151
327
|
return [...seen].sort();
|
|
152
328
|
}
|
|
153
329
|
|
|
330
|
+
function normalizeStringList(value, name) {
|
|
331
|
+
if (value === undefined) return [];
|
|
332
|
+
if (!Array.isArray(value)) throw new RouteInputError(`${name} must be an array`);
|
|
333
|
+
const seen = new Set();
|
|
334
|
+
for (const item of value) {
|
|
335
|
+
if (typeof item !== "string" || item.trim() === "") {
|
|
336
|
+
throw new RouteInputError(`${name} must contain non-empty strings`);
|
|
337
|
+
}
|
|
338
|
+
if (seen.has(item)) throw new RouteInputError(`Duplicate ${name.slice(0, -1)}: ${item}`);
|
|
339
|
+
seen.add(item);
|
|
340
|
+
}
|
|
341
|
+
return [...seen].sort();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function normalizeProjectEvidence(value) {
|
|
345
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
346
|
+
throw new RouteInputError("projectEvidence must be an object");
|
|
347
|
+
}
|
|
348
|
+
if (value.schemaVersion !== undefined && value.schemaVersion !== ROUTING_SCHEMA_VERSION) {
|
|
349
|
+
throw new RouteInputError(`Unsupported projectEvidence schema version: ${value.schemaVersion}`);
|
|
350
|
+
}
|
|
351
|
+
const frameworks = normalizeArray(value.frameworks, "frameworks", PROJECT_FRAMEWORKS);
|
|
352
|
+
const hasDotnet = frameworks.includes("dotnet");
|
|
353
|
+
const invalidOverlays = frameworks.filter((framework) => framework === "aspnetcore" || framework === "abp");
|
|
354
|
+
if (!hasDotnet && invalidOverlays.length > 0) {
|
|
355
|
+
const noun = invalidOverlays.length === 1 ? "framework" : "frameworks";
|
|
356
|
+
const verb = invalidOverlays.length === 1 ? "requires" : "require";
|
|
357
|
+
const labels = invalidOverlays.map((framework) => `"${framework}"`).join(", ");
|
|
358
|
+
throw new RouteInputError(`projectEvidence ${noun} ${labels} ${verb} "dotnet"`);
|
|
359
|
+
}
|
|
360
|
+
const scope = value.scope ?? (frameworks.length > 0 ? "UNSCOPED" : "NONE");
|
|
361
|
+
if (!PROJECT_EVIDENCE_SCOPES.includes(scope)) {
|
|
362
|
+
throw new RouteInputError(`Unknown project evidence scope: ${scope}`);
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
schemaVersion: ROUTING_SCHEMA_VERSION,
|
|
366
|
+
scope,
|
|
367
|
+
frameworks,
|
|
368
|
+
projectRoots: normalizeStringList(value.projectRoots, "projectRoots"),
|
|
369
|
+
primarySignals: normalizeStringList(value.primarySignals, "primarySignals"),
|
|
370
|
+
supportingSignals: normalizeStringList(value.supportingSignals, "supportingSignals"),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
154
374
|
export function normalizeRouteInput(input = {}) {
|
|
155
375
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
156
376
|
throw new RouteInputError("Route input must be an object");
|
|
@@ -163,6 +383,9 @@ export function normalizeRouteInput(input = {}) {
|
|
|
163
383
|
throw new RouteInputError(`${key} must be boolean`);
|
|
164
384
|
}
|
|
165
385
|
}
|
|
386
|
+
const projectEvidence = input.projectEvidence === undefined
|
|
387
|
+
? undefined
|
|
388
|
+
: normalizeProjectEvidence(input.projectEvidence);
|
|
166
389
|
return {
|
|
167
390
|
schemaVersion: ROUTING_SCHEMA_VERSION,
|
|
168
391
|
workType: input.workType,
|
|
@@ -171,6 +394,7 @@ export function normalizeRouteInput(input = {}) {
|
|
|
171
394
|
platforms: normalizeArray(input.platforms, "platforms", SIGNALS.platforms),
|
|
172
395
|
behaviorChange: input.behaviorChange ?? false,
|
|
173
396
|
executableChange: input.executableChange ?? false,
|
|
397
|
+
...(projectEvidence ? { projectEvidence } : {}),
|
|
174
398
|
};
|
|
175
399
|
}
|
|
176
400
|
|
|
@@ -186,6 +410,14 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
186
410
|
selected.set(guide, reasons);
|
|
187
411
|
}
|
|
188
412
|
|
|
413
|
+
const projectEvidence = normalized.projectEvidence;
|
|
414
|
+
addFlutterProjectGuides(normalized, add);
|
|
415
|
+
addDotNetProjectGuides(normalized, add);
|
|
416
|
+
addNodeJsProjectGuides(normalized, add);
|
|
417
|
+
addRustProjectGuides(normalized, add);
|
|
418
|
+
addLanguageProjectGuides(normalized, add);
|
|
419
|
+
addProjectBaselineGuides(normalized, add);
|
|
420
|
+
|
|
189
421
|
const workReason = reasonForWorkType(normalized.workType);
|
|
190
422
|
for (const guide of WORK_GUIDES[normalized.workType]) add(guide, workReason);
|
|
191
423
|
|
|
@@ -246,13 +478,7 @@ export function evaluateRoute(input = {}, profileOptions = {}) {
|
|
|
246
478
|
|
|
247
479
|
for (const guide of GUIDE_IDS) {
|
|
248
480
|
if (selected.has(guide)) continue;
|
|
249
|
-
|
|
250
|
-
else if (guide === "performance") excluded[guide] = ["NO_MEASURABLE_PERFORMANCE_RISK"];
|
|
251
|
-
else if (guide === "design" || guide === "accessibility") excluded[guide] = ["NO_UI_SURFACE"];
|
|
252
|
-
else if (guide === "premium" || guide === "games") excluded[guide] = ["NO_PRIMARY_WORK_TYPE"];
|
|
253
|
-
else if (guide === "taste") excluded[guide] = ["NO_TASTE_FRONTEND_CONTEXT"];
|
|
254
|
-
else if (guide === "documentation") excluded[guide] = ["NO_DOCUMENTATION_SURFACE"];
|
|
255
|
-
else excluded[guide] = ["NO_BEHAVIOR_OR_EXECUTABLE_CHANGE"];
|
|
481
|
+
excluded[guide] = [exclusionReasonForGuide(guide, projectEvidence, normalized.workType)];
|
|
256
482
|
}
|
|
257
483
|
|
|
258
484
|
const guides = [...selected.keys()];
|