scene-capability-engine 3.6.45 → 3.6.47
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 +22 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/docs/agent-runtime/symbol-evidence.schema.json +1 -1
- package/docs/command-reference.md +8 -0
- package/docs/interactive-customization/dialogue-governance-policy-baseline.json +4 -1
- package/docs/interactive-customization/embedded-assistant-authorization-dialogue-rules.md +5 -0
- package/docs/releases/README.md +2 -0
- package/docs/releases/v3.6.46.md +23 -0
- package/docs/releases/v3.6.47.md +23 -0
- package/docs/sce-business-mode-map.md +2 -1
- package/docs/sce-capability-matrix-e2e-example.md +2 -1
- package/docs/security-governance-default-baseline.md +2 -0
- package/docs/starter-kit/README.md +3 -0
- package/docs/zh/releases/README.md +2 -0
- package/docs/zh/releases/v3.6.46.md +23 -0
- package/docs/zh/releases/v3.6.47.md +23 -0
- package/lib/workspace/takeover-baseline.js +293 -1
- package/package.json +6 -2
- package/scripts/auto-strategy-router.js +231 -0
- package/scripts/capability-mapping-report.js +339 -0
- package/scripts/check-branding-consistency.js +140 -0
- package/scripts/check-sce-tracking.js +54 -0
- package/scripts/check-skip-allowlist.js +94 -0
- package/scripts/clarification-first-audit.js +322 -0
- package/scripts/errorbook-registry-health-gate.js +172 -0
- package/scripts/errorbook-release-gate.js +132 -0
- package/scripts/failure-attribution-repair.js +317 -0
- package/scripts/git-managed-gate.js +464 -0
- package/scripts/interactive-approval-event-projection.js +400 -0
- package/scripts/interactive-approval-workflow.js +829 -0
- package/scripts/interactive-authorization-tier-evaluate.js +413 -0
- package/scripts/interactive-change-plan-gate.js +225 -0
- package/scripts/interactive-context-bridge.js +617 -0
- package/scripts/interactive-customization-loop.js +1690 -0
- package/scripts/interactive-dialogue-governance.js +873 -0
- package/scripts/interactive-feedback-log.js +253 -0
- package/scripts/interactive-flow-smoke.js +238 -0
- package/scripts/interactive-flow.js +1059 -0
- package/scripts/interactive-governance-report.js +1112 -0
- package/scripts/interactive-intent-build.js +707 -0
- package/scripts/interactive-loop-smoke.js +215 -0
- package/scripts/interactive-moqui-adapter.js +304 -0
- package/scripts/interactive-plan-build.js +426 -0
- package/scripts/interactive-runtime-policy-evaluate.js +495 -0
- package/scripts/interactive-work-order-build.js +552 -0
- package/scripts/matrix-regression-gate.js +167 -0
- package/scripts/moqui-core-regression-suite.js +397 -0
- package/scripts/moqui-lexicon-audit.js +651 -0
- package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
- package/scripts/moqui-matrix-remediation-queue.js +852 -0
- package/scripts/moqui-metadata-extract.js +1340 -0
- package/scripts/moqui-rebuild-gate.js +167 -0
- package/scripts/moqui-release-summary.js +729 -0
- package/scripts/moqui-standard-rebuild.js +1370 -0
- package/scripts/moqui-template-baseline-report.js +682 -0
- package/scripts/npm-package-runtime-asset-check.js +221 -0
- package/scripts/problem-closure-gate.js +441 -0
- package/scripts/release-asset-integrity-check.js +216 -0
- package/scripts/release-asset-nonempty-normalize.js +166 -0
- package/scripts/release-drift-evaluate.js +223 -0
- package/scripts/release-drift-signals.js +255 -0
- package/scripts/release-governance-snapshot-export.js +132 -0
- package/scripts/release-ops-weekly-summary.js +934 -0
- package/scripts/release-risk-remediation-bundle.js +315 -0
- package/scripts/release-weekly-ops-gate.js +423 -0
- package/scripts/state-migration-reconciliation-gate.js +110 -0
- package/scripts/state-storage-tiering-audit.js +337 -0
- package/scripts/steering-content-audit.js +393 -0
- package/scripts/symbol-evidence-locate.js +370 -0
- package/template/.sce/README.md +1 -0
- package/template/.sce/steering/CORE_PRINCIPLES.md +25 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MAX_HITS = 10;
|
|
8
|
+
const DEFAULT_MIN_SCORE = 0.35;
|
|
9
|
+
const DEFAULT_MIN_RELIABLE_SCORE = 0.6;
|
|
10
|
+
const FALLBACK_ACTION_ALLOW_WRITE = 'allow_write';
|
|
11
|
+
const FALLBACK_ACTION_CLARIFY_BUSINESS_SCOPE = 'clarify_business_scope';
|
|
12
|
+
const DEFAULT_EXTENSIONS = [
|
|
13
|
+
'.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx',
|
|
14
|
+
'.py', '.java', '.go', '.rb', '.php', '.cs',
|
|
15
|
+
'.json', '.yaml', '.yml', '.md'
|
|
16
|
+
];
|
|
17
|
+
const DEFAULT_IGNORED_DIRS = new Set([
|
|
18
|
+
'.git',
|
|
19
|
+
'.svn',
|
|
20
|
+
'.hg',
|
|
21
|
+
'node_modules',
|
|
22
|
+
'dist',
|
|
23
|
+
'build',
|
|
24
|
+
'.next',
|
|
25
|
+
'.cache'
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
function parseArgs(argv = []) {
|
|
29
|
+
const options = {
|
|
30
|
+
workspace: process.cwd(),
|
|
31
|
+
query: '',
|
|
32
|
+
queryFile: '',
|
|
33
|
+
maxHits: DEFAULT_MAX_HITS,
|
|
34
|
+
minScore: DEFAULT_MIN_SCORE,
|
|
35
|
+
minReliableScore: DEFAULT_MIN_RELIABLE_SCORE,
|
|
36
|
+
extensions: [...DEFAULT_EXTENSIONS],
|
|
37
|
+
strict: false,
|
|
38
|
+
json: false
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
42
|
+
const token = argv[index];
|
|
43
|
+
const next = argv[index + 1];
|
|
44
|
+
|
|
45
|
+
if (token === '--workspace' && next) {
|
|
46
|
+
options.workspace = next;
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (token === '--query' && next) {
|
|
51
|
+
options.query = next;
|
|
52
|
+
index += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (token === '--query-file' && next) {
|
|
56
|
+
options.queryFile = next;
|
|
57
|
+
index += 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (token === '--max-hits' && next) {
|
|
61
|
+
options.maxHits = Number(next);
|
|
62
|
+
index += 1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (token === '--min-score' && next) {
|
|
66
|
+
options.minScore = Number(next);
|
|
67
|
+
index += 1;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (token === '--min-reliable-score' && next) {
|
|
71
|
+
options.minReliableScore = Number(next);
|
|
72
|
+
index += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (token === '--extensions' && next) {
|
|
76
|
+
options.extensions = next
|
|
77
|
+
.split(',')
|
|
78
|
+
.map((item) => item.trim().toLowerCase())
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.map((item) => (item.startsWith('.') ? item : `.${item}`));
|
|
81
|
+
index += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (token === '--strict') {
|
|
85
|
+
options.strict = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (token === '--json') {
|
|
89
|
+
options.json = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (token === '--help' || token === '-h') {
|
|
93
|
+
printHelpAndExit(0);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!Number.isFinite(options.maxHits) || options.maxHits <= 0) {
|
|
98
|
+
throw new Error('--max-hits must be a positive number.');
|
|
99
|
+
}
|
|
100
|
+
if (!Number.isFinite(options.minScore) || options.minScore < 0 || options.minScore > 1) {
|
|
101
|
+
throw new Error('--min-score must be between 0 and 1.');
|
|
102
|
+
}
|
|
103
|
+
if (
|
|
104
|
+
!Number.isFinite(options.minReliableScore)
|
|
105
|
+
|| options.minReliableScore < 0
|
|
106
|
+
|| options.minReliableScore > 1
|
|
107
|
+
) {
|
|
108
|
+
throw new Error('--min-reliable-score must be between 0 and 1.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return options;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function printHelpAndExit(code) {
|
|
115
|
+
const lines = [
|
|
116
|
+
'Usage: node scripts/symbol-evidence-locate.js --query "<text>" [options]',
|
|
117
|
+
'',
|
|
118
|
+
'Options:',
|
|
119
|
+
' --workspace <path> Workspace root to search (default: current directory)',
|
|
120
|
+
' --query <text> Query text for symbol localization',
|
|
121
|
+
' --query-file <path> Query text file (fallback when --query absent)',
|
|
122
|
+
` --max-hits <n> Maximum hits to return (default: ${DEFAULT_MAX_HITS})`,
|
|
123
|
+
` --min-score <0-1> Minimum hit score (default: ${DEFAULT_MIN_SCORE})`,
|
|
124
|
+
` --min-reliable-score <0-1> Reliability threshold (default: ${DEFAULT_MIN_RELIABLE_SCORE})`,
|
|
125
|
+
` --extensions <csv> File extensions (default: ${DEFAULT_EXTENSIONS.join(',')})`,
|
|
126
|
+
' --strict Exit code 2 when no reliable evidence is found and scope clarification is required',
|
|
127
|
+
' --json Print JSON payload',
|
|
128
|
+
' -h, --help Show this help'
|
|
129
|
+
];
|
|
130
|
+
console.log(lines.join('\n'));
|
|
131
|
+
process.exit(code);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function resolveQuery(options) {
|
|
135
|
+
if (typeof options.query === 'string' && options.query.trim()) {
|
|
136
|
+
return options.query.trim();
|
|
137
|
+
}
|
|
138
|
+
if (typeof options.queryFile === 'string' && options.queryFile.trim()) {
|
|
139
|
+
const resolvedFile = path.resolve(options.workspace, options.queryFile);
|
|
140
|
+
if (!fs.existsSync(resolvedFile)) {
|
|
141
|
+
throw new Error(`query file not found: ${options.queryFile}`);
|
|
142
|
+
}
|
|
143
|
+
const content = fs.readFileSync(resolvedFile, 'utf8').trim();
|
|
144
|
+
if (!content) {
|
|
145
|
+
throw new Error(`query file is empty: ${options.queryFile}`);
|
|
146
|
+
}
|
|
147
|
+
return content;
|
|
148
|
+
}
|
|
149
|
+
throw new Error('query is required. Use --query or --query-file.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeQueryTokens(query) {
|
|
153
|
+
return `${query || ''}`
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/[^a-z0-9_:\-\s]/g, ' ')
|
|
156
|
+
.split(/\s+/)
|
|
157
|
+
.map((token) => token.trim())
|
|
158
|
+
.filter((token) => token.length >= 2);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function listCandidateFiles(workspaceRoot, extensions) {
|
|
162
|
+
const resolvedRoot = path.resolve(workspaceRoot);
|
|
163
|
+
const extSet = new Set((extensions || []).map((item) => `${item}`.toLowerCase()));
|
|
164
|
+
const files = [];
|
|
165
|
+
const stack = [resolvedRoot];
|
|
166
|
+
|
|
167
|
+
while (stack.length > 0) {
|
|
168
|
+
const current = stack.pop();
|
|
169
|
+
let entries = [];
|
|
170
|
+
try {
|
|
171
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
172
|
+
} catch (_error) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
const fullPath = path.join(current, entry.name);
|
|
178
|
+
if (entry.isDirectory()) {
|
|
179
|
+
if (DEFAULT_IGNORED_DIRS.has(entry.name)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
stack.push(fullPath);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!entry.isFile()) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
191
|
+
if (extSet.size > 0 && !extSet.has(ext)) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
files.push(fullPath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return files;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function extractSymbolFromLine(line = '') {
|
|
202
|
+
const patterns = [
|
|
203
|
+
/\bfunction\s+([A-Za-z_$][\w$]*)\s*\(/,
|
|
204
|
+
/\bclass\s+([A-Za-z_$][\w$]*)\b/,
|
|
205
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/,
|
|
206
|
+
/\b([A-Za-z_$][\w$]*)\s*:\s*(?:async\s*)?\(/,
|
|
207
|
+
/\b([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(/
|
|
208
|
+
];
|
|
209
|
+
for (const pattern of patterns) {
|
|
210
|
+
const match = pattern.exec(line);
|
|
211
|
+
if (match && match[1]) {
|
|
212
|
+
return match[1];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function computeHitScore({ query, queryTokens, line, symbol }) {
|
|
219
|
+
const loweredLine = `${line || ''}`.toLowerCase();
|
|
220
|
+
const normalizedQuery = `${query || ''}`.toLowerCase().trim();
|
|
221
|
+
const normalizedSymbol = `${symbol || ''}`.toLowerCase();
|
|
222
|
+
if (queryTokens.length === 0) {
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const tokenMatches = queryTokens.filter((token) => loweredLine.includes(token)).length;
|
|
227
|
+
if (tokenMatches === 0) {
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const tokenCoverage = tokenMatches / queryTokens.length;
|
|
232
|
+
const hasPhraseMatch = normalizedQuery && loweredLine.includes(normalizedQuery);
|
|
233
|
+
const hasSymbolMatch = normalizedSymbol
|
|
234
|
+
&& queryTokens.some((token) => normalizedSymbol.includes(token) || token.includes(normalizedSymbol));
|
|
235
|
+
|
|
236
|
+
const rawScore = (tokenCoverage * 0.65) + (hasPhraseMatch ? 0.2 : 0) + (hasSymbolMatch ? 0.15 : 0);
|
|
237
|
+
return Math.max(0, Math.min(1, Number(rawScore.toFixed(4))));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function confidenceFromScore(score) {
|
|
241
|
+
if (!Number.isFinite(score) || score <= 0) {
|
|
242
|
+
return 'none';
|
|
243
|
+
}
|
|
244
|
+
if (score >= 0.8) {
|
|
245
|
+
return 'high';
|
|
246
|
+
}
|
|
247
|
+
if (score >= 0.6) {
|
|
248
|
+
return 'medium';
|
|
249
|
+
}
|
|
250
|
+
if (score >= 0.4) {
|
|
251
|
+
return 'low';
|
|
252
|
+
}
|
|
253
|
+
return 'none';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function locateSymbolEvidence({
|
|
257
|
+
workspace,
|
|
258
|
+
query,
|
|
259
|
+
maxHits = DEFAULT_MAX_HITS,
|
|
260
|
+
minScore = DEFAULT_MIN_SCORE,
|
|
261
|
+
minReliableScore = DEFAULT_MIN_RELIABLE_SCORE,
|
|
262
|
+
extensions = DEFAULT_EXTENSIONS
|
|
263
|
+
}) {
|
|
264
|
+
const workspaceRoot = path.resolve(workspace);
|
|
265
|
+
const queryTokens = normalizeQueryTokens(query);
|
|
266
|
+
const candidateFiles = listCandidateFiles(workspaceRoot, extensions);
|
|
267
|
+
const hits = [];
|
|
268
|
+
|
|
269
|
+
for (const filePath of candidateFiles) {
|
|
270
|
+
let content = '';
|
|
271
|
+
try {
|
|
272
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
273
|
+
} catch (_error) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const lines = content.split(/\r?\n/);
|
|
278
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
279
|
+
const line = lines[index];
|
|
280
|
+
const symbol = extractSymbolFromLine(line);
|
|
281
|
+
const score = computeHitScore({ query, queryTokens, line, symbol });
|
|
282
|
+
if (score < minScore) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const relativeFile = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
|
|
286
|
+
hits.push({
|
|
287
|
+
file: relativeFile || path.basename(filePath),
|
|
288
|
+
line: index + 1,
|
|
289
|
+
snippet: line.trim().slice(0, 240),
|
|
290
|
+
symbol: symbol || 'unknown',
|
|
291
|
+
score,
|
|
292
|
+
source: symbol ? 'symbol-heuristic' : 'text-grep'
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
hits.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file) || (a.line - b.line));
|
|
298
|
+
const slicedHits = hits.slice(0, Math.max(1, Math.floor(maxHits)));
|
|
299
|
+
const reliableHits = slicedHits.filter((item) => item.score >= minReliableScore);
|
|
300
|
+
const topScore = slicedHits.length > 0 ? slicedHits[0].score : 0;
|
|
301
|
+
const confidence = confidenceFromScore(topScore);
|
|
302
|
+
const reliable = reliableHits.length > 0;
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
mode: 'symbol-evidence-locate',
|
|
306
|
+
query,
|
|
307
|
+
hits: slicedHits,
|
|
308
|
+
evidence: {
|
|
309
|
+
confidence,
|
|
310
|
+
reliable,
|
|
311
|
+
fallback_action: reliable
|
|
312
|
+
? FALLBACK_ACTION_ALLOW_WRITE
|
|
313
|
+
: FALLBACK_ACTION_CLARIFY_BUSINESS_SCOPE,
|
|
314
|
+
advisory: reliable
|
|
315
|
+
? 'Symbol evidence is reliable; scoped code change can proceed.'
|
|
316
|
+
: 'No reliable symbol evidence found yet. Clarify target module/page/entity and business constraints before deciding whether scoped writes are safe.'
|
|
317
|
+
},
|
|
318
|
+
summary: {
|
|
319
|
+
searched_files: candidateFiles.length,
|
|
320
|
+
candidate_files: candidateFiles.length,
|
|
321
|
+
total_hits: slicedHits.length,
|
|
322
|
+
reliable_hits: reliableHits.length
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function main() {
|
|
328
|
+
const options = parseArgs(process.argv.slice(2));
|
|
329
|
+
const query = resolveQuery(options);
|
|
330
|
+
const payload = locateSymbolEvidence({
|
|
331
|
+
workspace: options.workspace,
|
|
332
|
+
query,
|
|
333
|
+
maxHits: options.maxHits,
|
|
334
|
+
minScore: options.minScore,
|
|
335
|
+
minReliableScore: options.minReliableScore,
|
|
336
|
+
extensions: options.extensions
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (options.json) {
|
|
340
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
341
|
+
} else {
|
|
342
|
+
process.stdout.write(`symbol evidence confidence=${payload.evidence.confidence}\n`);
|
|
343
|
+
process.stdout.write(`hits=${payload.hits.length} reliable=${payload.summary.reliable_hits}\n`);
|
|
344
|
+
process.stdout.write(`fallback_action=${payload.evidence.fallback_action}\n`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (options.strict && payload.evidence.reliable !== true) {
|
|
348
|
+
process.exitCode = 2;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (require.main === module) {
|
|
353
|
+
try {
|
|
354
|
+
main();
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error(`symbol-evidence-locate failed: ${error.message}`);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
module.exports = {
|
|
362
|
+
parseArgs,
|
|
363
|
+
resolveQuery,
|
|
364
|
+
normalizeQueryTokens,
|
|
365
|
+
listCandidateFiles,
|
|
366
|
+
extractSymbolFromLine,
|
|
367
|
+
computeHitScore,
|
|
368
|
+
confidenceFromScore,
|
|
369
|
+
locateSymbolEvidence
|
|
370
|
+
};
|
package/template/.sce/README.md
CHANGED
|
@@ -15,6 +15,7 @@ This project uses **Spec-driven development** - a structured approach where:
|
|
|
15
15
|
- When user requests a feature → Check if Spec exists, if not, help create one
|
|
16
16
|
- When implementing → Follow the Spec's requirements and design
|
|
17
17
|
- When stuck → Read the Spec documents for context
|
|
18
|
+
- When business scene/module/page/entity is unclear → Clarify scope first; do not replace missing understanding with blanket disable
|
|
18
19
|
- Track progress by updating task status
|
|
19
20
|
|
|
20
21
|
---
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
## 4. 复用已有机制,不要平行造轮子
|
|
23
23
|
|
|
24
24
|
- 已有能力优先复用,例如缺陷经验与发布阻断统一走 `errorbook`。
|
|
25
|
+
- 若接管项目中已存在自定义缺陷复盘册、故障账本或问题经验库,必须统一盘点并吸收到 `.sce/errorbook`,不要长期并存混用。
|
|
25
26
|
- 不要在 steering 中额外定义另一套错题、发布、会话或治理模式。
|
|
26
27
|
|
|
27
28
|
## 5. 质量问题必须追根
|
|
@@ -33,3 +34,27 @@
|
|
|
33
34
|
|
|
34
35
|
- 每周、发布前、重大 Spec 收尾后运行 `npm run audit:steering`。
|
|
35
36
|
- 发现问题时优先合并重复、迁移错层、归档历史、删除失效条目。
|
|
37
|
+
|
|
38
|
+
## 7. 业务场景未知时先澄清,不得直接彻底禁用
|
|
39
|
+
|
|
40
|
+
- 不了解业务场景、模块、页面、实体或业务约束时,先补上下文并缩小范围。
|
|
41
|
+
- 禁止把“暂时没理解场景”直接变成 blanket disable、answer-only 或一刀切阻断。
|
|
42
|
+
- 只有明确命中安全、权限、合规或破坏性规则时,才允许阻断;否则先澄清业务范围。
|
|
43
|
+
- 这条规则适用于所有使用 SCE 的项目,不设项目级例外。
|
|
44
|
+
|
|
45
|
+
## 8. 禁止盲改问题
|
|
46
|
+
|
|
47
|
+
- 修问题前先明确现象、复现条件、影响范围和验证方式。
|
|
48
|
+
- 缺少证据时先补日志、数据、接口样本或最小复现,不要靠猜测连续改代码。
|
|
49
|
+
- 两轮修改仍未收敛时,先回到调试和根因分析,不要盲目扩大改动面。
|
|
50
|
+
|
|
51
|
+
## 9. Steering 变更先评估,不得随意增删
|
|
52
|
+
|
|
53
|
+
- 新增、删除或重写 steering 条目前,先判断它是否真属于长期原则,是否应迁到 `CURRENT_CONTEXT.md`、Spec 或项目文档。
|
|
54
|
+
- 未经评估,不要把临时偏好、短期任务或偶发结论直接固化进 steering。
|
|
55
|
+
|
|
56
|
+
## 10. 问题修复时前后端接口不一致默认以后端契约为准
|
|
57
|
+
|
|
58
|
+
- 前端调用后端 API 不匹配时,默认以后端现有接口契约为准。
|
|
59
|
+
- 除非明确要求新建或修改后端接口,否则不要为了迁就前端错误调用去改后端。
|
|
60
|
+
- 优先调整前端请求、映射、类型和兼容处理,使其与后端接口一致。
|