@polymorphism-tech/morph-spec 8.47.0 → 9.0.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 +48 -39
- package/README.md +3 -4
- package/bin/morph-spec.js +37 -51
- package/bin/validate.js +3 -111
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +75 -18
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-proposal.md +2 -50
- package/framework/commands/morph-status.md +3 -3
- package/framework/docs/CLI.md +585 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/comment-guard.js +1 -1
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
- package/framework/hooks/claude-code/core/gate-guard.js +2 -11
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
- package/framework/hooks/claude-code/core/state-sync.js +7 -21
- package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
- package/framework/hooks/claude-code/core/validate-completion.js +33 -97
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
- package/framework/hooks/claude-code/core/worktree-context.js +5 -4
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +1 -1
- package/framework/hooks/shared/build-check.js +10 -6
- package/framework/hooks/shared/command-tokens.js +169 -0
- package/framework/hooks/shared/comment-rules.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +10 -23
- package/framework/hooks/shared/comment-stripper.js +1 -1
- package/framework/hooks/shared/e2e-check.js +64 -27
- package/framework/hooks/shared/errata.js +72 -0
- package/framework/hooks/shared/feature-locator.js +1 -1
- package/framework/hooks/shared/frontend-locations.js +6 -0
- package/framework/hooks/shared/gate-question.js +18 -0
- package/framework/hooks/shared/phase-utils.js +15 -32
- package/framework/hooks/shared/slice-progress.js +118 -0
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +61 -87
- package/framework/hooks/shared/state-writer.js +2 -3
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- 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 +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +77 -152
- package/framework/standards/STANDARDS.json +86 -1218
- package/framework/standards/STANDARDS.meta.json +1305 -0
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/architecture/code-comments.md +6 -6
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +6 -170
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/e2e/e2e.js +2 -2
- package/src/commands/observability/cost.js +18 -60
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +9 -9
- package/src/commands/project/fleet.js +4 -4
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +4 -0
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +79 -25
- package/src/commands/project/status.js +29 -69
- package/src/commands/project/update.js +17 -1
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +62 -46
- package/src/commands/state/approve.js +81 -141
- package/src/commands/state/archive.js +20 -84
- package/src/commands/state/auto-approval.js +68 -0
- package/src/commands/state/create.js +28 -1
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +11 -23
- package/src/commands/state/gate-check.js +85 -84
- package/src/commands/state/gate-decision.js +6 -4
- package/src/commands/state/handoff.js +1 -1
- package/src/commands/state/list.js +9 -9
- package/src/commands/state/report-check.js +157 -311
- package/src/commands/state/retype.js +9 -3
- package/src/commands/validation/mutate.js +77 -14
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +60 -135
- package/src/commands/validation/verify.js +40 -72
- package/src/core/paths/output-schema.js +19 -66
- package/src/core/state/gate-order.js +5 -6
- package/src/core/state/lifecycle-audit.js +2 -2
- package/src/core/state/state-manager.js +17 -98
- package/src/core/templates/template-registry.js +2 -2
- package/src/core/templates/template-seed.js +22 -0
- package/src/core/work-types.js +10 -10
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
- package/src/dashboard/app/components/CommandPalette.jsx +5 -5
- package/src/dashboard/app/components/EntityLink.jsx +1 -1
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
- package/src/dashboard/app/lib/phases.js +2 -4
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
- package/src/dashboard/app/pages/Features.jsx +9 -9
- package/src/dashboard/app/pages/Guide.jsx +2 -2
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
- package/src/dashboard/app/pages/Standards.jsx +1 -1
- package/src/dashboard/scanner/activity-scanner.js +5 -15
- package/src/dashboard/scanner/agents-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +2 -2
- package/src/dashboard/scanner/features-scanner.js +95 -60
- package/src/dashboard/scanner/project-api.js +1 -1
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ci/ci-verify.js +17 -1
- package/src/lib/ci/github-ci.js +1 -1
- package/src/lib/detectors/index.js +3 -8
- package/src/lib/generators/handoff-generator.js +34 -65
- package/src/lib/generators/settings-generator.js +11 -89
- package/src/lib/git/evidence-staleness.js +1 -1
- package/src/lib/git/tree-fingerprint.js +19 -7
- package/src/lib/git/tree-state.js +2 -2
- package/src/lib/heavy/cleanup.js +1 -5
- package/src/lib/installers/memory-installer.js +6 -8
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +46 -9
- package/src/lib/plan/artifact-vocabulary.js +60 -0
- package/src/lib/plan/checks.js +234 -0
- package/src/lib/plan/plan-parser.js +209 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/review-reports.js +222 -0
- package/src/lib/risk-detector.js +131 -140
- package/src/lib/specs/adr-index.js +50 -69
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +44 -0
- package/src/lib/specs/gfm-table.js +17 -0
- package/src/lib/specs/old-format.js +68 -0
- package/src/lib/specs/revocations.js +37 -95
- package/src/lib/specs/screen-checklist.js +18 -17
- package/src/lib/stack/dotnet-build-ignores.js +47 -0
- package/src/lib/stack/stack-scan.js +267 -0
- package/src/lib/standards/registry.js +43 -0
- package/src/lib/standards/sections.js +58 -0
- package/src/lib/tasks/eval-node.js +4 -12
- package/src/lib/tasks/test-nodes.js +1 -74
- package/src/lib/tasks/test-runner.js +75 -117
- package/src/lib/telemetry/metrics.js +8 -69
- package/src/lib/telemetry/telemetry-reader.js +5 -118
- package/src/lib/telemetry/verify-event.js +5 -7
- package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
- package/src/lib/validators/reuse/reuse-validator.js +22 -45
- package/src/lib/validators/traceability-check.js +85 -129
- package/src/lib/validators/verify-runner.js +118 -235
- package/src/lib/worktree/docker-audit.js +1 -2
- package/src/lib/worktree/fleet-scanner.js +25 -38
- package/src/scripts/setup-infra.js +9 -0
- package/src/utils/agents-installer.js +76 -9
- package/src/utils/claude-settings-manager.js +0 -35
- package/src/utils/file-copier.js +27 -1
- package/src/utils/hooks-installer.js +16 -9
- package/src/utils/skills-installer.js +1 -2
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
|
@@ -227,25 +227,18 @@ export function getFeature(featureName, projectPath) {
|
|
|
227
227
|
* Derive phase from filesystem (phase deleted from state).
|
|
228
228
|
*
|
|
229
229
|
* Walks the phase map in descending order and returns the first phase whose
|
|
230
|
-
* CANONICAL ARTIFACT exists. An empty folder is not enough —
|
|
231
|
-
*
|
|
232
|
-
* advances the phase before plan content is written, then
|
|
233
|
-
* `enforce-phase-writes` blocks writes back to `0-proposal/`.
|
|
230
|
+
* CANONICAL ARTIFACT exists. An empty folder is not enough — a pre-created
|
|
231
|
+
* sibling folder never advances the phase before its content is written.
|
|
234
232
|
*
|
|
235
233
|
* Canonical artifacts (one per phase) match MORPH.md §3:
|
|
236
|
-
* - 4-review
|
|
237
|
-
* - 3-implement →
|
|
238
|
-
* -
|
|
239
|
-
* -
|
|
240
|
-
* - 0-proposal → proposal.md
|
|
234
|
+
* - 4-review → verifier.md
|
|
235
|
+
* - 3-implement → any slice report (`reports/*.md`)
|
|
236
|
+
* - 1-design → design-system.md
|
|
237
|
+
* - 2-plan → plan.md
|
|
241
238
|
*
|
|
242
|
-
* If no canonical file is found, returns `'
|
|
243
|
-
*
|
|
244
|
-
* @param {string} featureName
|
|
245
|
-
* @param {string} [projectPath]
|
|
246
|
-
* @returns {string} Phase name
|
|
239
|
+
* If no canonical file is found, returns `'plan'` (the starting phase).
|
|
247
240
|
*/
|
|
248
|
-
export const PHASE_INDEX = {
|
|
241
|
+
export const PHASE_INDEX = { plan: 0, uiux: 1, implement: 2, review: 3 };
|
|
249
242
|
|
|
250
243
|
/**
|
|
251
244
|
* The canonical artifact of each phase, descending. Exported so a caller that
|
|
@@ -253,32 +246,61 @@ export const PHASE_INDEX = { proposal: 0, uiux: 1, plan: 2, implement: 3, review
|
|
|
253
246
|
* tree, say) can answer the same question without re-encoding the map.
|
|
254
247
|
*/
|
|
255
248
|
export const PHASE_ARTIFACTS = [
|
|
256
|
-
['4-review', 'review', '
|
|
257
|
-
['3-implement', 'implement', '
|
|
258
|
-
['2-plan', 'plan', 'spec.md'],
|
|
249
|
+
['4-review', 'review', 'verifier.md'],
|
|
250
|
+
['3-implement', 'implement', 'reports/'],
|
|
259
251
|
['1-design', 'uiux', 'design-system.md'],
|
|
260
|
-
['
|
|
252
|
+
['2-plan', 'plan', 'plan.md'],
|
|
261
253
|
];
|
|
262
254
|
|
|
255
|
+
const DESIGN_DIR = '1-design';
|
|
256
|
+
|
|
263
257
|
const gatesOf = (feature) => feature?.approvals || feature?.approvalGates;
|
|
264
258
|
|
|
265
259
|
/**
|
|
266
260
|
* Gate-implied phase from an ALREADY-LOADED feature object — no disk access.
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
261
|
+
* An approved gate authorizes the NEXT phase:
|
|
262
|
+
* review approved → review (terminal)
|
|
263
|
+
* plan approved → implement, or uiux while the design branch was taken
|
|
264
|
+
* (`1-design/` exists) and its gate is still open
|
|
265
|
+
* otherwise → plan
|
|
271
266
|
*
|
|
272
267
|
* @param {Object|null} feature
|
|
268
|
+
* @param {boolean} [designStarted] - whether `1-design/` exists for the feature
|
|
273
269
|
* @returns {string} phase name
|
|
274
270
|
*/
|
|
275
|
-
export function gateImpliedPhaseFrom(feature) {
|
|
271
|
+
export function gateImpliedPhaseFrom(feature, designStarted = false) {
|
|
276
272
|
const gates = gatesOf(feature);
|
|
277
273
|
const ok = (g) => gates?.[g]?.approved === true;
|
|
278
274
|
if (ok('review')) return 'review';
|
|
279
|
-
if (ok('plan')) return 'implement';
|
|
280
|
-
|
|
281
|
-
|
|
275
|
+
if (ok('plan')) return designStarted && !ok('uiux') ? 'uiux' : 'implement';
|
|
276
|
+
return 'plan';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Artifact predicate over a feature dir on disk. A path ending in `/` asks for
|
|
281
|
+
* any `.md` file directly under it: an empty folder is not an artifact.
|
|
282
|
+
*/
|
|
283
|
+
export function diskArtifactProbe(featureDir) {
|
|
284
|
+
return (rel) => {
|
|
285
|
+
const abs = join(featureDir, ...rel.split('/').filter(Boolean));
|
|
286
|
+
if (!rel.endsWith('/')) return existsSync(abs);
|
|
287
|
+
try {
|
|
288
|
+
return readdirSync(abs, { withFileTypes: true }).some((e) => e.isFile() && e.name.endsWith('.md'));
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** The same predicate over a set of feature-relative POSIX paths (a git tree). */
|
|
296
|
+
export function fileSetArtifactProbe(files) {
|
|
297
|
+
return (rel) => {
|
|
298
|
+
if (!rel.endsWith('/')) return files.has(rel);
|
|
299
|
+
for (const f of files) {
|
|
300
|
+
if (f.startsWith(rel) && f.endsWith('.md') && !f.slice(rel.length).includes('/')) return true;
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
282
304
|
}
|
|
283
305
|
|
|
284
306
|
/**
|
|
@@ -286,44 +308,33 @@ export function gateImpliedPhaseFrom(feature) {
|
|
|
286
308
|
* access of its own, so the artifact universe can be a git tree.
|
|
287
309
|
*
|
|
288
310
|
* @param {Object|null} feature
|
|
289
|
-
* @param {(relPath: string) => boolean} hasArtifact -
|
|
290
|
-
* paths relative to the feature dir
|
|
311
|
+
* @param {(relPath: string) => boolean} hasArtifact - {@link diskArtifactProbe}
|
|
312
|
+
* or {@link fileSetArtifactProbe}: POSIX paths relative to the feature dir
|
|
291
313
|
* @returns {string} phase name
|
|
292
314
|
*/
|
|
293
315
|
export function derivePhaseFrom(feature, hasArtifact) {
|
|
294
|
-
let artifactPhase = '
|
|
316
|
+
let artifactPhase = 'plan';
|
|
295
317
|
for (const [folder, phase, canonical] of PHASE_ARTIFACTS) {
|
|
296
318
|
if (hasArtifact(`${folder}/${canonical}`)) { artifactPhase = phase; break; }
|
|
297
319
|
}
|
|
298
320
|
|
|
299
321
|
// Gate-aware: an approved gate advances the phase even before the next
|
|
300
|
-
// phase's first canonical artifact exists
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
const implied = gateImpliedPhaseFrom(feature);
|
|
322
|
+
// phase's first canonical artifact exists, or that artifact could never be
|
|
323
|
+
// written. Take whichever of (artifact, gate-implied) is later.
|
|
324
|
+
const designStarted = hasArtifact(DESIGN_DIR) || hasArtifact(`${DESIGN_DIR}/design-system.md`);
|
|
325
|
+
const implied = gateImpliedPhaseFrom(feature, designStarted);
|
|
304
326
|
return PHASE_INDEX[implied] > PHASE_INDEX[artifactPhase] ? implied : artifactPhase;
|
|
305
327
|
}
|
|
306
328
|
|
|
307
|
-
/**
|
|
308
|
-
* The phase implied purely by approved gates, independent of which artifacts
|
|
309
|
-
* exist on disk. An approved gate authorizes the NEXT phase, so:
|
|
310
|
-
* review approved → review (terminal)
|
|
311
|
-
* plan approved → implement
|
|
312
|
-
* uiux approved → plan
|
|
313
|
-
* otherwise → proposal (proposal-approval's fork between uiux/plan is
|
|
314
|
-
* resolved by enforce-phase-writes, not here)
|
|
315
|
-
* @returns {string} phase name
|
|
316
|
-
*/
|
|
329
|
+
/** The phase implied purely by approved gates — see {@link gateImpliedPhaseFrom}. */
|
|
317
330
|
export function gateImpliedPhase(featureName, projectPath) {
|
|
318
|
-
|
|
331
|
+
const designDir = join(projectPath || process.cwd(), '.morph/features', featureName, DESIGN_DIR);
|
|
332
|
+
return gateImpliedPhaseFrom(getFeature(featureName, projectPath), existsSync(designDir));
|
|
319
333
|
}
|
|
320
334
|
|
|
321
335
|
export function derivePhaseForFeature(featureName, projectPath) {
|
|
322
336
|
const basePath = join(projectPath || process.cwd(), '.morph/features', featureName);
|
|
323
|
-
return derivePhaseFrom(
|
|
324
|
-
getFeature(featureName, projectPath),
|
|
325
|
-
(rel) => existsSync(join(basePath, rel)),
|
|
326
|
-
);
|
|
337
|
+
return derivePhaseFrom(getFeature(featureName, projectPath), diskArtifactProbe(basePath));
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
/**
|
|
@@ -342,7 +353,7 @@ export function getFeaturePhase(featureName, projectPath) {
|
|
|
342
353
|
/**
|
|
343
354
|
* Get approval status for a gate.
|
|
344
355
|
* @param {string} featureName
|
|
345
|
-
* @param {string} gate - '
|
|
356
|
+
* @param {string} gate - 'plan'|'uiux'|'review'
|
|
346
357
|
* @param {string} [projectPath]
|
|
347
358
|
* @returns {boolean}
|
|
348
359
|
*/
|
|
@@ -384,43 +395,6 @@ export function getOutputs(featureName, projectPath) {
|
|
|
384
395
|
return feature?.outputs || null;
|
|
385
396
|
}
|
|
386
397
|
|
|
387
|
-
/**
|
|
388
|
-
* Get uncompleted required outputs for current phase.
|
|
389
|
-
* @param {string} featureName
|
|
390
|
-
* @param {string} [projectPath]
|
|
391
|
-
* @returns {Array<{type: string, path: string}>}
|
|
392
|
-
*/
|
|
393
|
-
/**
|
|
394
|
-
* Get task statistics directly from taskList (preferred over feature.tasks counters).
|
|
395
|
-
* Excludes expanded tasks from counts, matching calculateProgress() behavior.
|
|
396
|
-
*
|
|
397
|
-
* @param {Object} feature - Feature state object
|
|
398
|
-
* @returns {{ total: number, completed: number, inProgress: number, pending: number }}
|
|
399
|
-
*/
|
|
400
|
-
export function getTaskStats(feature) {
|
|
401
|
-
const list = feature?.taskList || [];
|
|
402
|
-
if (list.length > 0) {
|
|
403
|
-
const active = list.filter(t => t.status !== 'expanded');
|
|
404
|
-
return {
|
|
405
|
-
total: active.length,
|
|
406
|
-
completed: active.filter(t => t.status === 'completed').length,
|
|
407
|
-
inProgress: active.filter(t => t.status === 'in_progress').length,
|
|
408
|
-
pending: active.filter(t => t.status === 'pending').length,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
// Fallback: read from legacy counter object
|
|
412
|
-
const tasks = feature?.tasks;
|
|
413
|
-
if (tasks && typeof tasks === 'object' && !Array.isArray(tasks)) {
|
|
414
|
-
return {
|
|
415
|
-
total: tasks.total || 0,
|
|
416
|
-
completed: tasks.completed || 0,
|
|
417
|
-
inProgress: tasks.inProgress || 0,
|
|
418
|
-
pending: tasks.pending || 0,
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
return { total: 0, completed: 0, inProgress: 0, pending: 0 };
|
|
422
|
-
}
|
|
423
|
-
|
|
424
398
|
export function getMissingOutputs(featureName, projectPath) {
|
|
425
399
|
const feature = getFeature(featureName, projectPath);
|
|
426
400
|
if (!feature?.outputs) return [];
|
|
@@ -101,7 +101,7 @@ export function persistFeature(featureName, feature, projectPath) {
|
|
|
101
101
|
* canonical shape — onto the feature and persists it. Returns true on success.
|
|
102
102
|
*
|
|
103
103
|
* @param {string} featureName
|
|
104
|
-
* @param {string} gate - '
|
|
104
|
+
* @param {string} gate - 'plan'|'uiux'|'review'
|
|
105
105
|
* @param {{ approvedBy?: string, timestamp?: string }} [meta]
|
|
106
106
|
* @param {string} [projectPath]
|
|
107
107
|
*/
|
|
@@ -142,9 +142,8 @@ export function createDefaultFeature(featureName) {
|
|
|
142
142
|
createdAt: now,
|
|
143
143
|
updatedAt: now,
|
|
144
144
|
approvalGates: {
|
|
145
|
-
proposal: { approved: false, timestamp: null, approvedBy: null },
|
|
146
|
-
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
147
145
|
plan: { approved: false, timestamp: null, approvedBy: null },
|
|
146
|
+
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
148
147
|
review: { approved: false, timestamp: null, approvedBy: null },
|
|
149
148
|
},
|
|
150
149
|
outputs: {},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Centralized MCP registry with phase relevance and install configuration for morph-spec workflows",
|
|
4
4
|
"mcps": {
|
|
5
5
|
"context7": {
|
|
6
|
-
"phases": ["
|
|
6
|
+
"phases": ["plan", "implement"],
|
|
7
7
|
"tools": ["resolve-library-id", "query-docs"],
|
|
8
8
|
"serverNames": ["context7", "plugin_context7_context7"],
|
|
9
9
|
"detection": "enabledPlugins includes context7 or mcpServers includes context7",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
}
|
|
217
217
|
},
|
|
218
218
|
"graphify": {
|
|
219
|
-
"phases": ["
|
|
219
|
+
"phases": ["plan", "implement", "review"],
|
|
220
220
|
"tools": ["query_graph", "get_node", "get_neighbors", "get_community", "god_nodes", "graph_stats", "shortest_path", "list_prs", "get_pr_impact", "triage_prs"],
|
|
221
221
|
"detection": "graphify-out/graph.json exists in the project root (written by morph-spec graph build, or by the graphify post-commit hook)",
|
|
222
222
|
"fallback": "Comportamento atual: Grep/Glob/Read. O grafo é um subconjunto estrito do grep — no eval não encontrou um único arquivo que o grep perderia — então cair para grep não perde recall, só devolve mais ruído",
|
|
@@ -281,9 +281,8 @@
|
|
|
281
281
|
},
|
|
282
282
|
"_phaseMatrixNote": "Keys must be the v5 phases from framework/phases.json. The v4 keys 'design' and 'tasks' both collapsed into 'plan'; 'review' was absent entirely, so a project in review resolved to undefined. Stack-scoped MCPs (asaas, neon) stay out of this matrix on purpose — only universal ones are listed.",
|
|
283
283
|
"phaseMatrix": {
|
|
284
|
-
"proposal": ["context7", "graphify"],
|
|
285
|
-
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
286
284
|
"plan": ["context7", "graphify"],
|
|
285
|
+
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
287
286
|
"implement": ["context7", "playwright", "vercel", "morph-assets", "graphify"],
|
|
288
287
|
"review": ["playwright", "context7", "graphify"]
|
|
289
288
|
}
|
package/framework/phases.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "5.0.0",
|
|
3
3
|
"phases": {
|
|
4
|
-
"
|
|
5
|
-
"id": "
|
|
4
|
+
"plan": {
|
|
5
|
+
"id": "plan",
|
|
6
6
|
"index": 0,
|
|
7
|
-
"folder": "
|
|
8
|
-
"displayName": "
|
|
7
|
+
"folder": "2-plan",
|
|
8
|
+
"displayName": "Plan",
|
|
9
9
|
"goals": [
|
|
10
|
-
"Understand the business problem and
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"Understand the business problem and scan the stack (brownfield) or assume the default (greenfield)",
|
|
11
|
+
"Write plan.md — scenarios with proof, decisions, reuse and slices",
|
|
12
|
+
"Run plan-check to produce checks.json and reach the plan gate"
|
|
13
13
|
],
|
|
14
|
-
"requiredOutputs": ["
|
|
15
|
-
"optionalOutputs": [
|
|
14
|
+
"requiredOutputs": ["plan", "checks"],
|
|
15
|
+
"optionalOutputs": [],
|
|
16
16
|
"pausePoints": [
|
|
17
|
-
{ "id": "
|
|
17
|
+
{ "id": "plan", "label": "Gate do plano — Approve plan.md + checks.json" }
|
|
18
18
|
],
|
|
19
|
-
"skill": "morph-
|
|
19
|
+
"skill": "morph-plan"
|
|
20
20
|
},
|
|
21
21
|
"uiux": {
|
|
22
22
|
"id": "uiux",
|
|
@@ -35,50 +35,34 @@
|
|
|
35
35
|
],
|
|
36
36
|
"skill": "morph-uiux"
|
|
37
37
|
},
|
|
38
|
-
"plan": {
|
|
39
|
-
"id": "plan",
|
|
40
|
-
"index": 2,
|
|
41
|
-
"folder": "2-plan",
|
|
42
|
-
"displayName": "Plan",
|
|
43
|
-
"goals": [
|
|
44
|
-
"Write spec.md (technical spec)",
|
|
45
|
-
"Write tasks.json (ordered task breakdown)"
|
|
46
|
-
],
|
|
47
|
-
"requiredOutputs": ["spec", "tasks"],
|
|
48
|
-
"optionalOutputs": ["decisions"],
|
|
49
|
-
"pausePoints": [
|
|
50
|
-
{ "id": "plan", "label": "Gate 2 — Approve spec + tasks" }
|
|
51
|
-
],
|
|
52
|
-
"skill": "morph-plan"
|
|
53
|
-
},
|
|
54
38
|
"implement": {
|
|
55
39
|
"id": "implement",
|
|
56
|
-
"index":
|
|
40
|
+
"index": 2,
|
|
57
41
|
"folder": "3-implement",
|
|
58
42
|
"displayName": "Implementation",
|
|
59
43
|
"goals": [
|
|
60
|
-
"Execute
|
|
61
|
-
"
|
|
62
|
-
"
|
|
44
|
+
"Execute the slices of plan.md, in parallel waves when plan-check allows",
|
|
45
|
+
"Close a slice only on verify green and its scenarios proven",
|
|
46
|
+
"One report per slice (3-implement/reports/F{n}.md, with **Arquivos:** and **Standards:**) — the implementation record"
|
|
63
47
|
],
|
|
64
|
-
"requiredOutputs": [
|
|
48
|
+
"requiredOutputs": [],
|
|
65
49
|
"optionalOutputs": ["evidencias"],
|
|
66
50
|
"pausePoints": [],
|
|
67
|
-
"skill": "morph-
|
|
51
|
+
"skill": "morph-apply"
|
|
68
52
|
},
|
|
69
53
|
"review": {
|
|
70
54
|
"id": "review",
|
|
71
|
-
"index":
|
|
55
|
+
"index": 3,
|
|
72
56
|
"folder": "4-review",
|
|
73
57
|
"displayName": "Review",
|
|
74
58
|
"goals": [
|
|
75
|
-
"
|
|
76
|
-
"
|
|
59
|
+
"Verifier proves every scenario; reviewer (and security, when required) judge the diff at a fixed SHA",
|
|
60
|
+
"verifier.md + reviewer.md (+ security.md) with SHIP/FIX and evidence-typed blockers, then Gate 3"
|
|
77
61
|
],
|
|
78
|
-
"requiredOutputs": ["
|
|
79
|
-
"optionalOutputs": [],
|
|
62
|
+
"requiredOutputs": ["verifierReport", "reviewerReport"],
|
|
63
|
+
"optionalOutputs": ["securityReport"],
|
|
80
64
|
"pausePoints": [
|
|
81
|
-
{ "id": "review", "label": "Gate 3 — Approve review
|
|
65
|
+
{ "id": "review", "label": "Gate 3 — Approve the review reports" }
|
|
82
66
|
],
|
|
83
67
|
"skill": "morph-review"
|
|
84
68
|
}
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
"type": "object",
|
|
10
10
|
"additionalProperties": true,
|
|
11
11
|
"properties": {
|
|
12
|
+
"stack": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Stack tags joined by `+` (e.g. `dotnet+nextjs`). Decides the build node's commands, which assets install, and that bin/ and obj/ go into .gitignore."
|
|
15
|
+
},
|
|
16
|
+
"frontendPath": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Folder of the frontend, relative to the project root: where the build node runs `tsc`. Absent: the root."
|
|
19
|
+
},
|
|
20
|
+
"backendPath": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Folder of the .NET solution, relative to the project root: where the build node runs `dotnet build`. Absent: the root, or the repository's single .sln/.slnx (else single .csproj) when the root has none."
|
|
23
|
+
},
|
|
12
24
|
"ci": {
|
|
13
25
|
"type": "boolean",
|
|
14
26
|
"description": "false turns `verify {f} --ci` into the local verify (with the reason printed). Absent or true: the Gate 3 proof comes from the GitHub Actions workflow that runs `morph-spec verify` (created on first use), whenever origin is GitHub and gh is authenticated."
|
|
@@ -60,20 +72,13 @@
|
|
|
60
72
|
"e2e": {
|
|
61
73
|
"type": "boolean",
|
|
62
74
|
"default": false,
|
|
63
|
-
"description": "Requires the application stack to be up.
|
|
75
|
+
"description": "Requires the application stack to be up."
|
|
64
76
|
},
|
|
65
77
|
"linuxImage": {
|
|
66
78
|
"type": "string",
|
|
67
79
|
"minLength": 1,
|
|
68
80
|
"description": "Overrides the container image used by `verify --linux` for this node."
|
|
69
81
|
},
|
|
70
|
-
"groups": {
|
|
71
|
-
"type": "array",
|
|
72
|
-
"minItems": 1,
|
|
73
|
-
"uniqueItems": true,
|
|
74
|
-
"items": { "type": "string", "enum": ["backend", "frontend", "tests", "infra", "docs"] },
|
|
75
|
-
"description": "Which task group(s) (tasks.json `group`) this node verifies. Absent means the runner's own default applies (RUNNER_DEFAULT_GROUPS in src/lib/tasks/test-nodes.js) — `none`/`evals` have no default and are unscoped unless declared here."
|
|
76
|
-
},
|
|
77
82
|
"evals": {
|
|
78
83
|
"type": "object",
|
|
79
84
|
"additionalProperties": false,
|
|
@@ -191,7 +196,7 @@
|
|
|
191
196
|
"shutdownBuildServer": {
|
|
192
197
|
"type": "boolean",
|
|
193
198
|
"default": true,
|
|
194
|
-
"description": "Run `dotnet build-server shutdown` at the end of a
|
|
199
|
+
"description": "Run `dotnet build-server shutdown` at the end of a `verify` and of `e2e down`, only when no slot is held and no dotnet build/test is alive."
|
|
195
200
|
},
|
|
196
201
|
"disableNodeReuse": {
|
|
197
202
|
"type": "boolean",
|
|
@@ -38,17 +38,16 @@
|
|
|
38
38
|
"type": "object",
|
|
39
39
|
"description": "One entry per gate. Written ONLY by `morph-spec approve`/`advance` (approvedBy: \"auto\") or the state-sync hook on a human \"aprovado\" answer (approvedBy: \"manual\").",
|
|
40
40
|
"properties": {
|
|
41
|
-
"proposal": { "$ref": "#/definitions/gate" },
|
|
42
|
-
"uiux": { "$ref": "#/definitions/gate" },
|
|
43
41
|
"plan": { "$ref": "#/definitions/gate" },
|
|
42
|
+
"uiux": { "$ref": "#/definitions/gate" },
|
|
44
43
|
"review": { "$ref": "#/definitions/gate" }
|
|
45
44
|
},
|
|
46
45
|
"additionalProperties": false
|
|
47
46
|
},
|
|
48
47
|
"gateDecisions": {
|
|
49
48
|
"type": "object",
|
|
50
|
-
"description": "Trust decision persisted BEFORE acting on a gate, written ONLY by `morph-spec gate-decision` (decidedAt is server-side). Read by the gate-guard hook to verify the observed action matched the recorded decision.",
|
|
51
|
-
"propertyNames": { "enum": ["
|
|
49
|
+
"description": "Trust decision persisted BEFORE acting on a gate, written ONLY by `morph-spec gate-decision` and `morph-spec gate-check` (decidedAt is server-side). Read by the gate-guard hook to verify the observed action matched the recorded decision.",
|
|
50
|
+
"propertyNames": { "enum": ["plan", "uiux", "review"] },
|
|
52
51
|
"additionalProperties": {
|
|
53
52
|
"type": "object",
|
|
54
53
|
"required": ["pause", "reason", "signals", "trust", "decidedAt"],
|
|
@@ -57,48 +56,26 @@
|
|
|
57
56
|
"reason": { "type": "string", "description": "The signal that drove the decision (or 'nenhum sinal de risco')." },
|
|
58
57
|
"signals": {
|
|
59
58
|
"type": "object",
|
|
60
|
-
"description": "Risk signals evaluated (free-form):
|
|
59
|
+
"description": "Risk signals evaluated (free-form): sliceCount, planChecks, reviewReports, standardsViolations, designSystemDivergence..."
|
|
61
60
|
},
|
|
62
61
|
"trust": { "type": "string", "enum": ["auto", "manual"] },
|
|
63
62
|
"decidedAt": { "type": "string", "format": "date-time" },
|
|
64
63
|
"mockupsHash": {
|
|
65
64
|
"type": ["string", "null"],
|
|
66
65
|
"description": "uiux only: hash of the 1-design/mockups.md the decision saw (null = no mockup). A pause:false unlocks the auto signature only while the mockup is still this one."
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"additionalProperties": false
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
"taskVerifications": {
|
|
73
|
-
"type": "object",
|
|
74
|
-
"description": "Deterministic verify results per task, written ONLY by `morph-spec verify` (task scope). updatedAt is server-side; attempts auto-increment. Persisted only from the primary root or the feature's own registered worktree — see verify.js decidePersistence().",
|
|
75
|
-
"propertyNames": { "pattern": "^T\\d+$" },
|
|
76
|
-
"additionalProperties": {
|
|
77
|
-
"type": "object",
|
|
78
|
-
"required": ["status", "nodes", "attempts", "updatedAt"],
|
|
79
|
-
"properties": {
|
|
80
|
-
"status": { "type": "string", "enum": ["pass", "fail"] },
|
|
81
|
-
"nodes": {
|
|
82
|
-
"type": "object",
|
|
83
|
-
"required": ["build", "tests", "validators"],
|
|
84
|
-
"properties": {
|
|
85
|
-
"build": { "type": "string", "enum": ["pass", "fail", "skip"] },
|
|
86
|
-
"tests": { "type": "string", "enum": ["pass", "fail", "skip"] },
|
|
87
|
-
"validators": { "type": "string", "enum": ["pass", "fail", "skip"] },
|
|
88
|
-
"e2e": { "type": "string", "enum": ["pass", "fail", "skip"], "description": "Docker bring-up + Playwright specs against the running stack. Absent on entries written before the node existed — which is why it is NOT in `required`." },
|
|
89
|
-
"evals": { "type": ["string", "null"], "enum": ["pass", "fail", "skip", null], "description": "Eval suites, pulled out of the tests rollup. Written by the SAME function that writes the singular stamp (nodeStatuses in verify.js): the two shapes must stay identical, or a stamp would omit what the history records. null means the project declares no eval node at all — distinct from skip, which means it declares one and this run did not execute it. Absent on entries written before the field existed, which is why it is NOT in required." }
|
|
90
|
-
},
|
|
91
|
-
"additionalProperties": false
|
|
92
66
|
},
|
|
93
|
-
"
|
|
94
|
-
|
|
67
|
+
"source": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"enum": ["manual", "gate-check"],
|
|
70
|
+
"description": "Which command decided: `gate-decision` writes manual, `gate-check` writes gate-check. A manual pause:true is sticky: `gate-check` keeps it paused (manual reason, fresh signals); only a new `gate-decision --pause false` clears it."
|
|
71
|
+
}
|
|
95
72
|
},
|
|
96
73
|
"additionalProperties": false
|
|
97
74
|
}
|
|
98
75
|
},
|
|
99
76
|
"verifications": {
|
|
100
77
|
"type": "object",
|
|
101
|
-
"description": "Deterministic verify results at feature scope, written ONLY by `morph-spec verify
|
|
78
|
+
"description": "Deterministic verify results at feature scope, written ONLY by `morph-spec verify`.",
|
|
102
79
|
"properties": {
|
|
103
80
|
"feature": {
|
|
104
81
|
"type": "object",
|
|
@@ -127,8 +104,8 @@
|
|
|
127
104
|
},
|
|
128
105
|
"verification": {
|
|
129
106
|
"type": "object",
|
|
130
|
-
"description": "The single latest deterministic verify stamp
|
|
131
|
-
"required": ["status", "sha", "ts"
|
|
107
|
+
"description": "The single latest deterministic verify stamp, written ONLY by `morph-spec verify`. Distinct from `verifications` above (history with attempts/nodes): this is overwritten by every persisted verify, carrying just enough to correlate a pass/fail with the exact commit it covers. Primary consumer: `morph-spec gate-check`, which feeds `status` to shouldPauseGate as `verificationStatus` — a red verify pauses the gate (risk-detector.js) — and `nodes.e2e`/`e2eReason` as `e2eStatus`/`e2eReason`, the pair that decides whether an e2e SKIP is a recorded dispensation or an absence of proof (a skip never reddens `status`, so the rollup alone is blind to it). The rest (sha/ts, and the derived `verificationStale`) is audit context.",
|
|
108
|
+
"required": ["status", "sha", "ts"],
|
|
132
109
|
"properties": {
|
|
133
110
|
"status": { "type": "string", "enum": ["pass", "fail"] },
|
|
134
111
|
"nodes": {
|
|
@@ -142,31 +119,19 @@
|
|
|
142
119
|
"evals": {
|
|
143
120
|
"type": ["string", "null"],
|
|
144
121
|
"enum": ["pass", "fail", "skip", null],
|
|
145
|
-
"description": "Status of the `runner: \"evals\"` entries of nodes.tests.runs[], pulled out of the rollup. The rollup cannot answer what Gate 3 asks: a red eval and a red unit test both read as tests:fail
|
|
122
|
+
"description": "Status of the `runner: \"evals\"` entries of nodes.tests.runs[], pulled out of the rollup. The rollup cannot answer what Gate 3 asks: a red eval and a red unit test both read as tests:fail. null means this project declares NO eval node - distinct from 'skip', which means it declares one and this run did not execute it; risk-detector.js pauses on the two for different reasons because they call for different actions. Absent on stamps written before this field existed."
|
|
146
123
|
}
|
|
147
124
|
},
|
|
148
125
|
"additionalProperties": false
|
|
149
126
|
},
|
|
150
127
|
"e2eReason": { "type": ["string", "null"], "description": "The LITERAL reason the e2e node skipped (e2e-check.js's own wording), or null when it did not skip. risk-detector.js's classifyE2eSkip() reads it: 'project does not containerize' / 'does not touch runtime' are legitimate dispensations and pass; 'docker is not available' / 'requested' (--skip-e2e) / anything unclassifiable — null included — are an absence of proof and pause Gate 3. A skip recorded WITHOUT a reason is refused on purpose: a nameless dispensation is a silent no-op." },
|
|
151
128
|
"sha": { "type": ["string", "null"], "description": "HEAD commit SHA at verify time (git rev-parse HEAD); null when not in a git repo or no commits yet." },
|
|
152
|
-
"ts": { "type": "string", "format": "date-time", "description": "Server-side timestamp, same instant as the corresponding verifications
|
|
153
|
-
"scope": { "type": "string", "description": "'feature', or 'task:<id>' for a task-scoped verify. READ BY THE GATE, not just audit: this stamp is singular and any verify overwrites it, so a 'task:<id>' value at ship time means the last verify was NOT the feature's. risk-detector.js therefore accepts an e2e dispensation ('project'/'scope' in e2eReason) only from scope === 'feature' — a `morph-spec verify {f} {task}` of a task whose group does not touch runtime stamps exactly the legitimate-sounding reason that would otherwise buy Gate 3 for the whole feature (the last task of a feature is typically group: docs). Absent/unknown falls on the safe side, like an unclassifiable reason." },
|
|
129
|
+
"ts": { "type": "string", "format": "date-time", "description": "Server-side timestamp, same instant as the corresponding verifications entry's updatedAt." },
|
|
154
130
|
"source": { "type": "string", "enum": ["ci"], "description": "Present only when the stamp came from `verify --ci`: the verify --json the project CI uploaded for the pushed commit, judged by the same rules as a local run. Absent = local run." },
|
|
155
131
|
"ciRun": { "type": "string", "description": "URL of the CI run whose artifact wrote this stamp (with source: ci). `sha` is that run's headSha, so verificationStale compares the code CI saw with HEAD." }
|
|
156
132
|
},
|
|
157
133
|
"additionalProperties": false
|
|
158
134
|
},
|
|
159
|
-
"tasks": {
|
|
160
|
-
"type": "object",
|
|
161
|
-
"description": "Aggregate task counters (informational — the source of truth is 2-plan/tasks.json).",
|
|
162
|
-
"properties": {
|
|
163
|
-
"total": { "type": "integer", "minimum": 0 },
|
|
164
|
-
"completed": { "type": "integer", "minimum": 0 },
|
|
165
|
-
"inProgress": { "type": "integer", "minimum": 0 },
|
|
166
|
-
"pending": { "type": "integer", "minimum": 0 }
|
|
167
|
-
},
|
|
168
|
-
"additionalProperties": true
|
|
169
|
-
},
|
|
170
135
|
"outputs": {
|
|
171
136
|
"type": "object",
|
|
172
137
|
"description": "Outputs tracked by the state-sync hook: filename → { created, path }.",
|
|
@@ -234,7 +199,7 @@
|
|
|
234
199
|
"approved": { "type": "boolean" },
|
|
235
200
|
"timestamp": { "type": ["string", "null"], "description": "ISO timestamp of the approval/revocation, null while never evaluated." },
|
|
236
201
|
"approvedBy": { "type": ["string", "null"], "description": "\"auto\" (approve/advance --mode auto), \"manual\" (human in the loop), \"policy:{workType}\" (gate stamped at birth by `morph-spec create` per the work type's skippedGates — see src/core/work-types.js; the gate-guard hook only special-cases \"auto\", so policy-stamped gates stay silent to it), a --approver name, or null while never evaluated." },
|
|
237
|
-
"selfAssessed": { "type": "boolean", "description": "Gate `review` SOMENTE. `true` quando o Gate 3 foi aprovado sem `4-review/
|
|
202
|
+
"selfAssessed": { "type": "boolean", "description": "Gate `review` SOMENTE. `true` quando o Gate 3 foi aprovado sem os relatórios de review exigidos (`4-review/verifier.md`, `reviewer.md`, `security.md`), via `morph-spec approve <f> review --self-assessed` ou um \"aprovado\" humano sem eles. O veredito deveria vir dos agentes independentes; esta flag registra a degradação em vez de deixá-la silenciosa, e aparece no `morph-spec list`." }
|
|
238
203
|
},
|
|
239
204
|
"additionalProperties": true
|
|
240
205
|
}
|
|
@@ -12,10 +12,9 @@ These are the only skills the harness needs. MORPH.md references them by path.
|
|
|
12
12
|
|
|
13
13
|
| Skill | Purpose | User-invocable |
|
|
14
14
|
|----------------------|----------------------------------------------------------------------|----------------|
|
|
15
|
-
| `morph-
|
|
16
|
-
| `morph-
|
|
17
|
-
| `morph-
|
|
18
|
-
| `morph-implement` | Execute each task with standard + persona, auto-advance between tasks| no |
|
|
15
|
+
| `morph-plan` | Route, scan, interview, write plan.md, plan-check and the plan gate | yes |
|
|
16
|
+
| `morph-uiux` | Optional design branch after the plan gate (mockups + screen checklist) | yes |
|
|
17
|
+
| `morph-apply` | Run the plan slices (dispatch, report-check, verify) into the review | yes |
|
|
19
18
|
| `morph-review` | Final review report with SHIP/FIX verdict (Gate 3) | yes |
|
|
20
19
|
| `morph-standards` | Locate and load a specific standard on demand | no |
|
|
21
20
|
|
|
@@ -4,7 +4,7 @@ description: Guia a criação de uma landing page nova ponta-a-ponta a partir do
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando for iniciar um novo projeto de LP com o template Umbraco genérico
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "
|
|
7
|
+
cliVersion: "9.0.0"
|
|
8
8
|
stacks:
|
|
9
9
|
- umbraco
|
|
10
10
|
---
|
|
@@ -4,7 +4,7 @@ description: Migra uma landing page existente (React/Vite, Next.js estático ou
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando o usuário pedir "use /migrate-lp-to-umbraco {projeto}" ou descrever a necessidade de migrar uma LP para Umbraco
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "
|
|
7
|
+
cliVersion: "9.0.0"
|
|
8
8
|
stacks:
|
|
9
9
|
- umbraco
|
|
10
10
|
---
|