@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.
Files changed (222) hide show
  1. package/CLAUDE.md +48 -39
  2. package/README.md +3 -4
  3. package/bin/morph-spec.js +37 -51
  4. package/bin/validate.js +3 -111
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +75 -18
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-proposal.md +2 -50
  17. package/framework/commands/morph-status.md +3 -3
  18. package/framework/docs/CLI.md +585 -286
  19. package/framework/evals/review-checklist.md +82 -41
  20. package/framework/hooks/README.md +10 -11
  21. package/framework/hooks/claude-code/core/comment-guard.js +1 -1
  22. package/framework/hooks/claude-code/core/denylist.json +33 -3
  23. package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
  24. package/framework/hooks/claude-code/core/gate-guard.js +2 -11
  25. package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
  26. package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
  27. package/framework/hooks/claude-code/core/state-sync.js +7 -21
  28. package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
  29. package/framework/hooks/claude-code/core/validate-completion.js +33 -97
  30. package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
  31. package/framework/hooks/claude-code/core/worktree-context.js +5 -4
  32. package/framework/hooks/claude-code/statusline.py +55 -41
  33. package/framework/hooks/package.json +3 -0
  34. package/framework/hooks/shared/activity-logger.js +1 -1
  35. package/framework/hooks/shared/build-check.js +10 -6
  36. package/framework/hooks/shared/command-tokens.js +169 -0
  37. package/framework/hooks/shared/comment-rules.js +1 -1
  38. package/framework/hooks/shared/comment-scanner.js +10 -23
  39. package/framework/hooks/shared/comment-stripper.js +1 -1
  40. package/framework/hooks/shared/e2e-check.js +64 -27
  41. package/framework/hooks/shared/errata.js +72 -0
  42. package/framework/hooks/shared/feature-locator.js +1 -1
  43. package/framework/hooks/shared/frontend-locations.js +6 -0
  44. package/framework/hooks/shared/gate-question.js +18 -0
  45. package/framework/hooks/shared/phase-utils.js +15 -32
  46. package/framework/hooks/shared/slice-progress.js +118 -0
  47. package/framework/hooks/shared/spawn-command.js +23 -0
  48. package/framework/hooks/shared/state-reader.js +61 -87
  49. package/framework/hooks/shared/state-writer.js +2 -3
  50. package/framework/mcp/registry.json +3 -4
  51. package/framework/phases.json +23 -39
  52. package/framework/schemas/config.schema.json +14 -9
  53. package/framework/schemas/feature.schema.json +15 -50
  54. package/framework/skills/README.md +3 -4
  55. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  56. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  57. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  58. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  59. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  60. package/framework/skills/core/morph-media/SKILL.md +1 -1
  61. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  62. package/framework/skills/core/morph-review/SKILL.md +111 -136
  63. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  64. package/framework/skills/core/morph-uiux/SKILL.md +77 -152
  65. package/framework/standards/STANDARDS.json +86 -1218
  66. package/framework/standards/STANDARDS.meta.json +1305 -0
  67. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  68. package/framework/standards/ai-agents/setup.md +1 -1
  69. package/framework/standards/architecture/code-comments.md +6 -6
  70. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  71. package/framework/templates/README.md +2 -3
  72. package/framework/templates/REGISTRY.json +6 -170
  73. package/framework/templates/docs/plan.md +72 -0
  74. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  75. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  76. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  77. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  78. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  79. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  80. package/package.json +1 -1
  81. package/src/commands/e2e/e2e.js +2 -2
  82. package/src/commands/observability/cost.js +18 -60
  83. package/src/commands/project/doctor-procs.js +1 -1
  84. package/src/commands/project/doctor.js +9 -9
  85. package/src/commands/project/fleet.js +4 -4
  86. package/src/commands/project/heavy.js +3 -3
  87. package/src/commands/project/init.js +4 -0
  88. package/src/commands/project/setup-infra-cmd.js +2 -1
  89. package/src/commands/project/stack-scan.js +58 -0
  90. package/src/commands/project/standards.js +79 -25
  91. package/src/commands/project/status.js +29 -69
  92. package/src/commands/project/update.js +17 -1
  93. package/src/commands/project/worktree.js +1 -1
  94. package/src/commands/state/advance.js +62 -46
  95. package/src/commands/state/approve.js +81 -141
  96. package/src/commands/state/archive.js +20 -84
  97. package/src/commands/state/auto-approval.js +68 -0
  98. package/src/commands/state/create.js +28 -1
  99. package/src/commands/state/delete.js +1 -1
  100. package/src/commands/state/finish.js +11 -23
  101. package/src/commands/state/gate-check.js +85 -84
  102. package/src/commands/state/gate-decision.js +6 -4
  103. package/src/commands/state/handoff.js +1 -1
  104. package/src/commands/state/list.js +9 -9
  105. package/src/commands/state/report-check.js +157 -311
  106. package/src/commands/state/retype.js +9 -3
  107. package/src/commands/validation/mutate.js +77 -14
  108. package/src/commands/validation/plan-check.js +81 -0
  109. package/src/commands/validation/validate-feature.js +60 -135
  110. package/src/commands/validation/verify.js +40 -72
  111. package/src/core/paths/output-schema.js +19 -66
  112. package/src/core/state/gate-order.js +5 -6
  113. package/src/core/state/lifecycle-audit.js +2 -2
  114. package/src/core/state/state-manager.js +17 -98
  115. package/src/core/templates/template-registry.js +2 -2
  116. package/src/core/templates/template-seed.js +22 -0
  117. package/src/core/work-types.js +10 -10
  118. package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
  119. package/src/dashboard/app/components/CommandPalette.jsx +5 -5
  120. package/src/dashboard/app/components/EntityLink.jsx +1 -1
  121. package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
  122. package/src/dashboard/app/lib/phases.js +2 -4
  123. package/src/dashboard/app/pages/Activity.jsx +1 -1
  124. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
  125. package/src/dashboard/app/pages/Features.jsx +9 -9
  126. package/src/dashboard/app/pages/Guide.jsx +2 -2
  127. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
  128. package/src/dashboard/app/pages/Standards.jsx +1 -1
  129. package/src/dashboard/scanner/activity-scanner.js +5 -15
  130. package/src/dashboard/scanner/agents-scanner.js +1 -1
  131. package/src/dashboard/scanner/docs-scanner.js +2 -2
  132. package/src/dashboard/scanner/features-scanner.js +95 -60
  133. package/src/dashboard/scanner/project-api.js +1 -1
  134. package/src/dashboard/scanner/standards-scanner.js +2 -1
  135. package/src/lib/ci/ci-verify.js +17 -1
  136. package/src/lib/ci/github-ci.js +1 -1
  137. package/src/lib/detectors/index.js +3 -8
  138. package/src/lib/generators/handoff-generator.js +34 -65
  139. package/src/lib/generators/settings-generator.js +11 -89
  140. package/src/lib/git/evidence-staleness.js +1 -1
  141. package/src/lib/git/tree-fingerprint.js +19 -7
  142. package/src/lib/git/tree-state.js +2 -2
  143. package/src/lib/heavy/cleanup.js +1 -5
  144. package/src/lib/installers/memory-installer.js +6 -8
  145. package/src/lib/mutation/commands.js +3 -2
  146. package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
  147. package/src/lib/mutation/plan.js +14 -6
  148. package/src/lib/mutation/runner.js +1 -1
  149. package/src/lib/mutation/subset.js +46 -9
  150. package/src/lib/plan/artifact-vocabulary.js +60 -0
  151. package/src/lib/plan/checks.js +234 -0
  152. package/src/lib/plan/plan-parser.js +209 -0
  153. package/src/lib/plan/plan-template.js +18 -0
  154. package/src/lib/review-reports.js +222 -0
  155. package/src/lib/risk-detector.js +131 -140
  156. package/src/lib/specs/adr-index.js +50 -69
  157. package/src/lib/specs/design-seed.js +18 -0
  158. package/src/lib/specs/frontmatter.js +44 -0
  159. package/src/lib/specs/gfm-table.js +17 -0
  160. package/src/lib/specs/old-format.js +68 -0
  161. package/src/lib/specs/revocations.js +37 -95
  162. package/src/lib/specs/screen-checklist.js +18 -17
  163. package/src/lib/stack/dotnet-build-ignores.js +47 -0
  164. package/src/lib/stack/stack-scan.js +267 -0
  165. package/src/lib/standards/registry.js +43 -0
  166. package/src/lib/standards/sections.js +58 -0
  167. package/src/lib/tasks/eval-node.js +4 -12
  168. package/src/lib/tasks/test-nodes.js +1 -74
  169. package/src/lib/tasks/test-runner.js +75 -117
  170. package/src/lib/telemetry/metrics.js +8 -69
  171. package/src/lib/telemetry/telemetry-reader.js +5 -118
  172. package/src/lib/telemetry/verify-event.js +5 -7
  173. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  174. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
  175. package/src/lib/validators/reuse/reuse-validator.js +22 -45
  176. package/src/lib/validators/traceability-check.js +85 -129
  177. package/src/lib/validators/verify-runner.js +118 -235
  178. package/src/lib/worktree/docker-audit.js +1 -2
  179. package/src/lib/worktree/fleet-scanner.js +25 -38
  180. package/src/scripts/setup-infra.js +9 -0
  181. package/src/utils/agents-installer.js +76 -9
  182. package/src/utils/claude-settings-manager.js +0 -35
  183. package/src/utils/file-copier.js +27 -1
  184. package/src/utils/hooks-installer.js +16 -9
  185. package/src/utils/skills-installer.js +1 -2
  186. package/framework/evals/trace-schema.md +0 -67
  187. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  188. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  189. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  190. package/framework/hooks/shared/tasks-validator.js +0 -111
  191. package/framework/rules/morph-task-tracking.md +0 -54
  192. package/framework/rules/morph-workflow.md +0 -244
  193. package/framework/schemas/tasks.schema.json +0 -79
  194. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  195. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  196. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  197. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  198. package/framework/templates/docs/proposal.md +0 -192
  199. package/framework/templates/docs/spec.md +0 -57
  200. package/framework/templates/docs/ui-components.md +0 -52
  201. package/framework/templates/docs/ui-design-system.md +0 -76
  202. package/framework/templates/docs/ui-flows.md +0 -167
  203. package/framework/templates/examples/spec-examples.md +0 -91
  204. package/framework/templates/feature/decisions.md +0 -124
  205. package/framework/templates/feature/recap.md +0 -145
  206. package/framework/templates/tasks.template.json +0 -17
  207. package/src/commands/state/usecase.js +0 -143
  208. package/src/commands/tasks/dag.js +0 -181
  209. package/src/lib/agents/output-collector.js +0 -76
  210. package/src/lib/evaluator-report.js +0 -104
  211. package/src/lib/specs/acervo-format.js +0 -70
  212. package/src/lib/specs/acervo-lifecycle.js +0 -264
  213. package/src/lib/specs/done-criteria-check.js +0 -200
  214. package/src/lib/specs/usecase-check.js +0 -301
  215. package/src/lib/specs/usecase-promoter.js +0 -301
  216. package/src/lib/specs/usecase-registry.js +0 -237
  217. package/src/lib/tasks/dispatch-planner.js +0 -319
  218. package/src/lib/tasks/mermaid-renderer.js +0 -132
  219. package/src/lib/tasks/task-classifier.js +0 -30
  220. package/src/lib/tasks/task-parser.js +0 -209
  221. package/src/lib/tasks/tasks-json-parser.js +0 -296
  222. package/src/lib/tasks/test-scope.js +0 -597
@@ -8,6 +8,7 @@
8
8
  import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync, rmdirSync, statSync, readdirSync } from 'fs';
9
9
  import { join, dirname } from 'path';
10
10
  import { getAllOutputPaths, getOutputPath } from '../paths/output-schema.js';
11
+ import { derivePhaseFrom, diskArtifactProbe } from '../../../framework/hooks/shared/state-reader.js';
11
12
 
12
13
  const STATE_FILE_NAME = '.morph/state.json';
13
14
  const FEATURES_DIR_NAME = '.morph/features';
@@ -156,56 +157,19 @@ function scaffoldState() {
156
157
  };
157
158
  }
158
159
  /**
159
- * Derive current phase from filesystem.
160
- *
161
- * Walks the phase map in descending order and returns the first phase whose
162
- * CANONICAL ARTIFACT exists. An empty folder is not enough — `mkdir 2-plan/`
163
- * does not count as "in plan phase" until `spec.md` (the first canonical
164
- * plan-phase output) lands. If no canonical artifact is found anywhere,
165
- * returns `'proposal'` (the starting phase).
166
- *
167
- * This avoids the chicken-and-egg problem where the agent pre-creates all
168
- * phase folders, the derived phase jumps to the highest one, and
169
- * `enforce-phase-writes` then blocks the agent from writing back to the
170
- * prior phase folder.
160
+ * Derive current phase from filesystem — delegates to the hook-side rule
161
+ * (`derivePhaseFrom`) so the CLI and the hooks never disagree on a feature.
171
162
  *
172
163
  * @param {string} featurePath - Absolute path to .morph/features/{feature}/
173
- * @returns {string} Phase name: 'review' | 'implement' | 'plan' | 'uiux' | 'proposal'
164
+ * @returns {string} Phase name: 'review' | 'implement' | 'uiux' | 'plan'
174
165
  */
175
- const PHASE_INDEX = { proposal: 0, uiux: 1, plan: 2, implement: 3, review: 4 };
176
-
177
- /** Phase implied purely by approved gates (mirrors hook-side gateImpliedPhase). */
178
- function gateImpliedPhaseFromGates(gates) {
179
- const ok = (g) => gates?.[g]?.approved === true;
180
- if (ok('review')) return 'review';
181
- if (ok('plan')) return 'implement';
182
- if (ok('uiux')) return 'plan';
183
- return 'proposal';
184
- }
185
-
186
166
  export function derivePhase(featurePath) {
187
- const phaseMap = [
188
- ['4-review', 'review', 'review-report.md'],
189
- ['3-implement', 'implement', 'recap.md'],
190
- ['2-plan', 'plan', 'spec.md'],
191
- ['1-design', 'uiux', 'design-system.md'],
192
- ['0-proposal', 'proposal', 'proposal.md'],
193
- ];
194
- let artifactPhase = 'proposal';
195
- for (const [folder, phase, canonical] of phaseMap) {
196
- if (existsSync(join(featurePath, folder, canonical))) { artifactPhase = phase; break; }
197
- }
198
-
199
- // Gate-aware (same rule as the hook-side derivePhaseForFeature): an approved
200
- // gate advances the phase before the next phase's canonical artifact exists,
201
- // so the agent can actually write that first artifact.
202
- let gates = null;
167
+ let feature = null;
203
168
  try {
204
169
  const fjPath = join(featurePath, 'feature.json');
205
- if (existsSync(fjPath)) gates = JSON.parse(readFileSync(fjPath, 'utf8'))?.approvalGates;
206
- } catch { gates = null; }
207
- const implied = gateImpliedPhaseFromGates(gates);
208
- return PHASE_INDEX[implied] > PHASE_INDEX[artifactPhase] ? implied : artifactPhase;
170
+ if (existsSync(fjPath)) feature = JSON.parse(readFileSync(fjPath, 'utf8'));
171
+ } catch { feature = null; }
172
+ return derivePhaseFrom(feature, diskArtifactProbe(featurePath));
209
173
  }
210
174
 
211
175
  /**
@@ -498,7 +462,7 @@ export function getFeature(featureName) {
498
462
  /**
499
463
  * Create or get feature with default structure.
500
464
  * v5: every feature follows the fixed phase sequence
501
- * (proposal → uiux → plan → implement → review). No workflow selection.
465
+ * (plan → [uiux] → implement → review). No workflow selection.
502
466
  * @param {string} featureName - Feature name
503
467
  * @returns {Promise<Object>} Feature object
504
468
  */
@@ -511,17 +475,10 @@ async function ensureFeature(featureName) {
511
475
  createdAt: new Date().toISOString(),
512
476
  updatedAt: new Date().toISOString(),
513
477
  approvalGates: {
514
- proposal: { approved: false, timestamp: null, approvedBy: null },
515
- uiux: { approved: false, timestamp: null, approvedBy: null },
516
478
  plan: { approved: false, timestamp: null, approvedBy: null },
479
+ uiux: { approved: false, timestamp: null, approvedBy: null },
517
480
  review: { approved: false, timestamp: null, approvedBy: null }
518
481
  },
519
- tasks: {
520
- total: 0,
521
- completed: 0,
522
- inProgress: 0,
523
- pending: 0
524
- },
525
482
  checkpoints: [],
526
483
  trustConfig: {},
527
484
  contextBundles: [],
@@ -575,18 +532,11 @@ export async function createFeature(featureName, options = {}) {
575
532
  createdAt: now,
576
533
  updatedAt: now,
577
534
  approvalGates: {
578
- proposal: { approved: false, timestamp: null, approvedBy: null },
579
- uiux: { approved: false, timestamp: null, approvedBy: null },
580
535
  plan: { approved: false, timestamp: null, approvedBy: null },
536
+ uiux: { approved: false, timestamp: null, approvedBy: null },
581
537
  review: { approved: false, timestamp: null, approvedBy: null }
582
538
  },
583
539
  gateDecisions: {},
584
- tasks: {
585
- total: 0,
586
- completed: 0,
587
- inProgress: 0,
588
- pending: 0
589
- },
590
540
  checkpoints: [],
591
541
  trustConfig: {},
592
542
  contextBundles: [],
@@ -603,7 +553,7 @@ export async function createFeature(featureName, options = {}) {
603
553
  }
604
554
 
605
555
  /**
606
- * Update feature property (supports nested keys like "tasks.completed")
556
+ * Update feature property (supports nested keys like "approvalGates.plan.approved")
607
557
  * @param {string} featureName - Feature name
608
558
  * @param {string} key - Property key (supports dot notation)
609
559
  * @param {any} value - Value to set
@@ -657,36 +607,6 @@ export async function updateFeatureMultiple(featureName, updates) {
657
607
  saveState(state);
658
608
  }
659
609
 
660
- /**
661
- * Add checkpoint to feature
662
- * @param {string} featureName - Feature name
663
- * @param {string} note - Checkpoint note
664
- * @returns {Object} Checkpoint object
665
- */
666
- export async function addCheckpoint(featureName, note) {
667
- await ensureFeature(featureName);
668
- const state = loadState();
669
- const feature = state.features[featureName];
670
-
671
- const checkpoint = {
672
- passed: true,
673
- checkpointNum: feature.checkpoints.length + 1,
674
- timestamp: new Date().toISOString(),
675
- phase: feature.phase || 'proposal',
676
- results: [],
677
- summary: {
678
- note: note,
679
- completedTasks: feature.tasks.completed
680
- }
681
- };
682
-
683
- feature.checkpoints.push(checkpoint);
684
- feature.updatedAt = new Date().toISOString();
685
-
686
- saveState(state);
687
- return checkpoint;
688
- }
689
-
690
610
  /**
691
611
  * Normalize output type from kebab-case to camelCase (BUG-002 fix - Enhanced)
692
612
  * Supports both kebab-case and camelCase input formats for all output types
@@ -781,7 +701,7 @@ function findClosestMatch(inputType, validTypes) {
781
701
  /**
782
702
  * Mark output as created
783
703
  * @param {string} featureName - Feature name
784
- * @param {string} outputType - Output type (proposal, spec, tasks, etc.)
704
+ * @param {string} outputType - Output type (plan, checks, verifierReport, etc.)
785
705
  */
786
706
  export async function markOutput(featureName, outputType) {
787
707
  await ensureFeature(featureName);
@@ -860,7 +780,7 @@ export async function trackFileChange(featureName, filePath, action, phase) {
860
780
  state.features[featureName].fileChanges.push({
861
781
  path: filePath,
862
782
  action,
863
- phase: phase || state.features[featureName].phase || 'proposal',
783
+ phase: phase || state.features[featureName].phase || 'plan',
864
784
  timestamp: new Date().toISOString()
865
785
  });
866
786
 
@@ -968,7 +888,7 @@ export function getSummary() {
968
888
  /**
969
889
  * Set approval gate status
970
890
  * @param {string} featureName - Feature name
971
- * @param {string} gate - Gate name (proposal, uiux, plan, review)
891
+ * @param {string} gate - Gate name (plan, uiux, review)
972
892
  * @param {boolean} approved - Approval status
973
893
  * @param {Object} metadata - Additional metadata (approvedBy, reason, etc.)
974
894
  */
@@ -978,9 +898,8 @@ export async function setApprovalGate(featureName, gate, approved, metadata = {}
978
898
 
979
899
  if (!feature.approvalGates) {
980
900
  feature.approvalGates = {
981
- proposal: { approved: false, timestamp: null, approvedBy: null },
982
- uiux: { approved: false, timestamp: null, approvedBy: null },
983
901
  plan: { approved: false, timestamp: null, approvedBy: null },
902
+ uiux: { approved: false, timestamp: null, approvedBy: null },
984
903
  review: { approved: false, timestamp: null, approvedBy: null }
985
904
  };
986
905
  }
@@ -1026,7 +945,7 @@ export function isPendingApproval(featureName) {
1026
945
  return false;
1027
946
  }
1028
947
 
1029
- const currentPhase = feature.phase || 'proposal';
948
+ const currentPhase = feature.phase || 'plan';
1030
949
 
1031
950
  // Check if current phase has an approval gate
1032
951
  const phaseGateMap = {
@@ -76,7 +76,7 @@ export function getAllTemplates(projectPath = null) {
76
76
  /**
77
77
  * Get template by ID.
78
78
  *
79
- * @param {string} templateId - Template identifier (e.g., 'proposal', 'spec')
79
+ * @param {string} templateId - Template identifier (e.g., 'plan')
80
80
  * @param {string} [projectPath] - Optional project path
81
81
  * @returns {Object|null} Template object or null if not found
82
82
  */
@@ -100,7 +100,7 @@ export function getTemplatesByCategory(category, projectPath = null) {
100
100
  /**
101
101
  * Get templates by phase.
102
102
  *
103
- * @param {string} phase - Phase name (e.g., 'proposal', 'design', 'implement')
103
+ * @param {string} phase - Phase name (e.g., 'plan', 'uiux', 'implement')
104
104
  * @param {string} [projectPath] - Optional project path
105
105
  * @returns {Array} Filtered template array
106
106
  */
@@ -0,0 +1,22 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { getTemplateById, resolveFrameworkTemplatePath } from './template-registry.js';
4
+
5
+ /** The project-local override (same relative path as in the package) wins over the package copy. */
6
+ export function templateSourcePath(templateId, cwd = process.cwd()) {
7
+ const template = getTemplateById(templateId);
8
+ const local = template ? join(cwd, '.morph', 'framework', 'templates', template.path) : null;
9
+ return local && existsSync(local) ? local : resolveFrameworkTemplatePath(templateId);
10
+ }
11
+
12
+ /** @returns {{ path: string, written: boolean }} `written: false` when the file already existed. */
13
+ export function writeIfAbsent(path, content) {
14
+ mkdirSync(dirname(path), { recursive: true });
15
+ try {
16
+ writeFileSync(path, content, { encoding: 'utf8', flag: 'wx' });
17
+ return { path, written: true };
18
+ } catch (err) {
19
+ if (err.code === 'EEXIST') return { path, written: false };
20
+ throw err;
21
+ }
22
+ }
@@ -13,7 +13,7 @@
13
13
  * Fields per type:
14
14
  * label - Human-readable name
15
15
  * scaffold - Phase folders created at feature birth (subset of
16
- * 0-proposal/1-design/2-plan/3-implement/4-review)
16
+ * 2-plan/1-design/3-implement/4-review)
17
17
  * skippedGates - Gates stamped `policy:{type}`-approved at birth
18
18
  * instead of waiting for human approval
19
19
  * forcedPauseGates - Gates that ALWAYS pause for a human, regardless of
@@ -27,29 +27,29 @@ export const POLICY_APPROVER_PREFIX = 'policy:';
27
27
  export const WORK_TYPES = {
28
28
  feature: {
29
29
  label: 'Feature',
30
- scaffold: ['0-proposal'],
30
+ scaffold: ['2-plan'],
31
31
  skippedGates: [],
32
32
  forcedPauseGates: [],
33
- entrySkill: 'morph-proposal',
33
+ entrySkill: 'morph-plan',
34
34
  },
35
35
  bug: {
36
36
  label: 'Bug fix',
37
- scaffold: ['0-proposal'],
37
+ scaffold: ['2-plan'],
38
38
  skippedGates: [],
39
39
  forcedPauseGates: [],
40
- entrySkill: 'morph-proposal',
40
+ entrySkill: 'morph-plan',
41
41
  },
42
42
  chore: {
43
43
  label: 'Chore',
44
- scaffold: ['0-proposal', '2-plan', '3-implement'],
45
- skippedGates: ['proposal', 'plan'],
44
+ scaffold: ['2-plan', '3-implement'],
45
+ skippedGates: ['plan'],
46
46
  forcedPauseGates: [],
47
- entrySkill: 'morph-proposal',
47
+ entrySkill: 'morph-plan',
48
48
  },
49
49
  hotfix: {
50
50
  label: 'Hotfix',
51
- scaffold: ['0-proposal', '2-plan', '3-implement'],
52
- skippedGates: ['proposal', 'plan'],
51
+ scaffold: ['2-plan', '3-implement'],
52
+ skippedGates: ['plan'],
53
53
  forcedPauseGates: ['review'],
54
54
  entrySkill: 'morph-hotfix',
55
55
  },
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
3
  import {
4
- ChatCircleText, Question, BookOpenText, PaperPlaneTilt, ArrowBendDownLeft, CheckSquare, ShieldCheck, Flag, WarningOctagon,
4
+ ChatCircleText, Question, BookOpenText, PaperPlaneTilt, ArrowBendDownLeft, ShieldCheck, Flag, WarningOctagon,
5
5
  PencilSimpleLine, Eye, TerminalWindow, Wrench,
6
6
  } from '@phosphor-icons/react';
7
7
  import { useMorphQuery } from '../hooks/useMorphQuery.js';
@@ -16,7 +16,6 @@ export const KIND_META = {
16
16
  skill: { label: 'Skills', icon: BookOpenText },
17
17
  dispatch: { label: 'Despachos', icon: PaperPlaneTilt },
18
18
  handback: { label: 'Retornos', icon: ArrowBendDownLeft },
19
- task: { label: 'Tasks', icon: CheckSquare },
20
19
  verify: { label: 'Verify', icon: ShieldCheck },
21
20
  gate: { label: 'Gates', icon: Flag },
22
21
  failure: { label: 'Falhas', icon: WarningOctagon },
@@ -25,7 +24,7 @@ export const KIND_META = {
25
24
  command: { label: 'Comandos', icon: TerminalWindow },
26
25
  tool: { label: 'Outras tools', icon: Wrench },
27
26
  };
28
- const DEFAULT = ['prompt', 'question', 'skill', 'dispatch', 'handback', 'task', 'verify', 'gate', 'failure'];
27
+ const DEFAULT = ['prompt', 'question', 'skill', 'dispatch', 'handback', 'verify', 'gate', 'failure'];
29
28
 
30
29
  const NODE_TONE = { pass: 'text-[var(--color-ok)]', fail: 'text-[var(--color-bad)]', skip: 'text-[var(--color-faint)]' };
31
30
 
@@ -8,7 +8,7 @@ import { useMorphQuery } from '../hooks/useMorphQuery.js';
8
8
  import { apiBase, projectHref, featureHref } from '../lib/project.js';
9
9
 
10
10
  const KIND_LABEL = {
11
- page: 'Página', project: 'Projeto', feature: 'Feature', task: 'Task', skill: 'Skill', persona: 'Persona',
11
+ page: 'Página', project: 'Projeto', feature: 'Feature', slice: 'Fatia', skill: 'Skill', persona: 'Persona',
12
12
  standard: 'Standard', rule: 'Rule', hook: 'Hook', guide: 'Guia',
13
13
  };
14
14
 
@@ -43,7 +43,7 @@ function projectIndex(portfolio) {
43
43
  }
44
44
 
45
45
  /**
46
- * Features and their tasks. `projectName` is set for another project's
46
+ * Features and their slices. `projectName` is set for another project's
47
47
  * features, so a hit says where it lives.
48
48
  */
49
49
  function featureIndex(project, features, projectName = null) {
@@ -51,8 +51,8 @@ function featureIndex(project, features, projectName = null) {
51
51
  const where = (text) => (projectName ? (text ? `${projectName}: ${text}` : projectName) : text);
52
52
  for (const f of features || []) {
53
53
  out.push({ kind: 'feature', title: f.name, hint: where(f.description), to: featureHref(project, f.name), hay: `${f.name} ${f.description || ''} ${projectName || ''}` });
54
- for (const t of f.taskIndex || []) {
55
- out.push({ kind: 'task', title: `${t.id} ${t.title}`, hint: where(f.name), to: featureHref(project, f.name, '?tab=tasks'), hay: `${t.id} ${t.title} ${f.name} ${projectName || ''}` });
54
+ for (const sl of f.sliceIndex || []) {
55
+ out.push({ kind: 'slice', title: `${sl.id} ${sl.title}`, hint: where(f.name), to: featureHref(project, f.name, '?tab=slices'), hay: `${sl.id} ${sl.title} ${f.name} ${projectName || ''}` });
56
56
  }
57
57
  }
58
58
  return out;
@@ -119,7 +119,7 @@ export default function CommandPalette({ open, onClose, portfolio, project }) {
119
119
  value={query}
120
120
  onChange={(e) => setQuery(e.target.value)}
121
121
  onKeyDown={onKey}
122
- placeholder="Buscar features, tasks, skills, personas, standards, seções do guia…"
122
+ placeholder="Buscar features, fatias, skills, personas, standards, seções do guia…"
123
123
  className="w-full bg-transparent text-base text-[var(--color-fg)] outline-none focus-visible:outline-none placeholder:text-[var(--color-faint)]"
124
124
  aria-controls="palette-results"
125
125
  aria-activedescendant={results[active] ? `palette-${active}` : undefined}
@@ -20,7 +20,7 @@ export function entityHref(kind, id) {
20
20
  }
21
21
  }
22
22
 
23
- /** `standards/backend/dotnet/testing.md` (as tasks.json cites it) → the registry `path`. */
23
+ // `standards/backend/dotnet/testing.md` (as a plan may cite it) → the registry `path`.
24
24
  export function standardKey(ref) {
25
25
  return String(ref).replace(/^\.?\/?(\.morph\/framework\/)?standards\//, '');
26
26
  }
@@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react';
5
5
  * reports that the project's `.morph/` changed in the given scope.
6
6
  *
7
7
  * `scope` matches the server's invalidation: 'features' (feature.json,
8
- * tasks.json, artifacts) or 'activity' (events.jsonl). A refetch keeps the old
8
+ * slice reports, artifacts) or 'activity' (events.jsonl). A refetch keeps the old
9
9
  * data on screen until the new one arrives — no flash of "loading" while an
10
10
  * agent is working.
11
11
  *
@@ -6,9 +6,8 @@
6
6
  import morphData from 'virtual:morph-data';
7
7
 
8
8
  const LABELS = {
9
- proposal: 'Proposta',
10
- uiux: 'UI/UX',
11
9
  plan: 'Plano',
10
+ uiux: 'UI/UX',
12
11
  implement: 'Implementação',
13
12
  review: 'Revisão',
14
13
  };
@@ -29,9 +28,8 @@ export function phaseMeta(id) {
29
28
  }
30
29
 
31
30
  export const GATE_LABEL = {
32
- proposal: 'Gate 1 (proposta)',
31
+ plan: 'Gate do plano',
33
32
  uiux: 'Gate UI/UX',
34
- plan: 'Gate 2 (plano)',
35
33
  review: 'Gate 3 (revisão)',
36
34
  };
37
35
 
@@ -13,7 +13,7 @@ export default function Activity() {
13
13
  <h1 className="mt-1 text-4xl font-semibold tracking-tight">Atividade</h1>
14
14
  <p className="mt-3 max-w-2xl text-sm leading-relaxed text-[var(--color-dim)]">
15
15
  O que o Claude fez no projeto, contado a partir da telemetria dos hooks: suas mensagens, as skills carregadas,
16
- os sub-agents despachados, as tasks, os verifies e os gates. Atualiza sozinho enquanto ele trabalha.
16
+ os sub-agents despachados, os verifies e os gates. Atualiza sozinho enquanto ele trabalha.
17
17
  </p>
18
18
  </header>
19
19
  <div className="rise-2 bezel">