@polymorphism-tech/morph-spec 8.37.1 → 8.38.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/CLAUDE.md +19 -6
- package/README.md +2 -2
- package/bin/morph-spec.js +16 -7
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +3 -3
- package/framework/MORPH.md +25 -22
- package/framework/agents.json +76 -27
- package/framework/docs/CLI.md +54 -17
- package/framework/evals/rubrics/code-quality.md +5 -0
- package/framework/evals/rubrics/condensed.md +6 -2
- package/framework/evals/rubrics/contracts.md +20 -0
- package/framework/evals/rubrics/test-coverage.md +17 -6
- package/framework/hooks/README.md +9 -3
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
- package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
- package/framework/hooks/claude-code/core/validate-completion.js +16 -90
- package/framework/hooks/claude-code/core/worktree-context.js +10 -0
- package/framework/hooks/shared/command-tokens.js +96 -0
- package/framework/hooks/shared/git-exclude.js +44 -0
- package/framework/hooks/shared/git-paths.js +52 -0
- package/framework/hooks/shared/graph-freshness.js +380 -0
- package/framework/hooks/shared/graph-refresh.js +416 -0
- package/framework/hooks/shared/morph-cli.js +17 -5
- package/framework/hooks/shared/redact-secrets.js +55 -0
- package/framework/hooks/shared/telemetry-logger.js +25 -2
- package/framework/hooks/shared/transcript-usage.js +80 -0
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/rules/csharp-standards.md +1 -0
- package/framework/rules/morph-workflow.md +1 -0
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/config.schema.json +41 -1
- package/framework/schemas/feature.schema.json +5 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +36 -11
- package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +39 -10
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +72 -15
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +51 -13
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +34 -9
- package/framework/standards/STANDARDS.json +187 -12
- package/framework/standards/ai-agents/testing-ai.md +2 -1
- package/framework/standards/architecture/code-comments.md +64 -0
- package/framework/standards/backend/dotnet/testing.md +22 -0
- package/framework/standards/frontend/admin-app/data-table.md +562 -0
- package/framework/standards/frontend/admin-app/feedback.md +236 -0
- package/framework/standards/frontend/admin-app/forms.md +232 -0
- package/framework/standards/frontend/admin-app/page-structure.md +203 -0
- package/framework/standards/frontend/nextjs/components.md +37 -58
- package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
- package/framework/standards/frontend/nextjs/forms.md +31 -30
- package/framework/standards/frontend/nextjs/state-management.md +2 -1
- package/framework/standards/frontend/nextjs/testing.md +18 -0
- package/framework/templates/context/design-system.md +3 -3
- package/framework/templates/docs/proposal.md +11 -0
- package/framework/templates/docs/spec.md +15 -0
- package/framework/templates/docs/ui-mockups.md +115 -98
- package/framework/templates/feature/decisions.md +124 -122
- package/package.json +1 -1
- package/src/commands/graph/affected.js +4 -3
- package/src/commands/graph/build.js +109 -18
- package/src/commands/graph/explain.js +8 -5
- package/src/commands/graph/status.js +14 -8
- package/src/commands/observability/cost.js +148 -18
- package/src/commands/project/doctor.js +82 -10
- package/src/commands/project/update.js +25 -0
- package/src/commands/project/worktree.js +73 -90
- package/src/commands/state/advance.js +18 -0
- package/src/commands/state/approve.js +15 -0
- package/src/commands/state/archive.js +62 -9
- package/src/commands/state/finish.js +62 -14
- package/src/commands/state/gate-check.js +8 -1
- package/src/commands/state/gate-decision.js +8 -4
- package/src/commands/state/report-check.js +90 -9
- package/src/commands/state/usecase.js +66 -0
- package/src/commands/tasks/dag.js +17 -2
- package/src/commands/validation/validate-feature.js +6 -1
- package/src/commands/validation/verify.js +20 -1
- package/src/dashboard/scanner/agents-scanner.js +5 -1
- package/src/dashboard/scanner/hooks-scanner.js +8 -1
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ai/comment-stripper.js +101 -4
- package/src/lib/evaluator-report.js +25 -0
- package/src/lib/generators/handoff-generator.js +4 -18
- package/src/lib/git/default-branch.js +52 -0
- package/src/lib/git/tree-fingerprint.js +58 -0
- package/src/lib/git/tree-state.js +167 -0
- package/src/lib/graph/god-nodes.js +12 -3
- package/src/lib/graph/graph-health.js +48 -2
- package/src/lib/graph/graph-loader.js +99 -25
- package/src/lib/graph/graphifyignore.js +145 -28
- package/src/lib/graph/sanitize.js +117 -11
- package/src/lib/risk-detector.js +90 -0
- package/src/lib/specs/acervo-format.js +70 -0
- package/src/lib/specs/acervo-lifecycle.js +264 -0
- package/src/lib/specs/adr-index.js +169 -0
- package/src/lib/specs/revocations.js +156 -0
- package/src/lib/specs/screen-checklist.js +357 -0
- package/src/lib/specs/usecase-check.js +20 -16
- package/src/lib/specs/usecase-promoter.js +103 -38
- package/src/lib/specs/usecase-registry.js +7 -7
- package/src/lib/tasks/tasks-json-parser.js +2 -3
- package/src/lib/telemetry/metrics.js +386 -0
- package/src/lib/telemetry/telemetry-reader.js +161 -51
- package/src/lib/telemetry/verify-event.js +79 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +172 -0
- package/src/lib/validators/code-budget/comment-scanner.js +75 -0
- package/src/lib/validators/code-budget/index.js +9 -0
- package/src/lib/validators/reuse/reuse-validator.js +4 -2
- package/src/lib/validators/reuse/symbol-scanner.js +27 -22
- package/src/lib/validators/validation-runner.js +21 -5
- package/src/lib/validators/verify-runner.js +11 -0
- package/src/lib/worktree/finish-backup.js +739 -0
- package/src/lib/worktree/ignored-triage.js +15 -8
- package/src/lib/worktree/worktree-audit.js +5 -0
- package/src/scripts/setup-infra.js +29 -18
- package/src/utils/agents-installer.js +281 -12
- package/src/utils/hooks-installer.js +75 -15
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extração determinística de declarações de tipo a partir do TEXTO do arquivo.
|
|
3
3
|
*
|
|
4
|
-
* Por que não ler as declarações do grafo: o grafo é reconstruído
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* janela em que ele precisa
|
|
4
|
+
* Por que não ler as declarações do grafo: o grafo só é reconstruído depois de
|
|
5
|
+
* um commit (o refresh em background de framework/hooks/shared/graph-refresh.js),
|
|
6
|
+
* então o código que acabou de ser escrito — o caso que este validador existe
|
|
7
|
+
* para pegar — ainda NÃO está nele. O grafo responde "o que já existia"; o
|
|
8
|
+
* disco responde "o que esta feature acabou de declarar". Inverter os dois
|
|
9
|
+
* papéis produziria um validador cego justamente na janela em que ele precisa
|
|
10
|
+
* morder — ver graph-loader.js `getFreshness`.
|
|
10
11
|
*
|
|
11
12
|
* A varredura é por linha, com regex ancorada no início da declaração. Mesma
|
|
12
13
|
* limitação (e mesmo grau de tolerância) do design-system-validator: uma
|
|
@@ -16,20 +17,11 @@
|
|
|
16
17
|
* @module reuse-symbol-scanner
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
|
-
import { languageOf } from '../../graph/sanitize.js';
|
|
20
|
+
import { isTestPath, languageOf } from '../../graph/sanitize.js';
|
|
20
21
|
|
|
21
22
|
/** Extensões que este scanner sabe ler. Fora daqui, o arquivo é ignorado. */
|
|
22
23
|
const SCANNABLE = /\.(cs|ts|tsx|js|jsx|mjs|cjs)$/i;
|
|
23
24
|
|
|
24
|
-
/**
|
|
25
|
-
* Arquivo de teste, em qualquer das convenções que os dois stacks usam.
|
|
26
|
-
* Duplicar um stub entre três arquivos de teste (`ResizeObserverStub` no
|
|
27
|
-
* ProspectPRO) é ruído conhecido: teste é código descartável por natureza e a
|
|
28
|
-
* regra de reuso não se aplica a ele. Cobre `__tests__/`, `.test.`, `.spec.`,
|
|
29
|
-
* e os projetos `*.Tests/` do .NET.
|
|
30
|
-
*/
|
|
31
|
-
const TEST_FILE = /(^|\/)__tests__\/|\.(test|spec)\.[jt]sx?$|(^|\/)(?:[Tt]ests?|[^/]*\.[Tt]ests?|[^/]*[a-z0-9_]Tests?)\//;
|
|
32
|
-
|
|
33
25
|
/**
|
|
34
26
|
* Diretórios que contêm arquivos com extensão de código mas que não são o
|
|
35
27
|
* código-fonte do projeto. `.morph/` é o caso que apareceu de verdade: o
|
|
@@ -71,17 +63,30 @@ const WEB_DECL =
|
|
|
71
63
|
/** Linha que é claramente comentário — não vale a pena parsear. */
|
|
72
64
|
const COMMENT_LINE = /^\s*(?:\/\/|\/\*|\*|<!--)/;
|
|
73
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} sourceFile
|
|
68
|
+
* @returns {boolean} código do projeto numa extensão lida, fora de `.d.ts` e de
|
|
69
|
+
* pasta que não é fonte — teste incluído
|
|
70
|
+
*/
|
|
71
|
+
export function isProjectSource(sourceFile) {
|
|
72
|
+
const f = String(sourceFile || '').replace(/\\/g, '/');
|
|
73
|
+
return SCANNABLE.test(f) && !DECLARATION_FILE.test(f) && !NOT_SOURCE.test(f);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {string} sourceFile
|
|
78
|
+
* @returns {boolean} arquivo de teste, pelo mesmo predicado do grafo
|
|
79
|
+
*/
|
|
80
|
+
export function isTestFile(sourceFile) {
|
|
81
|
+
return isTestPath(String(sourceFile || '').replace(/\\/g, '/'));
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
/**
|
|
75
85
|
* @param {string} sourceFile
|
|
76
86
|
* @returns {boolean} o arquivo entra na varredura de reuso
|
|
77
87
|
*/
|
|
78
88
|
export function isScannableFile(sourceFile) {
|
|
79
|
-
|
|
80
|
-
if (!SCANNABLE.test(f)) return false;
|
|
81
|
-
if (DECLARATION_FILE.test(f)) return false;
|
|
82
|
-
if (TEST_FILE.test(f)) return false;
|
|
83
|
-
if (NOT_SOURCE.test(f)) return false;
|
|
84
|
-
return true;
|
|
89
|
+
return isProjectSource(sourceFile) && !isTestFile(sourceFile);
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/**
|
|
@@ -24,7 +24,7 @@ import { aggregateScores, loadRubric } from './score-aggregator.js';
|
|
|
24
24
|
*/
|
|
25
25
|
const HEAVY_VALIDATORS = new Set([
|
|
26
26
|
'architecture', 'packages', 'contract-compliance',
|
|
27
|
-
'css', 'design-system', 'reuse'
|
|
27
|
+
'css', 'design-system', 'reuse', 'code-budget'
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -89,6 +89,11 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
89
89
|
if (!options.skipReuse && mode !== 'light') {
|
|
90
90
|
validatorIds.push('reuse');
|
|
91
91
|
}
|
|
92
|
+
// Mesma política, pelo mesmo motivo: comentário e orçamento de teste valem
|
|
93
|
+
// em qualquer stack, e a medida é da feature, não de um checkpoint.
|
|
94
|
+
if (mode !== 'light') {
|
|
95
|
+
validatorIds.push('code-budget');
|
|
96
|
+
}
|
|
92
97
|
|
|
93
98
|
if (validatorIds.length === 0) {
|
|
94
99
|
// Say which of the two it is. On a pure `dotnet` project every default
|
|
@@ -135,6 +140,7 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
135
140
|
// projeto sem grafo). Um validador assim não acha nada — e "não achou nada"
|
|
136
141
|
// é indistinguível de "verificou e está limpo" se ninguém contar.
|
|
137
142
|
const selfSkipped = [];
|
|
143
|
+
result.skipped = selfSkipped;
|
|
138
144
|
|
|
139
145
|
// Run each validator
|
|
140
146
|
for (const validatorId of uniqueValidators) {
|
|
@@ -149,15 +155,20 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
149
155
|
|
|
150
156
|
if (validatorResult) {
|
|
151
157
|
if (validatorResult._skipReason) {
|
|
152
|
-
selfSkipped.push(
|
|
158
|
+
selfSkipped.push({
|
|
159
|
+
validator: validatorId,
|
|
160
|
+
reason: validatorResult._skipReason,
|
|
161
|
+
unavailable: validatorResult._skipUnavailable === true,
|
|
162
|
+
});
|
|
153
163
|
}
|
|
154
164
|
const { errors, warnings, issues } = normalizeValidatorResult(validatorId, validatorResult);
|
|
155
165
|
result.errors += errors;
|
|
156
166
|
result.warnings += warnings;
|
|
157
167
|
result.issues.push(...issues);
|
|
158
168
|
|
|
159
|
-
//
|
|
160
|
-
|
|
169
|
+
// A validator that skipped itself checked nothing: in `results` it would
|
|
170
|
+
// be scored as a clean 10 and dilute the ones that ran.
|
|
171
|
+
if (!validatorResult._skipReason) result.results[validatorId] = { passed: errors === 0, issues };
|
|
161
172
|
}
|
|
162
173
|
} catch (err) {
|
|
163
174
|
// A validator that throws is a FAILED validation, not a silent skip.
|
|
@@ -187,7 +198,7 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
187
198
|
// `validators: pass` do `morph-spec verify` tendo verificado exatamente nada
|
|
188
199
|
// — o modo de falha que este repositório já pagou caro (guardrail inerte).
|
|
189
200
|
if (selfSkipped.length > 0 && selfSkipped.length === uniqueValidators.length) {
|
|
190
|
-
result._skipReason = `No validators ran — ${selfSkipped.join('; ')}`;
|
|
201
|
+
result._skipReason = `No validators ran — ${selfSkipped.map((s) => `${s.validator}: ${s.reason}`).join('; ')}`;
|
|
191
202
|
result.summary = result._skipReason;
|
|
192
203
|
return result;
|
|
193
204
|
}
|
|
@@ -325,6 +336,11 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
|
|
|
325
336
|
return await validateReuse(projectPath, featureName, options);
|
|
326
337
|
}
|
|
327
338
|
|
|
339
|
+
case 'code-budget': {
|
|
340
|
+
const { validateCodeBudget } = await import('./code-budget/code-budget-validator.js');
|
|
341
|
+
return await validateCodeBudget(projectPath, featureName, options);
|
|
342
|
+
}
|
|
343
|
+
|
|
328
344
|
case 'nextjs-component': {
|
|
329
345
|
const { validateNextComponentFiles } = await import('./nextjs/next-component-validator.js');
|
|
330
346
|
return await validateNextComponentFiles(projectPath, options);
|
|
@@ -975,12 +975,15 @@ async function runValidatorsNode({ projectPath, featureName, rubricId, _runValid
|
|
|
975
975
|
|
|
976
976
|
const durationMs = Date.now() - start;
|
|
977
977
|
|
|
978
|
+
const skipped = validationResult?.skipped ?? [];
|
|
979
|
+
|
|
978
980
|
if (validationResult && validationResult._skipReason) {
|
|
979
981
|
return {
|
|
980
982
|
status: 'skip',
|
|
981
983
|
score: null,
|
|
982
984
|
action: null,
|
|
983
985
|
ran: [],
|
|
986
|
+
skipped,
|
|
984
987
|
reason: validationResult._skipReason,
|
|
985
988
|
priorityFixes: [],
|
|
986
989
|
durationMs,
|
|
@@ -995,6 +998,7 @@ async function runValidatorsNode({ projectPath, featureName, rubricId, _runValid
|
|
|
995
998
|
score: scoreResult.score,
|
|
996
999
|
action: scoreResult.action,
|
|
997
1000
|
ran: Object.keys(validationResult.results || {}),
|
|
1001
|
+
skipped,
|
|
998
1002
|
reason: null,
|
|
999
1003
|
priorityFixes: scoreResult.priorityFixes || [],
|
|
1000
1004
|
durationMs,
|
|
@@ -1002,6 +1006,13 @@ async function runValidatorsNode({ projectPath, featureName, rubricId, _runValid
|
|
|
1002
1006
|
}
|
|
1003
1007
|
|
|
1004
1008
|
function appendValidatorsFeedback(feedback, validatorsNode) {
|
|
1009
|
+
// A validator that could not check (reuse without a graph) is invisible in a
|
|
1010
|
+
// green score; when the whole node skipped, its own line already says why.
|
|
1011
|
+
if (validatorsNode.status !== 'skip') {
|
|
1012
|
+
for (const s of validatorsNode.skipped || []) {
|
|
1013
|
+
if (s.unavailable) feedback.push(`[validators/${s.validator}] não rodou: ${s.reason}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1005
1016
|
for (const fix of validatorsNode.priorityFixes || []) {
|
|
1006
1017
|
const topIssue = fix.issues && fix.issues[0];
|
|
1007
1018
|
if (!topIssue) continue;
|