@ryuenn3123/agentic-senior-core 2.5.19 → 2.5.20
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/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.agent-context/state/stack-research-snapshot.json +112 -0
- package/.cursorrules +1 -1
- package/.windsurfrules +1 -1
- package/README.md +12 -0
- package/lib/cli/architect.mjs +656 -5
- package/lib/cli/commands/init.mjs +48 -0
- package/lib/cli/utils.mjs +4 -1
- package/package.json +1 -1
- package/scripts/validate.mjs +113 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"snapshotId": "ecosystem-signals-2026-04-18",
|
|
4
|
+
"generatedAt": "2026-04-18T00:00:00.000Z",
|
|
5
|
+
"deterministic": true,
|
|
6
|
+
"sourceName": "Agentic-Senior-Core deterministic stack research snapshot",
|
|
7
|
+
"sourceUrl": "state://stack-research-snapshot/2026-04-18",
|
|
8
|
+
"trustedRealtimeSources": [
|
|
9
|
+
{
|
|
10
|
+
"sourceId": "awwwards-trend-feed",
|
|
11
|
+
"sourceName": "Awwwards Trend Feed",
|
|
12
|
+
"sourceUrl": "https://www.awwwards.com"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"sourceId": "github-ecosystem-signals",
|
|
16
|
+
"sourceName": "GitHub Ecosystem Signals",
|
|
17
|
+
"sourceUrl": "https://github.com"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"stackSignals": [
|
|
21
|
+
{
|
|
22
|
+
"stackFileName": "typescript.md",
|
|
23
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
24
|
+
"metrics": {
|
|
25
|
+
"ecosystemMaturity": 0.91,
|
|
26
|
+
"talentAvailability": 0.9,
|
|
27
|
+
"deliveryVelocity": 0.89
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"stackFileName": "python.md",
|
|
32
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
33
|
+
"metrics": {
|
|
34
|
+
"ecosystemMaturity": 0.92,
|
|
35
|
+
"talentAvailability": 0.88,
|
|
36
|
+
"deliveryVelocity": 0.9
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"stackFileName": "java.md",
|
|
41
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
42
|
+
"metrics": {
|
|
43
|
+
"ecosystemMaturity": 0.89,
|
|
44
|
+
"talentAvailability": 0.83,
|
|
45
|
+
"deliveryVelocity": 0.8
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"stackFileName": "php.md",
|
|
50
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
51
|
+
"metrics": {
|
|
52
|
+
"ecosystemMaturity": 0.79,
|
|
53
|
+
"talentAvailability": 0.75,
|
|
54
|
+
"deliveryVelocity": 0.84
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"stackFileName": "go.md",
|
|
59
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
60
|
+
"metrics": {
|
|
61
|
+
"ecosystemMaturity": 0.84,
|
|
62
|
+
"talentAvailability": 0.78,
|
|
63
|
+
"deliveryVelocity": 0.82
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"stackFileName": "csharp.md",
|
|
68
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
69
|
+
"metrics": {
|
|
70
|
+
"ecosystemMaturity": 0.86,
|
|
71
|
+
"talentAvailability": 0.8,
|
|
72
|
+
"deliveryVelocity": 0.79
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"stackFileName": "rust.md",
|
|
77
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
78
|
+
"metrics": {
|
|
79
|
+
"ecosystemMaturity": 0.74,
|
|
80
|
+
"talentAvailability": 0.63,
|
|
81
|
+
"deliveryVelocity": 0.67
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"stackFileName": "ruby.md",
|
|
86
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
87
|
+
"metrics": {
|
|
88
|
+
"ecosystemMaturity": 0.7,
|
|
89
|
+
"talentAvailability": 0.62,
|
|
90
|
+
"deliveryVelocity": 0.72
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"stackFileName": "react-native.md",
|
|
95
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
96
|
+
"metrics": {
|
|
97
|
+
"ecosystemMaturity": 0.72,
|
|
98
|
+
"talentAvailability": 0.67,
|
|
99
|
+
"deliveryVelocity": 0.74
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"stackFileName": "flutter.md",
|
|
104
|
+
"measuredAt": "2026-04-18T00:00:00.000Z",
|
|
105
|
+
"metrics": {
|
|
106
|
+
"ecosystemMaturity": 0.75,
|
|
107
|
+
"talentAvailability": 0.69,
|
|
108
|
+
"deliveryVelocity": 0.76
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
package/.cursorrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
|
|
2
2
|
|
|
3
|
-
Generated by Agentic-Senior-Core CLI v2.5.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v2.5.20
|
|
4
4
|
Timestamp: 2026-04-18T03:00:00.000Z
|
|
5
5
|
Selected profile: beginner
|
|
6
6
|
Selected policy file: .agent-context/policies/llm-judge-threshold.json
|
package/.windsurfrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
|
|
2
2
|
|
|
3
|
-
Generated by Agentic-Senior-Core CLI v2.5.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v2.5.20
|
|
4
4
|
Timestamp: 2026-04-18T03:00:00.000Z
|
|
5
5
|
Selected profile: beginner
|
|
6
6
|
Selected policy file: .agent-context/policies/llm-judge-threshold.json
|
package/README.md
CHANGED
|
@@ -36,6 +36,18 @@ Project-description-first path (AI as Architect with veto control):
|
|
|
36
36
|
npx @ryuenn3123/agentic-senior-core init --project-description "Machine learning API for fraud detection"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
Deterministic stack research mode (default):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx @ryuenn3123/agentic-senior-core init --project-description "Event-driven payments platform" --architect-research-mode snapshot
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Optional trusted realtime enrichment (explicitly gated):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx @ryuenn3123/agentic-senior-core init --project-description "Modern conversion-focused product website" --architect-research-mode realtime --enable-realtime-research --architect-realtime-signal-file ./realtime-signals.json
|
|
49
|
+
```
|
|
50
|
+
|
|
39
51
|
---
|
|
40
52
|
|
|
41
53
|
## Before / After
|
package/lib/cli/architect.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
4
6
|
|
|
5
7
|
import { BLUEPRINT_RECOMMENDATIONS } from './constants.mjs';
|
|
6
8
|
import { ensureDirectory, pathExists, toTitleCase } from './utils.mjs';
|
|
@@ -11,6 +13,98 @@ export const ARCHITECT_MIN_TOKEN_BUDGET = 200;
|
|
|
11
13
|
export const ARCHITECT_MAX_TOKEN_BUDGET = 4000;
|
|
12
14
|
export const ARCHITECT_MIN_TIMEOUT_MS = 200;
|
|
13
15
|
export const ARCHITECT_MAX_TIMEOUT_MS = 10000;
|
|
16
|
+
export const ARCHITECT_DEFAULT_RESEARCH_MODE = 'snapshot';
|
|
17
|
+
|
|
18
|
+
const ARCHITECT_ALLOWED_RESEARCH_MODES = new Set(['snapshot', 'realtime']);
|
|
19
|
+
const ARCHITECT_DEFAULT_REALTIME_SIGNAL_ENV_KEY = 'AGENTIC_ARCHITECT_REALTIME_SIGNAL_JSON';
|
|
20
|
+
const ARCHITECT_DEFAULT_REALTIME_SIGNAL_PATH_ENV_KEY = 'AGENTIC_ARCHITECT_REALTIME_SIGNAL_PATH';
|
|
21
|
+
const ARCHITECT_ALLOWED_PALETTE_ROLES = ['base', 'surface', 'accent', 'success', 'danger'];
|
|
22
|
+
const ARCHITECT_ALLOWED_TYPOGRAPHY_SCALES = ['compact', 'balanced', 'expressive'];
|
|
23
|
+
const ARCHITECT_ALLOWED_SPACING_PATTERNS = ['compact-grid', 'balanced-grid', 'airy-grid'];
|
|
24
|
+
const ARCHITECT_ALLOWED_MOTION_CHARACTERISTICS = [
|
|
25
|
+
'subtle-enter',
|
|
26
|
+
'staggered-reveal',
|
|
27
|
+
'state-feedback',
|
|
28
|
+
'conversion-focus',
|
|
29
|
+
'calm-transition',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const ARCHITECT_MODULE_FILE_PATH = fileURLToPath(import.meta.url);
|
|
33
|
+
const ARCHITECT_PACKAGE_ROOT = path.resolve(path.dirname(ARCHITECT_MODULE_FILE_PATH), '..', '..');
|
|
34
|
+
const ARCHITECT_RESEARCH_SNAPSHOT_FILE_PATH = path.join(
|
|
35
|
+
ARCHITECT_PACKAGE_ROOT,
|
|
36
|
+
'.agent-context',
|
|
37
|
+
'state',
|
|
38
|
+
'stack-research-snapshot.json'
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const FALLBACK_STACK_RESEARCH_SNAPSHOT = {
|
|
42
|
+
schemaVersion: '1.0.0',
|
|
43
|
+
snapshotId: 'ecosystem-signals-fallback',
|
|
44
|
+
generatedAt: '2026-04-18T00:00:00.000Z',
|
|
45
|
+
deterministic: true,
|
|
46
|
+
sourceName: 'Agentic-Senior-Core internal fallback snapshot',
|
|
47
|
+
sourceUrl: 'state://stack-research-snapshot/fallback',
|
|
48
|
+
trustedRealtimeSources: [
|
|
49
|
+
{
|
|
50
|
+
sourceId: 'awwwards-trend-feed',
|
|
51
|
+
sourceName: 'Awwwards Trend Feed',
|
|
52
|
+
sourceUrl: 'https://www.awwwards.com',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
stackSignals: [
|
|
56
|
+
{
|
|
57
|
+
stackFileName: 'typescript.md',
|
|
58
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
59
|
+
metrics: { ecosystemMaturity: 0.91, talentAvailability: 0.9, deliveryVelocity: 0.89 },
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
stackFileName: 'python.md',
|
|
63
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
64
|
+
metrics: { ecosystemMaturity: 0.92, talentAvailability: 0.88, deliveryVelocity: 0.9 },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
stackFileName: 'java.md',
|
|
68
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
69
|
+
metrics: { ecosystemMaturity: 0.89, talentAvailability: 0.83, deliveryVelocity: 0.8 },
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
stackFileName: 'php.md',
|
|
73
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
74
|
+
metrics: { ecosystemMaturity: 0.79, talentAvailability: 0.75, deliveryVelocity: 0.84 },
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
stackFileName: 'go.md',
|
|
78
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
79
|
+
metrics: { ecosystemMaturity: 0.84, talentAvailability: 0.78, deliveryVelocity: 0.82 },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
stackFileName: 'csharp.md',
|
|
83
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
84
|
+
metrics: { ecosystemMaturity: 0.86, talentAvailability: 0.8, deliveryVelocity: 0.79 },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
stackFileName: 'rust.md',
|
|
88
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
89
|
+
metrics: { ecosystemMaturity: 0.74, talentAvailability: 0.63, deliveryVelocity: 0.67 },
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
stackFileName: 'ruby.md',
|
|
93
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
94
|
+
metrics: { ecosystemMaturity: 0.7, talentAvailability: 0.62, deliveryVelocity: 0.72 },
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
stackFileName: 'react-native.md',
|
|
98
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
99
|
+
metrics: { ecosystemMaturity: 0.72, talentAvailability: 0.67, deliveryVelocity: 0.74 },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
stackFileName: 'flutter.md',
|
|
103
|
+
measuredAt: '2026-04-18T00:00:00.000Z',
|
|
104
|
+
metrics: { ecosystemMaturity: 0.75, talentAvailability: 0.69, deliveryVelocity: 0.76 },
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
14
108
|
|
|
15
109
|
const ARCHITECT_PREFERENCE_FILE_PATH = process.env.AGENTIC_ARCHITECT_PREF_FILE
|
|
16
110
|
? path.resolve(process.env.AGENTIC_ARCHITECT_PREF_FILE)
|
|
@@ -140,6 +234,283 @@ function resolveRecommendedBlueprintFileName(stackFileName, blueprintFileNames)
|
|
|
140
234
|
return blueprintFileNames[0] || null;
|
|
141
235
|
}
|
|
142
236
|
|
|
237
|
+
function normalizeArchitectResearchMode(rawMode) {
|
|
238
|
+
const normalizedMode = String(rawMode || ARCHITECT_DEFAULT_RESEARCH_MODE).trim().toLowerCase();
|
|
239
|
+
if (ARCHITECT_ALLOWED_RESEARCH_MODES.has(normalizedMode)) {
|
|
240
|
+
return normalizedMode;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return ARCHITECT_DEFAULT_RESEARCH_MODE;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function isValidIsoTimestamp(value) {
|
|
247
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return !Number.isNaN(Date.parse(value));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function toIsoTimestamp(value, fallbackValue = new Date().toISOString()) {
|
|
255
|
+
return isValidIsoTimestamp(value) ? new Date(value).toISOString() : fallbackValue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function loadStackResearchSnapshot() {
|
|
259
|
+
try {
|
|
260
|
+
if (!existsSync(ARCHITECT_RESEARCH_SNAPSHOT_FILE_PATH)) {
|
|
261
|
+
return {
|
|
262
|
+
snapshot: FALLBACK_STACK_RESEARCH_SNAPSHOT,
|
|
263
|
+
sourcePath: 'fallback://embedded-snapshot',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const snapshotContent = readFileSync(ARCHITECT_RESEARCH_SNAPSHOT_FILE_PATH, 'utf8');
|
|
268
|
+
const parsedSnapshot = JSON.parse(snapshotContent);
|
|
269
|
+
|
|
270
|
+
if (!Array.isArray(parsedSnapshot?.stackSignals) || parsedSnapshot.stackSignals.length === 0) {
|
|
271
|
+
return {
|
|
272
|
+
snapshot: FALLBACK_STACK_RESEARCH_SNAPSHOT,
|
|
273
|
+
sourcePath: 'fallback://embedded-snapshot',
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
snapshot: parsedSnapshot,
|
|
279
|
+
sourcePath: ARCHITECT_RESEARCH_SNAPSHOT_FILE_PATH,
|
|
280
|
+
};
|
|
281
|
+
} catch {
|
|
282
|
+
return {
|
|
283
|
+
snapshot: FALLBACK_STACK_RESEARCH_SNAPSHOT,
|
|
284
|
+
sourcePath: 'fallback://embedded-snapshot',
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function normalizeMetricValue(value, fallbackValue = 0) {
|
|
290
|
+
const parsedValue = Number(value);
|
|
291
|
+
if (!Number.isFinite(parsedValue)) {
|
|
292
|
+
return fallbackValue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return clampNumericValue(parsedValue, 0, 1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function calculateSnapshotSignalScore(snapshotSignalEntry) {
|
|
299
|
+
const metrics = snapshotSignalEntry?.metrics || {};
|
|
300
|
+
const ecosystemMaturity = normalizeMetricValue(metrics.ecosystemMaturity, 0.65);
|
|
301
|
+
const talentAvailability = normalizeMetricValue(metrics.talentAvailability, 0.65);
|
|
302
|
+
const deliveryVelocity = normalizeMetricValue(metrics.deliveryVelocity, 0.65);
|
|
303
|
+
const aggregateScore = (ecosystemMaturity + talentAvailability + deliveryVelocity) / 3;
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
aggregateScore,
|
|
307
|
+
metrics: {
|
|
308
|
+
ecosystemMaturity,
|
|
309
|
+
talentAvailability,
|
|
310
|
+
deliveryVelocity,
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function parseRealtimeSignalPayloadFromJson(rawJsonContent) {
|
|
316
|
+
try {
|
|
317
|
+
const parsedPayload = JSON.parse(rawJsonContent);
|
|
318
|
+
if (!Array.isArray(parsedPayload?.stackSignals)) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return parsedPayload;
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function loadRealtimeSignalPayload(realtimeSignalFilePath) {
|
|
329
|
+
const normalizedRealtimeSignalFilePath = String(realtimeSignalFilePath || '').trim();
|
|
330
|
+
if (normalizedRealtimeSignalFilePath) {
|
|
331
|
+
try {
|
|
332
|
+
const absoluteRealtimeSignalPath = path.resolve(normalizedRealtimeSignalFilePath);
|
|
333
|
+
if (existsSync(absoluteRealtimeSignalPath)) {
|
|
334
|
+
const filePayload = parseRealtimeSignalPayloadFromJson(readFileSync(absoluteRealtimeSignalPath, 'utf8'));
|
|
335
|
+
if (filePayload) {
|
|
336
|
+
return {
|
|
337
|
+
payload: filePayload,
|
|
338
|
+
sourcePath: absoluteRealtimeSignalPath,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
// Ignore file loading errors and continue to environment fallback.
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const signalPathFromEnvironment = String(process.env[ARCHITECT_DEFAULT_REALTIME_SIGNAL_PATH_ENV_KEY] || '').trim();
|
|
348
|
+
if (signalPathFromEnvironment) {
|
|
349
|
+
try {
|
|
350
|
+
const absoluteRealtimeSignalPath = path.resolve(signalPathFromEnvironment);
|
|
351
|
+
if (existsSync(absoluteRealtimeSignalPath)) {
|
|
352
|
+
const filePayload = parseRealtimeSignalPayloadFromJson(readFileSync(absoluteRealtimeSignalPath, 'utf8'));
|
|
353
|
+
if (filePayload) {
|
|
354
|
+
return {
|
|
355
|
+
payload: filePayload,
|
|
356
|
+
sourcePath: absoluteRealtimeSignalPath,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
// Ignore file loading errors and continue to environment JSON fallback.
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const signalJsonFromEnvironment = String(process.env[ARCHITECT_DEFAULT_REALTIME_SIGNAL_ENV_KEY] || '').trim();
|
|
366
|
+
if (signalJsonFromEnvironment) {
|
|
367
|
+
const payloadFromEnvironment = parseRealtimeSignalPayloadFromJson(signalJsonFromEnvironment);
|
|
368
|
+
if (payloadFromEnvironment) {
|
|
369
|
+
return {
|
|
370
|
+
payload: payloadFromEnvironment,
|
|
371
|
+
sourcePath: `env://${ARCHITECT_DEFAULT_REALTIME_SIGNAL_ENV_KEY}`,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
payload: null,
|
|
378
|
+
sourcePath: null,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function normalizeStackSignalBoost(value) {
|
|
383
|
+
const normalizedSignal = normalizeMetricValue(value, 0);
|
|
384
|
+
return Number((normalizedSignal * 0.35).toFixed(4));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function normalizePaletteRoles(rawPaletteRoles) {
|
|
388
|
+
if (!Array.isArray(rawPaletteRoles) || rawPaletteRoles.length === 0) {
|
|
389
|
+
return ['base', 'surface', 'accent'];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const sanitizedRoles = rawPaletteRoles
|
|
393
|
+
.map((paletteRole) => String(paletteRole || '').trim().toLowerCase())
|
|
394
|
+
.filter((paletteRole) => ARCHITECT_ALLOWED_PALETTE_ROLES.includes(paletteRole));
|
|
395
|
+
|
|
396
|
+
if (sanitizedRoles.length === 0) {
|
|
397
|
+
return ['base', 'surface', 'accent'];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return Array.from(new Set(sanitizedRoles));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function normalizeTypographyScale(rawTypographyScale) {
|
|
404
|
+
const normalizedScale = String(rawTypographyScale || '').trim().toLowerCase();
|
|
405
|
+
if (ARCHITECT_ALLOWED_TYPOGRAPHY_SCALES.includes(normalizedScale)) {
|
|
406
|
+
return normalizedScale;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return 'balanced';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function normalizeSpacingPattern(rawSpacingPattern) {
|
|
413
|
+
const normalizedPattern = String(rawSpacingPattern || '').trim().toLowerCase();
|
|
414
|
+
if (ARCHITECT_ALLOWED_SPACING_PATTERNS.includes(normalizedPattern)) {
|
|
415
|
+
return normalizedPattern;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return 'balanced-grid';
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function normalizeMotionCharacteristics(rawMotionCharacteristics) {
|
|
422
|
+
if (!Array.isArray(rawMotionCharacteristics) || rawMotionCharacteristics.length === 0) {
|
|
423
|
+
return ['subtle-enter', 'state-feedback'];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const sanitizedCharacteristics = rawMotionCharacteristics
|
|
427
|
+
.map((motionCharacteristic) => String(motionCharacteristic || '').trim().toLowerCase())
|
|
428
|
+
.filter((motionCharacteristic) => ARCHITECT_ALLOWED_MOTION_CHARACTERISTICS.includes(motionCharacteristic));
|
|
429
|
+
|
|
430
|
+
if (sanitizedCharacteristics.length === 0) {
|
|
431
|
+
return ['subtle-enter', 'state-feedback'];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return Array.from(new Set(sanitizedCharacteristics));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function synthesizeDesignSignals({
|
|
438
|
+
normalizedProjectDescription,
|
|
439
|
+
recommendedStackFileName,
|
|
440
|
+
realtimePayload,
|
|
441
|
+
snapshotGeneratedAt,
|
|
442
|
+
}) {
|
|
443
|
+
const descriptionText = String(normalizedProjectDescription || '');
|
|
444
|
+
const looksLikeFinancialSurface = /fraud|bank|payment|regulated|security/.test(descriptionText);
|
|
445
|
+
const looksLikeGrowthSurface = /marketing|landing|conversion|sales|campaign/.test(descriptionText);
|
|
446
|
+
const looksLikeDataProduct = /analytics|dashboard|report|insight|data/.test(descriptionText);
|
|
447
|
+
|
|
448
|
+
let paletteRoles = ['base', 'surface', 'accent'];
|
|
449
|
+
let typographyScale = 'balanced';
|
|
450
|
+
let spacingPattern = 'balanced-grid';
|
|
451
|
+
let motionCharacteristics = ['subtle-enter', 'state-feedback'];
|
|
452
|
+
|
|
453
|
+
if (looksLikeFinancialSurface) {
|
|
454
|
+
paletteRoles = ['base', 'surface', 'accent', 'danger', 'success'];
|
|
455
|
+
motionCharacteristics = ['calm-transition', 'state-feedback'];
|
|
456
|
+
} else if (looksLikeGrowthSurface) {
|
|
457
|
+
paletteRoles = ['base', 'surface', 'accent', 'success'];
|
|
458
|
+
typographyScale = 'expressive';
|
|
459
|
+
motionCharacteristics = ['staggered-reveal', 'conversion-focus', 'state-feedback'];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (looksLikeDataProduct || recommendedStackFileName === 'python.md') {
|
|
463
|
+
spacingPattern = 'compact-grid';
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const realtimeDesignSignals = realtimePayload?.designSignals || null;
|
|
467
|
+
if (realtimeDesignSignals) {
|
|
468
|
+
paletteRoles = normalizePaletteRoles(realtimeDesignSignals.paletteRoles || paletteRoles);
|
|
469
|
+
typographyScale = normalizeTypographyScale(realtimeDesignSignals.typographyScale || typographyScale);
|
|
470
|
+
spacingPattern = normalizeSpacingPattern(realtimeDesignSignals.spacingPattern || spacingPattern);
|
|
471
|
+
motionCharacteristics = normalizeMotionCharacteristics(
|
|
472
|
+
realtimeDesignSignals.motionCharacteristics || motionCharacteristics
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
generatedAt: snapshotGeneratedAt,
|
|
478
|
+
normalizedSignals: {
|
|
479
|
+
paletteRoles,
|
|
480
|
+
typographyScale,
|
|
481
|
+
spacingPattern,
|
|
482
|
+
motionCharacteristics,
|
|
483
|
+
},
|
|
484
|
+
sourcePolicy: {
|
|
485
|
+
normalizedSignalsOnly: true,
|
|
486
|
+
copiedExternalProse: false,
|
|
487
|
+
blockedInputPacks: ['DESIGN.md'],
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function buildEvidenceCitation({
|
|
493
|
+
citationId,
|
|
494
|
+
sourceType,
|
|
495
|
+
sourceName,
|
|
496
|
+
sourceUrl,
|
|
497
|
+
measuredAt,
|
|
498
|
+
stackFileName,
|
|
499
|
+
metrics,
|
|
500
|
+
note,
|
|
501
|
+
}) {
|
|
502
|
+
return {
|
|
503
|
+
citationId,
|
|
504
|
+
sourceType,
|
|
505
|
+
sourceName,
|
|
506
|
+
sourceUrl,
|
|
507
|
+
measuredAt: toIsoTimestamp(measuredAt),
|
|
508
|
+
stackFileName,
|
|
509
|
+
metrics,
|
|
510
|
+
note,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
143
514
|
function buildFallbackRecommendation({
|
|
144
515
|
stackFileNames,
|
|
145
516
|
blueprintFileNames,
|
|
@@ -148,14 +519,45 @@ function buildFallbackRecommendation({
|
|
|
148
519
|
usedTokens,
|
|
149
520
|
elapsedMs,
|
|
150
521
|
timeoutTriggered,
|
|
522
|
+
requestedResearchMode,
|
|
523
|
+
effectiveResearchMode,
|
|
524
|
+
snapshot,
|
|
525
|
+
snapshotSourcePath,
|
|
526
|
+
realtimeGateEnabled,
|
|
527
|
+
realtimeSignalsLoaded,
|
|
528
|
+
realtimeSignalSourcePath,
|
|
529
|
+
normalizedProjectDescription,
|
|
151
530
|
}) {
|
|
152
531
|
const fallbackStackFileName = stackFileNames.includes('typescript.md')
|
|
153
532
|
? 'typescript.md'
|
|
154
533
|
: stackFileNames[0] || 'typescript.md';
|
|
155
534
|
const fallbackBlueprintFileName = resolveRecommendedBlueprintFileName(fallbackStackFileName, blueprintFileNames);
|
|
535
|
+
const fallbackSnapshotSignal = Array.isArray(snapshot?.stackSignals)
|
|
536
|
+
? snapshot.stackSignals.find((stackSignal) => stackSignal.stackFileName === fallbackStackFileName) || null
|
|
537
|
+
: null;
|
|
538
|
+
const fallbackSnapshotMetrics = calculateSnapshotSignalScore(fallbackSnapshotSignal);
|
|
539
|
+
const fallbackEvidenceCitations = [
|
|
540
|
+
buildEvidenceCitation({
|
|
541
|
+
citationId: `snapshot:${snapshot?.snapshotId || 'fallback'}:${fallbackStackFileName}`,
|
|
542
|
+
sourceType: 'snapshot',
|
|
543
|
+
sourceName: snapshot?.sourceName || 'Stack research snapshot',
|
|
544
|
+
sourceUrl: snapshot?.sourceUrl || snapshotSourcePath,
|
|
545
|
+
measuredAt: fallbackSnapshotSignal?.measuredAt || snapshot?.generatedAt,
|
|
546
|
+
stackFileName: fallbackStackFileName,
|
|
547
|
+
metrics: fallbackSnapshotMetrics.metrics,
|
|
548
|
+
note: 'Deterministic snapshot baseline used for fallback recommendation.',
|
|
549
|
+
}),
|
|
550
|
+
];
|
|
551
|
+
|
|
552
|
+
const fallbackDesignGuidance = synthesizeDesignSignals({
|
|
553
|
+
normalizedProjectDescription,
|
|
554
|
+
recommendedStackFileName: fallbackStackFileName,
|
|
555
|
+
realtimePayload: null,
|
|
556
|
+
snapshotGeneratedAt: toIsoTimestamp(snapshot?.generatedAt),
|
|
557
|
+
});
|
|
156
558
|
|
|
157
559
|
return {
|
|
158
|
-
projectDescription: '',
|
|
560
|
+
projectDescription: String(normalizedProjectDescription || '').trim(),
|
|
159
561
|
recommendedStackFileName: fallbackStackFileName,
|
|
160
562
|
recommendedBlueprintFileName: fallbackBlueprintFileName,
|
|
161
563
|
confidenceLabel: 'low',
|
|
@@ -176,7 +578,25 @@ function buildFallbackRecommendation({
|
|
|
176
578
|
lowConfidence: true,
|
|
177
579
|
dataConflict: false,
|
|
178
580
|
repeatedOverride: false,
|
|
581
|
+
realtimeGated: requestedResearchMode === 'realtime' && !realtimeGateEnabled,
|
|
582
|
+
realtimeUnavailable: requestedResearchMode === 'realtime' && realtimeGateEnabled && !realtimeSignalsLoaded,
|
|
583
|
+
},
|
|
584
|
+
research: {
|
|
585
|
+
requestedMode: requestedResearchMode,
|
|
586
|
+
effectiveMode: effectiveResearchMode,
|
|
587
|
+
deterministic: effectiveResearchMode === 'snapshot',
|
|
588
|
+
snapshotId: snapshot?.snapshotId || 'fallback',
|
|
589
|
+
snapshotGeneratedAt: toIsoTimestamp(snapshot?.generatedAt),
|
|
590
|
+
snapshotSourcePath,
|
|
591
|
+
realtimeGateEnabled,
|
|
592
|
+
realtimeSignalsLoaded,
|
|
593
|
+
realtimeSignalSourcePath,
|
|
594
|
+
trustedRealtimeSources: Array.isArray(snapshot?.trustedRealtimeSources)
|
|
595
|
+
? snapshot.trustedRealtimeSources
|
|
596
|
+
: [],
|
|
179
597
|
},
|
|
598
|
+
evidenceCitations: fallbackEvidenceCitations,
|
|
599
|
+
designGuidance: fallbackDesignGuidance,
|
|
180
600
|
researchBudget: {
|
|
181
601
|
tokenBudget,
|
|
182
602
|
timeoutMs,
|
|
@@ -195,16 +615,40 @@ export function recommendArchitecture({
|
|
|
195
615
|
blueprintFileNames,
|
|
196
616
|
tokenBudget = ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
197
617
|
timeoutMs = ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
618
|
+
researchMode = ARCHITECT_DEFAULT_RESEARCH_MODE,
|
|
619
|
+
enableRealtimeResearch = false,
|
|
620
|
+
realtimeSignalFilePath = null,
|
|
198
621
|
}) {
|
|
199
622
|
const startedAt = Date.now();
|
|
200
623
|
const boundedTokenBudget = clampNumericValue(tokenBudget, ARCHITECT_MIN_TOKEN_BUDGET, ARCHITECT_MAX_TOKEN_BUDGET);
|
|
201
624
|
const boundedTimeoutMs = clampNumericValue(timeoutMs, ARCHITECT_MIN_TIMEOUT_MS, ARCHITECT_MAX_TIMEOUT_MS);
|
|
625
|
+
const requestedResearchMode = normalizeArchitectResearchMode(researchMode);
|
|
626
|
+
const { snapshot: stackResearchSnapshot, sourcePath: snapshotSourcePath } = loadStackResearchSnapshot();
|
|
627
|
+
const snapshotGeneratedAt = toIsoTimestamp(stackResearchSnapshot.generatedAt);
|
|
628
|
+
const realtimeGateEnabled = requestedResearchMode === 'realtime' && enableRealtimeResearch === true;
|
|
629
|
+
const realtimeSignalPayloadResult = realtimeGateEnabled
|
|
630
|
+
? loadRealtimeSignalPayload(realtimeSignalFilePath)
|
|
631
|
+
: { payload: null, sourcePath: null };
|
|
632
|
+
const realtimeSignalPayload = realtimeSignalPayloadResult.payload;
|
|
633
|
+
const realtimeSignalsLoaded = Array.isArray(realtimeSignalPayload?.stackSignals)
|
|
634
|
+
&& realtimeSignalPayload.stackSignals.length > 0;
|
|
635
|
+
const effectiveResearchMode = realtimeGateEnabled && realtimeSignalsLoaded
|
|
636
|
+
? 'realtime'
|
|
637
|
+
: 'snapshot';
|
|
202
638
|
const normalizedDescription = String(projectDescription || '').trim().toLowerCase();
|
|
203
639
|
const effectiveDescriptionSeed = normalizedDescription || 'general software project';
|
|
204
640
|
let effectiveDescription = effectiveDescriptionSeed;
|
|
205
641
|
let usedTokens = estimateTokenUsage(effectiveDescription) + 120;
|
|
206
642
|
const uncertaintyNotes = [];
|
|
207
643
|
|
|
644
|
+
if (requestedResearchMode === 'realtime' && !realtimeGateEnabled) {
|
|
645
|
+
uncertaintyNotes.push('Realtime research mode requested but gate is off. Using deterministic snapshot baseline.');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (requestedResearchMode === 'realtime' && realtimeGateEnabled && !realtimeSignalsLoaded) {
|
|
649
|
+
uncertaintyNotes.push('Realtime research gate is on, but no trusted realtime payload was available. Using deterministic snapshot baseline.');
|
|
650
|
+
}
|
|
651
|
+
|
|
208
652
|
if (usedTokens > boundedTokenBudget) {
|
|
209
653
|
effectiveDescription = effectiveDescription.slice(0, Math.max(120, boundedTokenBudget * 4));
|
|
210
654
|
usedTokens = boundedTokenBudget;
|
|
@@ -220,9 +664,51 @@ export function recommendArchitecture({
|
|
|
220
664
|
usedTokens,
|
|
221
665
|
elapsedMs: Date.now() - startedAt,
|
|
222
666
|
timeoutTriggered: true,
|
|
667
|
+
requestedResearchMode,
|
|
668
|
+
effectiveResearchMode,
|
|
669
|
+
snapshot: stackResearchSnapshot,
|
|
670
|
+
snapshotSourcePath,
|
|
671
|
+
realtimeGateEnabled,
|
|
672
|
+
realtimeSignalsLoaded,
|
|
673
|
+
realtimeSignalSourcePath: realtimeSignalPayloadResult.sourcePath,
|
|
674
|
+
normalizedProjectDescription: normalizedDescription,
|
|
223
675
|
});
|
|
224
676
|
}
|
|
225
677
|
|
|
678
|
+
const snapshotSignalByStackFileName = new Map();
|
|
679
|
+
for (const snapshotSignalEntry of stackResearchSnapshot?.stackSignals || []) {
|
|
680
|
+
if (!snapshotSignalEntry?.stackFileName) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
snapshotSignalByStackFileName.set(snapshotSignalEntry.stackFileName, snapshotSignalEntry);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const realtimeSignalByStackFileName = new Map();
|
|
688
|
+
if (realtimeSignalsLoaded) {
|
|
689
|
+
for (const realtimeSignalEntry of realtimeSignalPayload.stackSignals) {
|
|
690
|
+
const stackFileName = realtimeSignalEntry?.stackFileName;
|
|
691
|
+
if (!stackFileName || !stackFileNames.includes(stackFileName)) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const rawRealtimeSignalStrength = realtimeSignalEntry.signalStrength
|
|
696
|
+
?? realtimeSignalEntry.metrics?.signalStrength
|
|
697
|
+
?? 0;
|
|
698
|
+
const normalizedRealtimeSignalStrength = normalizeMetricValue(rawRealtimeSignalStrength, 0);
|
|
699
|
+
realtimeSignalByStackFileName.set(stackFileName, {
|
|
700
|
+
signalBoost: normalizeStackSignalBoost(normalizedRealtimeSignalStrength),
|
|
701
|
+
measuredAt: realtimeSignalEntry.measuredAt || realtimeSignalPayload.generatedAt || snapshotGeneratedAt,
|
|
702
|
+
sourceName: realtimeSignalEntry.sourceName || realtimeSignalPayload.sourceName || 'Trusted realtime source',
|
|
703
|
+
sourceUrl: realtimeSignalEntry.sourceUrl || realtimeSignalPayload.sourceUrl || realtimeSignalPayloadResult.sourcePath,
|
|
704
|
+
metrics: {
|
|
705
|
+
signalStrength: normalizedRealtimeSignalStrength,
|
|
706
|
+
freshnessHours: Number(realtimeSignalEntry.metrics?.freshnessHours) || 0,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
226
712
|
const detectionScoreByStackFileName = new Map();
|
|
227
713
|
for (const rankedCandidate of projectDetection?.rankedCandidates || []) {
|
|
228
714
|
const confidenceScore = Number(rankedCandidate.confidenceScore) || 0;
|
|
@@ -251,13 +737,26 @@ export function recommendArchitecture({
|
|
|
251
737
|
}
|
|
252
738
|
|
|
253
739
|
const detectionSignalScore = detectionScoreByStackFileName.get(stackFileName) || 0;
|
|
254
|
-
const
|
|
740
|
+
const snapshotSignalEntry = snapshotSignalByStackFileName.get(stackFileName) || null;
|
|
741
|
+
const snapshotSignalScorePayload = calculateSnapshotSignalScore(snapshotSignalEntry);
|
|
742
|
+
const snapshotSignalScore = normalizeStackSignalBoost(snapshotSignalScorePayload.aggregateScore);
|
|
743
|
+
const realtimeSignalPayloadEntry = realtimeSignalByStackFileName.get(stackFileName) || null;
|
|
744
|
+
const realtimeSignalScore = realtimeSignalPayloadEntry?.signalBoost || 0;
|
|
745
|
+
const totalScore = 0.2 + keywordSignalScore + detectionSignalScore + snapshotSignalScore + realtimeSignalScore;
|
|
255
746
|
|
|
256
747
|
return {
|
|
257
748
|
stackFileName,
|
|
258
749
|
totalScore,
|
|
259
750
|
keywordSignalScore,
|
|
260
751
|
detectionSignalScore,
|
|
752
|
+
snapshotSignalScore,
|
|
753
|
+
snapshotMetrics: snapshotSignalScorePayload.metrics,
|
|
754
|
+
realtimeSignalScore,
|
|
755
|
+
realtimeMetrics: realtimeSignalPayloadEntry?.metrics || null,
|
|
756
|
+
snapshotMeasuredAt: snapshotSignalEntry?.measuredAt || stackResearchSnapshot.generatedAt,
|
|
757
|
+
realtimeMeasuredAt: realtimeSignalPayloadEntry?.measuredAt || null,
|
|
758
|
+
realtimeSourceName: realtimeSignalPayloadEntry?.sourceName || null,
|
|
759
|
+
realtimeSourceUrl: realtimeSignalPayloadEntry?.sourceUrl || null,
|
|
261
760
|
matchedSignals,
|
|
262
761
|
};
|
|
263
762
|
}).sort((leftCandidate, rightCandidate) => rightCandidate.totalScore - leftCandidate.totalScore);
|
|
@@ -271,6 +770,14 @@ export function recommendArchitecture({
|
|
|
271
770
|
usedTokens,
|
|
272
771
|
elapsedMs: Date.now() - startedAt,
|
|
273
772
|
timeoutTriggered: false,
|
|
773
|
+
requestedResearchMode,
|
|
774
|
+
effectiveResearchMode,
|
|
775
|
+
snapshot: stackResearchSnapshot,
|
|
776
|
+
snapshotSourcePath,
|
|
777
|
+
realtimeGateEnabled,
|
|
778
|
+
realtimeSignalsLoaded,
|
|
779
|
+
realtimeSignalSourcePath: realtimeSignalPayloadResult.sourcePath,
|
|
780
|
+
normalizedProjectDescription: normalizedDescription,
|
|
274
781
|
});
|
|
275
782
|
}
|
|
276
783
|
|
|
@@ -308,9 +815,15 @@ export function recommendArchitecture({
|
|
|
308
815
|
|
|
309
816
|
const recommendedStackFileName = strongestCandidate.stackFileName;
|
|
310
817
|
const recommendedBlueprintFileName = resolveRecommendedBlueprintFileName(recommendedStackFileName, blueprintFileNames);
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
818
|
+
const signalSummaryParts = [];
|
|
819
|
+
if (strongestCandidate.matchedSignals.length > 0) {
|
|
820
|
+
signalSummaryParts.push(strongestCandidate.matchedSignals.slice(0, 4).join(', '));
|
|
821
|
+
}
|
|
822
|
+
signalSummaryParts.push(`snapshot ${strongestCandidate.snapshotSignalScore.toFixed(2)}`);
|
|
823
|
+
if (strongestCandidate.realtimeSignalScore > 0) {
|
|
824
|
+
signalSummaryParts.push(`realtime ${strongestCandidate.realtimeSignalScore.toFixed(2)}`);
|
|
825
|
+
}
|
|
826
|
+
const signalSummary = signalSummaryParts.join(', ');
|
|
314
827
|
|
|
315
828
|
const rationaleSentences = [
|
|
316
829
|
`I recommend ${toTitleCase(recommendedStackFileName)} with ${toTitleCase(recommendedBlueprintFileName)} for this project.`,
|
|
@@ -322,6 +835,92 @@ export function recommendArchitecture({
|
|
|
322
835
|
rationaleSentences.push('Confidence is low, so review alternatives before finalizing architecture.');
|
|
323
836
|
}
|
|
324
837
|
|
|
838
|
+
const evidenceCitations = [
|
|
839
|
+
buildEvidenceCitation({
|
|
840
|
+
citationId: `snapshot:${stackResearchSnapshot.snapshotId || 'snapshot'}:${recommendedStackFileName}`,
|
|
841
|
+
sourceType: 'snapshot',
|
|
842
|
+
sourceName: stackResearchSnapshot.sourceName || 'Stack research snapshot',
|
|
843
|
+
sourceUrl: stackResearchSnapshot.sourceUrl || snapshotSourcePath,
|
|
844
|
+
measuredAt: strongestCandidate.snapshotMeasuredAt || snapshotGeneratedAt,
|
|
845
|
+
stackFileName: recommendedStackFileName,
|
|
846
|
+
metrics: strongestCandidate.snapshotMetrics,
|
|
847
|
+
note: 'Deterministic stack research snapshot signal.',
|
|
848
|
+
}),
|
|
849
|
+
];
|
|
850
|
+
|
|
851
|
+
if (projectDetection?.recommendedStackFileName) {
|
|
852
|
+
evidenceCitations.push(
|
|
853
|
+
buildEvidenceCitation({
|
|
854
|
+
citationId: `detector:${projectDetection.recommendedStackFileName}`,
|
|
855
|
+
sourceType: 'detection',
|
|
856
|
+
sourceName: 'Existing project marker detector',
|
|
857
|
+
sourceUrl: 'state://project-detection',
|
|
858
|
+
measuredAt: snapshotGeneratedAt,
|
|
859
|
+
stackFileName: projectDetection.recommendedStackFileName,
|
|
860
|
+
metrics: {
|
|
861
|
+
confidenceScore: Number(projectDetection.confidenceScore || 0),
|
|
862
|
+
confidenceGap: Number(projectDetection.confidenceGap || 0),
|
|
863
|
+
},
|
|
864
|
+
note: 'Repository marker detection signal used for stack recommendation bias.',
|
|
865
|
+
})
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (effectiveResearchMode === 'realtime') {
|
|
870
|
+
const realtimeCitationCandidate = strongestCandidate.realtimeSignalScore > 0
|
|
871
|
+
? strongestCandidate
|
|
872
|
+
: scoredStackCandidates.find((stackCandidate) => stackCandidate.realtimeSignalScore > 0) || null;
|
|
873
|
+
|
|
874
|
+
if (realtimeCitationCandidate) {
|
|
875
|
+
const realtimeCitationOnRecommendedStack = realtimeCitationCandidate.stackFileName === recommendedStackFileName;
|
|
876
|
+
evidenceCitations.push(
|
|
877
|
+
buildEvidenceCitation({
|
|
878
|
+
citationId: `realtime:${realtimeCitationCandidate.stackFileName}`,
|
|
879
|
+
sourceType: 'realtime',
|
|
880
|
+
sourceName: realtimeCitationCandidate.realtimeSourceName || 'Trusted realtime source',
|
|
881
|
+
sourceUrl: realtimeCitationCandidate.realtimeSourceUrl || realtimeSignalPayloadResult.sourcePath,
|
|
882
|
+
measuredAt: realtimeCitationCandidate.realtimeMeasuredAt || snapshotGeneratedAt,
|
|
883
|
+
stackFileName: realtimeCitationCandidate.stackFileName,
|
|
884
|
+
metrics: realtimeCitationCandidate.realtimeMetrics
|
|
885
|
+
|| { signalStrength: realtimeCitationCandidate.realtimeSignalScore },
|
|
886
|
+
note: realtimeCitationOnRecommendedStack
|
|
887
|
+
? 'Optional realtime signal, loaded only when realtime research is explicitly gated on.'
|
|
888
|
+
: 'Optional realtime signal loaded for an alternative stack signal; used as supporting evidence in realtime mode.',
|
|
889
|
+
})
|
|
890
|
+
);
|
|
891
|
+
} else if (realtimeSignalsLoaded && Array.isArray(realtimeSignalPayload?.stackSignals) && realtimeSignalPayload.stackSignals.length > 0) {
|
|
892
|
+
const fallbackRealtimeSignal = realtimeSignalPayload.stackSignals[0];
|
|
893
|
+
const fallbackRealtimeSignalStrength = normalizeMetricValue(
|
|
894
|
+
fallbackRealtimeSignal?.signalStrength
|
|
895
|
+
?? fallbackRealtimeSignal?.metrics?.signalStrength
|
|
896
|
+
?? 0,
|
|
897
|
+
0
|
|
898
|
+
);
|
|
899
|
+
|
|
900
|
+
evidenceCitations.push(
|
|
901
|
+
buildEvidenceCitation({
|
|
902
|
+
citationId: 'realtime:payload-fallback',
|
|
903
|
+
sourceType: 'realtime',
|
|
904
|
+
sourceName: fallbackRealtimeSignal?.sourceName || realtimeSignalPayload.sourceName || 'Trusted realtime source',
|
|
905
|
+
sourceUrl: fallbackRealtimeSignal?.sourceUrl || realtimeSignalPayload.sourceUrl || realtimeSignalPayloadResult.sourcePath,
|
|
906
|
+
measuredAt: fallbackRealtimeSignal?.measuredAt || realtimeSignalPayload.generatedAt || snapshotGeneratedAt,
|
|
907
|
+
stackFileName: recommendedStackFileName,
|
|
908
|
+
metrics: {
|
|
909
|
+
signalStrength: fallbackRealtimeSignalStrength,
|
|
910
|
+
},
|
|
911
|
+
note: 'Optional realtime payload loaded, but stack mapping did not match current candidate set. Stored as trace citation.',
|
|
912
|
+
})
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const designGuidance = synthesizeDesignSignals({
|
|
918
|
+
normalizedProjectDescription: normalizedDescription,
|
|
919
|
+
recommendedStackFileName,
|
|
920
|
+
realtimePayload: effectiveResearchMode === 'realtime' ? realtimeSignalPayload : null,
|
|
921
|
+
snapshotGeneratedAt,
|
|
922
|
+
});
|
|
923
|
+
|
|
325
924
|
const alternatives = scoredStackCandidates
|
|
326
925
|
.slice(1, 3)
|
|
327
926
|
.map((stackCandidate) => {
|
|
@@ -331,6 +930,7 @@ export function recommendArchitecture({
|
|
|
331
930
|
blueprintFileName: alternativeBlueprintFileName,
|
|
332
931
|
oneLineTradeoff: STACK_TRADEOFF_SUMMARIES[stackCandidate.stackFileName]
|
|
333
932
|
|| 'validate fit with your runtime and team constraints.',
|
|
933
|
+
evidenceSummary: `keyword=${stackCandidate.keywordSignalScore.toFixed(2)}, detection=${stackCandidate.detectionSignalScore.toFixed(2)}, snapshot=${stackCandidate.snapshotSignalScore.toFixed(2)}, realtime=${stackCandidate.realtimeSignalScore.toFixed(2)}`,
|
|
334
934
|
};
|
|
335
935
|
});
|
|
336
936
|
|
|
@@ -344,6 +944,14 @@ export function recommendArchitecture({
|
|
|
344
944
|
usedTokens,
|
|
345
945
|
elapsedMs,
|
|
346
946
|
timeoutTriggered: true,
|
|
947
|
+
requestedResearchMode,
|
|
948
|
+
effectiveResearchMode,
|
|
949
|
+
snapshot: stackResearchSnapshot,
|
|
950
|
+
snapshotSourcePath,
|
|
951
|
+
realtimeGateEnabled,
|
|
952
|
+
realtimeSignalsLoaded,
|
|
953
|
+
realtimeSignalSourcePath: realtimeSignalPayloadResult.sourcePath,
|
|
954
|
+
normalizedProjectDescription: normalizedDescription,
|
|
347
955
|
});
|
|
348
956
|
}
|
|
349
957
|
|
|
@@ -361,7 +969,25 @@ export function recommendArchitecture({
|
|
|
361
969
|
lowConfidence,
|
|
362
970
|
dataConflict,
|
|
363
971
|
repeatedOverride: false,
|
|
972
|
+
realtimeGated: requestedResearchMode === 'realtime' && !realtimeGateEnabled,
|
|
973
|
+
realtimeUnavailable: requestedResearchMode === 'realtime' && realtimeGateEnabled && !realtimeSignalsLoaded,
|
|
364
974
|
},
|
|
975
|
+
research: {
|
|
976
|
+
requestedMode: requestedResearchMode,
|
|
977
|
+
effectiveMode: effectiveResearchMode,
|
|
978
|
+
deterministic: effectiveResearchMode === 'snapshot',
|
|
979
|
+
snapshotId: stackResearchSnapshot.snapshotId || 'snapshot',
|
|
980
|
+
snapshotGeneratedAt,
|
|
981
|
+
snapshotSourcePath,
|
|
982
|
+
realtimeGateEnabled,
|
|
983
|
+
realtimeSignalsLoaded,
|
|
984
|
+
realtimeSignalSourcePath: realtimeSignalPayloadResult.sourcePath,
|
|
985
|
+
trustedRealtimeSources: Array.isArray(stackResearchSnapshot.trustedRealtimeSources)
|
|
986
|
+
? stackResearchSnapshot.trustedRealtimeSources
|
|
987
|
+
: [],
|
|
988
|
+
},
|
|
989
|
+
evidenceCitations,
|
|
990
|
+
designGuidance,
|
|
365
991
|
researchBudget: {
|
|
366
992
|
tokenBudget: boundedTokenBudget,
|
|
367
993
|
timeoutMs: boundedTimeoutMs,
|
|
@@ -374,11 +1000,15 @@ export function recommendArchitecture({
|
|
|
374
1000
|
}
|
|
375
1001
|
|
|
376
1002
|
export function formatArchitectureRecommendation(architectureRecommendation) {
|
|
1003
|
+
const researchModeSummary = architectureRecommendation.research
|
|
1004
|
+
? `${architectureRecommendation.research.requestedMode} -> ${architectureRecommendation.research.effectiveMode}`
|
|
1005
|
+
: 'snapshot';
|
|
377
1006
|
const outputLines = [
|
|
378
1007
|
'\nArchitecture recommendation (project-description-first):',
|
|
379
1008
|
`- Stack: ${toTitleCase(architectureRecommendation.recommendedStackFileName)}`,
|
|
380
1009
|
`- Blueprint: ${toTitleCase(architectureRecommendation.recommendedBlueprintFileName)}`,
|
|
381
1010
|
`- Confidence: ${architectureRecommendation.confidenceLabel} (${architectureRecommendation.confidenceScore})`,
|
|
1011
|
+
`- Research mode: ${researchModeSummary}`,
|
|
382
1012
|
'- Rationale:',
|
|
383
1013
|
...architectureRecommendation.rationaleSentences.map((sentence, sentenceIndex) => ` ${sentenceIndex + 1}. ${sentence}`),
|
|
384
1014
|
'- Alternatives:',
|
|
@@ -418,6 +1048,27 @@ export function formatArchitectureRecommendation(architectureRecommendation) {
|
|
|
418
1048
|
outputLines.push(`- Caution labels: ${cautionLabels.join(', ')}`);
|
|
419
1049
|
}
|
|
420
1050
|
|
|
1051
|
+
if (Array.isArray(architectureRecommendation.evidenceCitations) && architectureRecommendation.evidenceCitations.length > 0) {
|
|
1052
|
+
outputLines.push('- Evidence citations (measurable source + timestamp):');
|
|
1053
|
+
outputLines.push(
|
|
1054
|
+
...architectureRecommendation.evidenceCitations.map((citationEntry, citationIndex) => {
|
|
1055
|
+
const metricsSummary = Object.entries(citationEntry.metrics || {})
|
|
1056
|
+
.map(([metricKey, metricValue]) => `${metricKey}=${metricValue}`)
|
|
1057
|
+
.join(', ');
|
|
1058
|
+
return ` ${citationIndex + 1}. [${citationEntry.sourceType}] ${citationEntry.sourceName} @ ${citationEntry.measuredAt} (${metricsSummary || 'no metrics'})`;
|
|
1059
|
+
})
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (architectureRecommendation.designGuidance?.normalizedSignals) {
|
|
1064
|
+
const normalizedSignals = architectureRecommendation.designGuidance.normalizedSignals;
|
|
1065
|
+
outputLines.push('- Design signal synthesis (normalized, no copied external prose):');
|
|
1066
|
+
outputLines.push(` 1. Palette roles: ${normalizedSignals.paletteRoles.join(', ')}`);
|
|
1067
|
+
outputLines.push(` 2. Typography scale: ${normalizedSignals.typographyScale}`);
|
|
1068
|
+
outputLines.push(` 3. Spacing pattern: ${normalizedSignals.spacingPattern}`);
|
|
1069
|
+
outputLines.push(` 4. Motion characteristics: ${normalizedSignals.motionCharacteristics.join(', ')}`);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
421
1072
|
outputLines.push(
|
|
422
1073
|
`- Research guardrails: ${architectureRecommendation.researchBudget.usedTokens}/${architectureRecommendation.researchBudget.tokenBudget} tokens, ${architectureRecommendation.researchBudget.elapsedMs}ms/${architectureRecommendation.researchBudget.timeoutMs}ms`
|
|
423
1074
|
);
|
|
@@ -65,6 +65,7 @@ import { evaluateSkillDomainCompatibility } from '../compatibility.mjs';
|
|
|
65
65
|
import {
|
|
66
66
|
ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
67
67
|
ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
68
|
+
ARCHITECT_DEFAULT_RESEARCH_MODE,
|
|
68
69
|
ARCHITECT_MIN_TOKEN_BUDGET,
|
|
69
70
|
ARCHITECT_MAX_TOKEN_BUDGET,
|
|
70
71
|
ARCHITECT_MIN_TIMEOUT_MS,
|
|
@@ -100,6 +101,9 @@ export function parseInitArguments(commandArguments) {
|
|
|
100
101
|
projectDescription: '',
|
|
101
102
|
architectTokenBudget: ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
102
103
|
architectTimeoutMs: ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
104
|
+
architectResearchMode: ARCHITECT_DEFAULT_RESEARCH_MODE,
|
|
105
|
+
enableRealtimeResearch: false,
|
|
106
|
+
architectRealtimeSignalFile: null,
|
|
103
107
|
runtimeEnv: 'auto',
|
|
104
108
|
runtimeEnvProvided: false,
|
|
105
109
|
};
|
|
@@ -308,6 +312,38 @@ export function parseInitArguments(commandArguments) {
|
|
|
308
312
|
continue;
|
|
309
313
|
}
|
|
310
314
|
|
|
315
|
+
if (currentArgument === '--architect-research-mode') {
|
|
316
|
+
parsedInitOptions.architectResearchMode = commandArguments[argumentIndex + 1] || ARCHITECT_DEFAULT_RESEARCH_MODE;
|
|
317
|
+
argumentIndex += 1;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (currentArgument.startsWith('--architect-research-mode=')) {
|
|
322
|
+
parsedInitOptions.architectResearchMode = currentArgument.split('=')[1] || ARCHITECT_DEFAULT_RESEARCH_MODE;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (currentArgument === '--enable-realtime-research') {
|
|
327
|
+
parsedInitOptions.enableRealtimeResearch = true;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (currentArgument === '--disable-realtime-research') {
|
|
332
|
+
parsedInitOptions.enableRealtimeResearch = false;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (currentArgument === '--architect-realtime-signal-file') {
|
|
337
|
+
parsedInitOptions.architectRealtimeSignalFile = commandArguments[argumentIndex + 1] || null;
|
|
338
|
+
argumentIndex += 1;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (currentArgument.startsWith('--architect-realtime-signal-file=')) {
|
|
343
|
+
parsedInitOptions.architectRealtimeSignalFile = currentArgument.split('=')[1] || null;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
311
347
|
if (currentArgument === '--runtime-env') {
|
|
312
348
|
parsedInitOptions.runtimeEnv = commandArguments[argumentIndex + 1] || 'auto';
|
|
313
349
|
parsedInitOptions.runtimeEnvProvided = true;
|
|
@@ -350,9 +386,18 @@ export function parseInitArguments(commandArguments) {
|
|
|
350
386
|
throw new Error(`--architect-timeout-ms must be an integer between ${ARCHITECT_MIN_TIMEOUT_MS} and ${ARCHITECT_MAX_TIMEOUT_MS}`);
|
|
351
387
|
}
|
|
352
388
|
|
|
389
|
+
const normalizedArchitectResearchMode = normalizeChoiceInput(
|
|
390
|
+
parsedInitOptions.architectResearchMode || ARCHITECT_DEFAULT_RESEARCH_MODE
|
|
391
|
+
);
|
|
392
|
+
const supportedArchitectResearchModes = new Set(['snapshot', 'realtime']);
|
|
393
|
+
if (!supportedArchitectResearchModes.has(normalizedArchitectResearchMode)) {
|
|
394
|
+
throw new Error('--architect-research-mode must be one of: snapshot, realtime');
|
|
395
|
+
}
|
|
396
|
+
|
|
353
397
|
parsedInitOptions.docsLang = normalizedDocsLanguage;
|
|
354
398
|
parsedInitOptions.runtimeEnv = normalizedRuntimeEnvironment;
|
|
355
399
|
parsedInitOptions.tokenAgent = normalizeAgentName(parsedInitOptions.tokenAgent);
|
|
400
|
+
parsedInitOptions.architectResearchMode = normalizedArchitectResearchMode;
|
|
356
401
|
|
|
357
402
|
return parsedInitOptions;
|
|
358
403
|
}
|
|
@@ -843,6 +888,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
843
888
|
blueprintFileNames,
|
|
844
889
|
tokenBudget: initOptions.architectTokenBudget,
|
|
845
890
|
timeoutMs: initOptions.architectTimeoutMs,
|
|
891
|
+
researchMode: initOptions.architectResearchMode,
|
|
892
|
+
enableRealtimeResearch: initOptions.enableRealtimeResearch,
|
|
893
|
+
realtimeSignalFilePath: initOptions.architectRealtimeSignalFile,
|
|
846
894
|
});
|
|
847
895
|
|
|
848
896
|
architectureRecommendation.userVeto = {
|
package/lib/cli/utils.mjs
CHANGED
|
@@ -29,7 +29,7 @@ export function printUsage() {
|
|
|
29
29
|
console.log('');
|
|
30
30
|
console.log('Usage:');
|
|
31
31
|
console.log(' agentic-senior-core launch');
|
|
32
|
-
console.log(' agentic-senior-core init [target-directory] [--preset <name>] [--profile <beginner|balanced|strict>] [--profile-pack <name>] [--stack <name>] [--blueprint <name>] [--project-description <text>] [--architect-token-budget <number>] [--architect-timeout-ms <number>] [--ci <true|false>] [--newbie] [--token-optimize] [--no-token-optimize] [--token-agent <name>] [--memory-continuity] [--no-memory-continuity] [--scaffold-docs] [--no-scaffold-docs] [--docs-lang <en|id>] [--project-config <path>] [--runtime-env <auto|linux-wsl|linux|windows|macos>]');
|
|
32
|
+
console.log(' agentic-senior-core init [target-directory] [--preset <name>] [--profile <beginner|balanced|strict>] [--profile-pack <name>] [--stack <name>] [--blueprint <name>] [--project-description <text>] [--architect-token-budget <number>] [--architect-timeout-ms <number>] [--architect-research-mode <snapshot|realtime>] [--enable-realtime-research] [--architect-realtime-signal-file <path>] [--ci <true|false>] [--newbie] [--token-optimize] [--no-token-optimize] [--token-agent <name>] [--memory-continuity] [--no-memory-continuity] [--scaffold-docs] [--no-scaffold-docs] [--docs-lang <en|id>] [--project-config <path>] [--runtime-env <auto|linux-wsl|linux|windows|macos>]');
|
|
33
33
|
console.log(' agentic-senior-core upgrade [target-directory] [--dry-run] [--yes] [--mcp-template]');
|
|
34
34
|
console.log(' agentic-senior-core optimize [target-directory] [--agent <copilot|claude|cursor|windsurf|gemini|codex|cline>] [--enable|--disable] [--show]');
|
|
35
35
|
console.log(' agentic-senior-core mcp');
|
|
@@ -49,6 +49,9 @@ export function printUsage() {
|
|
|
49
49
|
console.log(' --project-description Architecture intent text used for stack/blueprint recommendation');
|
|
50
50
|
console.log(' --architect-token-budget Max token estimate used by recommendation research (default: 900)');
|
|
51
51
|
console.log(' --architect-timeout-ms Max recommendation research time in milliseconds (default: 1500)');
|
|
52
|
+
console.log(' --architect-research-mode Recommendation evidence mode (snapshot or realtime; default: snapshot)');
|
|
53
|
+
console.log(' --enable-realtime-research Explicit gate to allow trusted realtime evidence ingestion');
|
|
54
|
+
console.log(' --architect-realtime-signal-file Optional JSON payload path for trusted realtime stack/design signals');
|
|
52
55
|
console.log(' --ci Override CI/CD quality checks (guardrails) (true|false)');
|
|
53
56
|
console.log(' --token-optimize Explicitly enable token optimization policy during init (default behavior)');
|
|
54
57
|
console.log(' --token-agent Set token optimization agent target (copilot, claude, cursor, windsurf, gemini, codex, cline)');
|
package/package.json
CHANGED
package/scripts/validate.mjs
CHANGED
|
@@ -180,6 +180,29 @@ const REQUIRED_DETECTION_TRANSPARENCY_SNIPPETS = [
|
|
|
180
180
|
],
|
|
181
181
|
},
|
|
182
182
|
];
|
|
183
|
+
const REQUIRED_STACK_RESEARCH_ENGINE_SNIPPETS = [
|
|
184
|
+
{
|
|
185
|
+
path: 'lib/cli/architect.mjs',
|
|
186
|
+
snippets: [
|
|
187
|
+
'ARCHITECT_RESEARCH_SNAPSHOT_FILE_PATH',
|
|
188
|
+
'evidenceCitations',
|
|
189
|
+
'designGuidance',
|
|
190
|
+
'copiedExternalProse: false',
|
|
191
|
+
'realtimeGateEnabled',
|
|
192
|
+
'requestedMode: requestedResearchMode',
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
path: 'lib/cli/commands/init.mjs',
|
|
197
|
+
snippets: [
|
|
198
|
+
'--architect-research-mode',
|
|
199
|
+
'--enable-realtime-research',
|
|
200
|
+
'--architect-realtime-signal-file',
|
|
201
|
+
'researchMode: initOptions.architectResearchMode',
|
|
202
|
+
'enableRealtimeResearch: initOptions.enableRealtimeResearch',
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
];
|
|
183
206
|
|
|
184
207
|
const validationResult = {
|
|
185
208
|
passed: 0,
|
|
@@ -300,6 +323,7 @@ async function validateRequiredFiles() {
|
|
|
300
323
|
'.agent-context/state/benchmark-reproducibility.json',
|
|
301
324
|
'.agent-context/state/benchmark-writer-judge-config.json',
|
|
302
325
|
'.agent-context/state/benchmark-watchlist.json',
|
|
326
|
+
'.agent-context/state/stack-research-snapshot.json',
|
|
303
327
|
'.agent-context/state/memory-schema-v1.json',
|
|
304
328
|
'.agent-context/state/memory-adapter-contract.json',
|
|
305
329
|
'.agent-context/state/skill-platform.json',
|
|
@@ -975,6 +999,93 @@ async function validateDetectionTransparencyCoverage() {
|
|
|
975
999
|
}
|
|
976
1000
|
}
|
|
977
1001
|
|
|
1002
|
+
async function validateStackResearchEngineCoverage() {
|
|
1003
|
+
console.log('\nChecking stack research engine coverage...');
|
|
1004
|
+
|
|
1005
|
+
for (const coverageRule of REQUIRED_STACK_RESEARCH_ENGINE_SNIPPETS) {
|
|
1006
|
+
const absoluteCoveragePath = join(ROOT_DIR, coverageRule.path);
|
|
1007
|
+
|
|
1008
|
+
if (!(await fileExists(absoluteCoveragePath))) {
|
|
1009
|
+
fail(`Missing stack research source: ${coverageRule.path}`);
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const coverageContent = await readTextFile(absoluteCoveragePath);
|
|
1014
|
+
for (const requiredSnippet of coverageRule.snippets) {
|
|
1015
|
+
if (coverageContent.includes(requiredSnippet)) {
|
|
1016
|
+
pass(`${coverageRule.path} includes stack research snippet: ${requiredSnippet}`);
|
|
1017
|
+
} else {
|
|
1018
|
+
fail(`${coverageRule.path} is missing stack research snippet: ${requiredSnippet}`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function isNormalizedMetricValue(value) {
|
|
1025
|
+
return Number.isFinite(Number(value)) && Number(value) >= 0 && Number(value) <= 1;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
async function validateStackResearchSnapshotState() {
|
|
1029
|
+
console.log('\nChecking deterministic stack research snapshot state...');
|
|
1030
|
+
|
|
1031
|
+
const snapshotPath = join(ROOT_DIR, '.agent-context', 'state', 'stack-research-snapshot.json');
|
|
1032
|
+
if (!(await fileExists(snapshotPath))) {
|
|
1033
|
+
fail('Missing deterministic stack research snapshot: .agent-context/state/stack-research-snapshot.json');
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
let snapshotPayload;
|
|
1038
|
+
try {
|
|
1039
|
+
snapshotPayload = JSON.parse(await readTextFile(snapshotPath));
|
|
1040
|
+
} catch {
|
|
1041
|
+
fail('Invalid JSON in .agent-context/state/stack-research-snapshot.json');
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (snapshotPayload?.deterministic === true) {
|
|
1046
|
+
pass('stack-research-snapshot.json declares deterministic: true');
|
|
1047
|
+
} else {
|
|
1048
|
+
fail('stack-research-snapshot.json must declare deterministic: true');
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const generatedAtValue = String(snapshotPayload?.generatedAt || '');
|
|
1052
|
+
if (!Number.isNaN(Date.parse(generatedAtValue))) {
|
|
1053
|
+
pass('stack-research-snapshot.json includes valid generatedAt timestamp');
|
|
1054
|
+
} else {
|
|
1055
|
+
fail('stack-research-snapshot.json must include a valid generatedAt timestamp');
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (Array.isArray(snapshotPayload?.trustedRealtimeSources) && snapshotPayload.trustedRealtimeSources.length > 0) {
|
|
1059
|
+
pass('stack-research-snapshot.json includes trustedRealtimeSources');
|
|
1060
|
+
} else {
|
|
1061
|
+
fail('stack-research-snapshot.json must include at least one trustedRealtimeSources entry');
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (!Array.isArray(snapshotPayload?.stackSignals) || snapshotPayload.stackSignals.length === 0) {
|
|
1065
|
+
fail('stack-research-snapshot.json must include non-empty stackSignals array');
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
pass(`stack-research-snapshot.json includes ${snapshotPayload.stackSignals.length} stack signal entries`);
|
|
1070
|
+
|
|
1071
|
+
const invalidSignalEntries = snapshotPayload.stackSignals.filter((signalEntry) => {
|
|
1072
|
+
const hasStackName = typeof signalEntry?.stackFileName === 'string' && signalEntry.stackFileName.trim().length > 0;
|
|
1073
|
+
const hasMeasuredAt = !Number.isNaN(Date.parse(String(signalEntry?.measuredAt || '')));
|
|
1074
|
+
const metrics = signalEntry?.metrics || {};
|
|
1075
|
+
const hasValidMetrics = isNormalizedMetricValue(metrics.ecosystemMaturity)
|
|
1076
|
+
&& isNormalizedMetricValue(metrics.talentAvailability)
|
|
1077
|
+
&& isNormalizedMetricValue(metrics.deliveryVelocity);
|
|
1078
|
+
|
|
1079
|
+
return !(hasStackName && hasMeasuredAt && hasValidMetrics);
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
if (invalidSignalEntries.length === 0) {
|
|
1083
|
+
pass('stack-research-snapshot.json stackSignals keep measurable metrics and timestamps');
|
|
1084
|
+
} else {
|
|
1085
|
+
fail(`stack-research-snapshot.json has invalid stackSignals entries: ${invalidSignalEntries.length}`);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
978
1089
|
async function validateMcpConfiguration() {
|
|
979
1090
|
console.log('\nChecking MCP configuration...');
|
|
980
1091
|
|
|
@@ -1225,6 +1336,8 @@ async function main() {
|
|
|
1225
1336
|
await validateDocumentationFlow();
|
|
1226
1337
|
await validateTerminologyMapping();
|
|
1227
1338
|
await validateDetectionTransparencyCoverage();
|
|
1339
|
+
await validateStackResearchEngineCoverage();
|
|
1340
|
+
await validateStackResearchSnapshotState();
|
|
1228
1341
|
await validateMcpConfiguration();
|
|
1229
1342
|
await validateHumanWritingGovernance();
|
|
1230
1343
|
await validateInstructionAdapters();
|