@polymorphism-tech/morph-spec 8.44.0 → 8.45.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 +10 -9
- package/README.md +10 -10
- package/bin/morph-spec.js +10 -13
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -4
- package/docs/QUICKSTART.md +6 -6
- package/framework/CLAUDE.md +6 -8
- package/framework/MORPH.md +39 -45
- package/framework/agents.json +8 -9
- package/framework/commands/morph-apply.md +1 -3
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/docs/CLI.md +10 -10
- package/framework/evals/review-checklist.md +90 -0
- package/framework/evals/trace-schema.md +2 -12
- package/framework/hooks/README.md +2 -3
- package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
- package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
- package/framework/hooks/claude-code/core/validate-completion.js +3 -56
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/build-check.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +1 -1
- package/framework/hooks/shared/phase-utils.js +0 -5
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/phases.json +4 -6
- package/framework/rubrics/backend.json +1 -1
- package/framework/rubrics/default.json +1 -1
- package/framework/rubrics/frontend.json +1 -1
- package/framework/rules/morph-workflow.md +6 -6
- package/framework/schemas/feature.schema.json +2 -28
- package/framework/schemas/tasks.schema.json +6 -2
- package/framework/skills/README.md +1 -2
- 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 +145 -246
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
- package/framework/skills/core/morph-implement/SKILL.md +101 -201
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +171 -287
- package/framework/skills/core/morph-proposal/SKILL.md +4 -5
- package/framework/skills/core/morph-review/SKILL.md +109 -430
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +2 -2
- package/framework/skills/core/morph-uiux/SKILL.md +2 -3
- package/framework/standards/STANDARDS.json +10 -10
- package/framework/standards/ai-agents/agent-archetypes.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/agent-spec.md +1 -1
- package/framework/standards/ai-agents/batching.md +1 -1
- package/framework/standards/ai-agents/context-providers.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
- package/framework/standards/ai-agents/cost-and-budget.md +1 -1
- package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/mcp-server.md +1 -1
- package/framework/standards/ai-agents/mcp-tools.md +1 -1
- package/framework/standards/ai-agents/media-pipeline.md +1 -1
- package/framework/standards/ai-agents/media-video.md +1 -1
- package/framework/standards/ai-agents/middleware-patterns.md +1 -1
- package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
- package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
- package/framework/standards/ai-agents/modalities-vision.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
- package/framework/standards/ai-agents/observability-patterns.md +3 -3
- package/framework/standards/ai-agents/production.md +2 -2
- package/framework/standards/ai-agents/prompt-sources.md +1 -1
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
- package/framework/standards/ai-agents/service-tier-flex.md +1 -1
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/ai-agents/testing-ai.md +1 -1
- package/framework/standards/ai-agents/tool-adapters.md +1 -1
- package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/README.md +1 -1
- package/framework/templates/REGISTRY.json +0 -81
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/spec.md +19 -205
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +2 -2
- package/src/commands/e2e/e2e.js +1 -1
- package/src/commands/project/heavy.js +1 -1
- package/src/commands/project/status.js +3 -3
- package/src/commands/state/approve.js +7 -49
- package/src/commands/state/gate-check.js +16 -106
- package/src/commands/state/gate-decision.js +1 -1
- package/src/commands/state/report-check.js +42 -17
- package/src/commands/validation/mutate.js +1 -1
- package/src/commands/validation/scout-check.js +28 -0
- package/src/commands/validation/validate-feature.js +5 -32
- package/src/commands/validation/verify.js +3 -4
- package/src/core/paths/output-schema.js +0 -2
- package/src/core/state/state-manager.js +1 -2
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/lib/format.js +5 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
- package/src/dashboard/app/pages/Features.jsx +7 -7
- package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
- package/src/dashboard/app/pages/Rubrics.jsx +7 -11
- package/src/dashboard/scanner/docs-scanner.js +1 -2
- package/src/dashboard/scanner/features-scanner.js +3 -4
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/output-collector.js +0 -1
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/evaluator-report.js +52 -334
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/handoff-generator.js +4 -5
- package/src/lib/installers/memory-installer.js +2 -2
- package/src/lib/mutation/manifest.js +8 -2
- package/src/lib/mutation/report.js +5 -3
- package/src/lib/mutation/runner.js +9 -7
- package/src/lib/risk-detector.js +60 -122
- package/src/lib/scout/scout-check.js +293 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +7 -5
- package/src/lib/tasks/test-nodes.js +1 -1
- package/src/lib/tasks/test-runner.js +1 -1
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/reuse-validator.js +6 -8
- package/src/lib/validators/verify-runner.js +1 -1
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +5 -9
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/hooks-installer.js +3 -10
- package/src/utils/skills-installer.js +1 -1
- package/framework/evals/proof-rubrics/README.md +0 -76
- package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
- package/framework/evals/proof-rubrics/fiacao.md +0 -54
- package/framework/evals/proof-rubrics/guard-cego.md +0 -52
- package/framework/evals/proof-rubrics/media-claims.md +0 -109
- package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
- package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
- package/framework/evals/rubrics/README.md +0 -45
- package/framework/evals/rubrics/architecture.md +0 -103
- package/framework/evals/rubrics/code-quality.md +0 -98
- package/framework/evals/rubrics/condensed.md +0 -57
- package/framework/evals/rubrics/contracts.md +0 -67
- package/framework/evals/rubrics/test-coverage.md +0 -99
- package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
- package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
- package/framework/evals/suites/standard.yaml +0 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
- package/framework/skills/core/morph-eval/SKILL.md +0 -64
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/framework/templates/docs/clarifications.md +0 -253
- package/framework/templates/docs/onboarding.md +0 -122
- package/framework/templates/docs/schema-analysis.md +0 -119
- package/framework/templates/docs/user-stories.md +0 -34
- package/src/commands/state/score.js +0 -147
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { StrictMode } from 'react';
|
|
2
|
-
import { createRoot } from 'react-dom/client';
|
|
3
|
-
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
-
import App from './App.jsx';
|
|
5
|
-
import './styles/index.css';
|
|
6
|
-
|
|
7
|
-
createRoot(document.getElementById('root')).render(
|
|
8
|
-
<StrictMode>
|
|
9
|
-
<BrowserRouter>
|
|
10
|
-
<App />
|
|
11
|
-
</BrowserRouter>
|
|
12
|
-
</StrictMode>
|
|
13
|
-
);
|
|
1
|
+
import { StrictMode } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
+
import App from './App.jsx';
|
|
5
|
+
import './styles/index.css';
|
|
6
|
+
|
|
7
|
+
createRoot(document.getElementById('root')).render(
|
|
8
|
+
<StrictMode>
|
|
9
|
+
<BrowserRouter>
|
|
10
|
+
<App />
|
|
11
|
+
</BrowserRouter>
|
|
12
|
+
</StrictMode>
|
|
13
|
+
);
|
|
@@ -13,7 +13,7 @@ import { usePref, useNow } from '../hooks/usePref.js';
|
|
|
13
13
|
import { Segmented, isWorking } from './Features.jsx';
|
|
14
14
|
import { apiBase, projectHref } from '../lib/project.js';
|
|
15
15
|
import { PHASES, GATE_LABEL, STATUS_LABEL, LOCATION_LABEL, phaseMeta } from '../lib/phases.js';
|
|
16
|
-
import { timeAgo, dateTime, num, duration, bytes } from '../lib/format.js';
|
|
16
|
+
import { timeAgo, dateTime, num, duration, bytes, verdictLabel } from '../lib/format.js';
|
|
17
17
|
|
|
18
18
|
const TABS = [
|
|
19
19
|
{ id: 'overview', label: 'Visão geral' },
|
|
@@ -77,7 +77,7 @@ export function nextAction(f) {
|
|
|
77
77
|
if (f.status === 'abandoned') return 'Abandonada.';
|
|
78
78
|
const paused = f.gates.find((g) => g.paused);
|
|
79
79
|
if (paused) return `O ${GATE_LABEL[paused.gate] || paused.gate} pausou${paused.reason ? `: ${paused.reason}` : ''}. A decisão é sua, no Claude Code.`;
|
|
80
|
-
if (f.inEvaluation) return 'O avaliador independente entregou o relatório. O Gate 3 decide pelo
|
|
80
|
+
if (f.inEvaluation) return 'O avaliador independente entregou o relatório. O Gate 3 decide pelo veredito e pelos bloqueadores.';
|
|
81
81
|
if (f.phase === 'implement' && f.tasksList?.length) {
|
|
82
82
|
const open = f.tasksList.find((t) => t.status === 'in_progress') || f.tasksList.find((t) => t.status === 'pending');
|
|
83
83
|
if (open) return `${f.tasks.done} de ${f.tasks.total} tasks feitas. Próxima: ${open.id}, ${open.title}.`;
|
|
@@ -149,24 +149,20 @@ function NodeChips({ nodes }) {
|
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function
|
|
153
|
-
const score = f.score ?? f.legacyScore;
|
|
154
|
-
const proofs = f.proofScores ? Object.entries(f.proofScores) : [];
|
|
152
|
+
function VerdictBlock({ f }) {
|
|
155
153
|
return (
|
|
156
154
|
<div>
|
|
157
155
|
<p className="flex items-baseline gap-2">
|
|
158
|
-
<span className="text-4xl font-semibold
|
|
159
|
-
<span className="text-sm text-[var(--color-dim)]">{
|
|
156
|
+
<span className="text-4xl font-semibold tracking-tight">{f.verdict ?? '—'}</span>
|
|
157
|
+
<span className="text-sm text-[var(--color-dim)]">{f.verdict ? `${f.blockers ?? 0} bloqueador(es)` : 'sem veredito do avaliador'}</span>
|
|
160
158
|
</p>
|
|
161
|
-
{
|
|
162
|
-
<ul className="mt-4 space-y-1.5">
|
|
163
|
-
{
|
|
164
|
-
<li key={
|
|
165
|
-
<span className="text-[var(--color-dim)]">{
|
|
166
|
-
<span className="
|
|
167
|
-
|
|
168
|
-
</span>
|
|
169
|
-
<span className="text-right font-mono tabular-nums text-[var(--color-dim)]">{v}</span>
|
|
159
|
+
{f.blockerList?.length > 0 && (
|
|
160
|
+
<ul className="mt-4 space-y-1.5 text-xs">
|
|
161
|
+
{f.blockerList.map((b) => (
|
|
162
|
+
<li key={b.id} className="grid grid-cols-[3rem_6rem_minmax(0,1fr)] gap-3">
|
|
163
|
+
<span className="font-mono text-[var(--color-dim)]">{b.id}</span>
|
|
164
|
+
<span className="text-[var(--color-warn)]">{b.type}</span>
|
|
165
|
+
<span className="truncate text-[var(--color-dim)]" title={b.evidence}>{b.where}</span>
|
|
170
166
|
</li>
|
|
171
167
|
))}
|
|
172
168
|
</ul>
|
|
@@ -212,7 +208,7 @@ function OverviewTab({ f, cost }) {
|
|
|
212
208
|
<Panel title="Gates"><GateList f={f} /></Panel>
|
|
213
209
|
</div>
|
|
214
210
|
<div className="space-y-5">
|
|
215
|
-
<Panel title="Avaliação"><
|
|
211
|
+
<Panel title="Avaliação"><VerdictBlock f={f} /></Panel>
|
|
216
212
|
<Panel title="Último verify">
|
|
217
213
|
{f.verification && (
|
|
218
214
|
<p className="mb-3 text-xs text-[var(--color-faint)]">
|
|
@@ -256,7 +252,7 @@ function TaskRow({ t, f, open, onToggle, duration: secs, ready }) {
|
|
|
256
252
|
<span className="hidden text-xs md:block">
|
|
257
253
|
{verify ? <span className={verify.status === 'pass' ? 'text-[var(--color-ok)]' : 'text-[var(--color-bad)]'}>verify {verify.status}</span> : <span className="text-[var(--color-faint)]">—</span>}
|
|
258
254
|
</span>
|
|
259
|
-
<span className="hidden text-right font-mono text-xs tabular-nums text-[var(--color-dim)] md:block">{
|
|
255
|
+
<span className="hidden text-right font-mono text-xs tabular-nums text-[var(--color-dim)] md:block">{verify?.status ?? '—'}</span>
|
|
260
256
|
<span className="hidden text-[var(--color-faint)] md:block">{open ? <CaretDown weight="light" size={14} /> : <CaretRight weight="light" size={14} />}</span>
|
|
261
257
|
</button>
|
|
262
258
|
{open && (
|
|
@@ -320,7 +316,6 @@ const BOARD_COLUMNS = [
|
|
|
320
316
|
|
|
321
317
|
function TaskCard({ t, f, secs, ready }) {
|
|
322
318
|
const reports = reportPathFor(f, t.id);
|
|
323
|
-
const score = t.score?.lastScore ?? t.verification?.score;
|
|
324
319
|
return (
|
|
325
320
|
<li className="rounded-xl bg-[var(--color-raised)] px-3.5 py-3 shadow-[inset_0_0_0_1px_var(--color-hair),inset_0_1px_0_rgb(255_255_255_/_0.05)]">
|
|
326
321
|
<p className="flex items-baseline gap-2">
|
|
@@ -335,7 +330,6 @@ function TaskCard({ t, f, secs, ready }) {
|
|
|
335
330
|
<div className="mt-2.5 flex items-center justify-between text-xs">
|
|
336
331
|
<span className="flex items-center gap-2">
|
|
337
332
|
{t.verification && <span className={t.verification.status === 'pass' ? 'text-[var(--color-ok)]' : 'text-[var(--color-bad)]'}>verify {t.verification.status}</span>}
|
|
338
|
-
{score != null && <span className="font-mono text-[var(--color-dim)]">{score}</span>}
|
|
339
333
|
</span>
|
|
340
334
|
<span className="text-[var(--color-faint)]">{secs ? duration(secs) : ''}</span>
|
|
341
335
|
</div>
|
|
@@ -488,7 +482,7 @@ function DocsTab({ f, doc, onDoc, project }) {
|
|
|
488
482
|
}
|
|
489
483
|
|
|
490
484
|
export function FeatureDetailView({ f, tab, onTab, doc, onDoc, cost, latest = null, tasksView = 'list', onTasksView = () => {}, now = Date.now(), project, projectName }) {
|
|
491
|
-
const
|
|
485
|
+
const verdict = verdictLabel(f);
|
|
492
486
|
const working = latest && isWorking({ ...f, lastActivity: latest }, now);
|
|
493
487
|
return (
|
|
494
488
|
<div className="mx-auto max-w-6xl px-4 pb-24 pt-8 md:px-10 md:pt-12">
|
|
@@ -507,7 +501,7 @@ export function FeatureDetailView({ f, tab, onTab, doc, onDoc, cost, latest = nu
|
|
|
507
501
|
</div>
|
|
508
502
|
<div className="flex shrink-0 gap-6 text-right">
|
|
509
503
|
<div><p className="text-2xl font-semibold tabular-nums">{f.tasks.total ? `${f.tasks.done}/${f.tasks.total}` : '—'}</p><p className="text-xs text-[var(--color-faint)]">tasks</p></div>
|
|
510
|
-
<div><p className="text-2xl font-semibold
|
|
504
|
+
<div><p className="text-2xl font-semibold">{verdict ?? '—'}</p><p className="text-xs text-[var(--color-faint)]">veredito</p></div>
|
|
511
505
|
</div>
|
|
512
506
|
</div>
|
|
513
507
|
<div className="mt-8"><PhaseRail feature={f} size="lg" /></div>
|
|
@@ -8,7 +8,7 @@ import PhaseRail from '../components/PhaseRail.jsx';
|
|
|
8
8
|
import { useMorphQuery } from '../hooks/useMorphQuery.js';
|
|
9
9
|
import { usePref, useNow } from '../hooks/usePref.js';
|
|
10
10
|
import { PHASES, phaseMeta, LOCATION_LABEL } from '../lib/phases.js';
|
|
11
|
-
import { timeAgo } from '../lib/format.js';
|
|
11
|
+
import { timeAgo, verdictLabel } from '../lib/format.js';
|
|
12
12
|
import { apiBase, featureHref } from '../lib/project.js';
|
|
13
13
|
import { isFinished } from '../../scanner/feature-status.js';
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ const LOCATION_ICON = { worktree: GitBranch, archive: Archive, root: HouseSimple
|
|
|
21
21
|
const SORTS = {
|
|
22
22
|
updated: { label: 'Mais recentes', cmp: (a, b) => (lastTouch(b) || '').localeCompare(lastTouch(a) || '') },
|
|
23
23
|
name: { label: 'Nome', cmp: (a, b) => a.name.localeCompare(b.name) },
|
|
24
|
-
|
|
24
|
+
verdict: { label: 'Veredito', cmp: (a, b) => (a.blockers ?? 99) - (b.blockers ?? 99) },
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
function lastTouch(f) {
|
|
@@ -83,7 +83,7 @@ function subline(f, now) {
|
|
|
83
83
|
|
|
84
84
|
export function FeatureRow({ f, now, project, projectName }) {
|
|
85
85
|
const LocIcon = LOCATION_ICON[f.location] || HouseSimple;
|
|
86
|
-
const
|
|
86
|
+
const verdict = verdictLabel(f);
|
|
87
87
|
return (
|
|
88
88
|
<li>
|
|
89
89
|
<Link
|
|
@@ -108,8 +108,8 @@ export function FeatureRow({ f, now, project, projectName }) {
|
|
|
108
108
|
<div className="flex items-center gap-5 md:contents">
|
|
109
109
|
<TaskMeter tasks={f.tasks} />
|
|
110
110
|
<VerifyMark verification={f.verification} />
|
|
111
|
-
<span className="font-mono text-sm tabular-nums text-[var(--color-dim)]" title={
|
|
112
|
-
{
|
|
111
|
+
<span className="font-mono text-sm tabular-nums text-[var(--color-dim)]" title={verdict ? 'veredito do avaliador' : 'sem veredito'}>
|
|
112
|
+
{verdict ?? '—'}
|
|
113
113
|
</span>
|
|
114
114
|
<span className="flex items-center gap-1.5 text-xs text-[var(--color-faint)]" title={LOCATION_LABEL[f.location]}>
|
|
115
115
|
<LocIcon weight="light" size={14} />
|
|
@@ -141,7 +141,7 @@ export function Section({ title, hint, features, now, project }) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
function BoardCard({ f, now, project }) {
|
|
144
|
-
const
|
|
144
|
+
const verdict = verdictLabel(f);
|
|
145
145
|
const paused = f.gates.find((g) => g.paused);
|
|
146
146
|
return (
|
|
147
147
|
<li>
|
|
@@ -169,7 +169,7 @@ function BoardCard({ f, now, project }) {
|
|
|
169
169
|
<div className="mt-2.5 flex items-center justify-between text-xs text-[var(--color-faint)]">
|
|
170
170
|
<span className="flex items-center gap-2">
|
|
171
171
|
<VerifyMark verification={f.verification} />
|
|
172
|
-
{
|
|
172
|
+
{verdict && <span className="font-mono tabular-nums text-[var(--color-dim)]">{verdict}</span>}
|
|
173
173
|
{f.workType && f.workType !== 'feature' && <span>{f.workType}</span>}
|
|
174
174
|
</span>
|
|
175
175
|
<span>{timeAgo(lastTouch(f), now)}</span>
|
|
@@ -14,7 +14,7 @@ const GATE_CHECKS = {
|
|
|
14
14
|
proposal: 'Confere o escopo da proposta: o problema, o que entra e o que fica de fora, antes de gastar esforço em design ou plano.',
|
|
15
15
|
uiux: 'Confere o design das telas (mockups e checklist de tela). É opcional: só existe quando a feature passa pela fase de UI/UX.',
|
|
16
16
|
plan: 'Confere a spec, as tasks e o plano de implementação. Depois dele a spec congela e a implementação começa.',
|
|
17
|
-
review: 'Confere a avaliação independente: um avaliador em contexto novo dá o
|
|
17
|
+
review: 'Confere a avaliação independente: um avaliador em contexto novo dá o veredito (SHIP ou FIX) e só bloqueia com evidência, com o verify verde.',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const GATE_PREFIX = 'gate-';
|
|
@@ -2,16 +2,12 @@ import morphData from 'virtual:morph-data';
|
|
|
2
2
|
import KnowledgeMarkdown from '../components/KnowledgeMarkdown.jsx';
|
|
3
3
|
import { useSelection } from '../components/EntityLink.jsx';
|
|
4
4
|
|
|
5
|
-
const DOCS = morphData.docs || { rubrics: []
|
|
6
|
-
const
|
|
7
|
-
const DIMENSIONS = DOCS.rubrics.filter((r) => r.id !== 'README' && r.id !== 'condensed');
|
|
8
|
-
const CONDENSED = DOCS.rubrics.find((r) => r.id === 'condensed');
|
|
5
|
+
const DOCS = morphData.docs || { rubrics: [] };
|
|
6
|
+
const CHECKLIST = DOCS.rubrics.find((r) => r.id === 'review-checklist');
|
|
9
7
|
const VALIDATORS = morphData.rubrics || [];
|
|
10
8
|
|
|
11
9
|
const GROUPS = [
|
|
12
|
-
{ title: '
|
|
13
|
-
{ title: 'Versão curta', hint: 'usada na avaliação por task', items: CONDENSED ? [{ key: 'dim:condensed', title: CONDENSED.title, doc: CONDENSED }] : [] },
|
|
14
|
-
{ title: 'Provas', hint: 'o que o avaliador precisa ver acontecer', items: DOCS.proofRubrics.filter((r) => r.id !== 'README').map((r) => ({ key: `proof:${r.id}`, title: r.title, doc: r })) },
|
|
10
|
+
{ title: 'Avaliador do Gate 3', hint: 'veredito e bloqueadores com evidência', items: CHECKLIST ? [{ key: 'dim:review-checklist', title: CHECKLIST.title, doc: CHECKLIST }] : [] },
|
|
15
11
|
{ title: 'Validadores determinísticos', hint: 'pesos por categoria no verify', items: VALIDATORS.map((v) => ({ key: `val:${v.id}`, title: `Rubrica ${v.id}`, validator: v })) },
|
|
16
12
|
].filter((g) => g.items.length);
|
|
17
13
|
|
|
@@ -35,7 +31,7 @@ function ValidatorRubric({ v }) {
|
|
|
35
31
|
))}
|
|
36
32
|
</tbody>
|
|
37
33
|
</table>
|
|
38
|
-
<p>
|
|
34
|
+
<p>O score é informativo: o nó de validadores do verify reprova por erro de validador, não pelo número.</p>
|
|
39
35
|
</article>
|
|
40
36
|
);
|
|
41
37
|
}
|
|
@@ -47,7 +43,7 @@ export function RubricsView({ sel, onSel }) {
|
|
|
47
43
|
<header className="rise mb-8">
|
|
48
44
|
<h1 className="text-4xl font-semibold tracking-tight md:text-5xl">Rubricas</h1>
|
|
49
45
|
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[var(--color-dim)]">
|
|
50
|
-
Com o que o trabalho é julgado. O avaliador independente
|
|
46
|
+
Com o que o trabalho é julgado. O avaliador independente decide SHIP ou FIX, e só bloqueia com evidência de UC, teste, regressão ou segurança.
|
|
51
47
|
Os validadores rodam no <span className="font-mono text-[var(--color-fg)]">morph-spec verify</span>, sem modelo.
|
|
52
48
|
</p>
|
|
53
49
|
</header>
|
|
@@ -59,7 +55,7 @@ export function RubricsView({ sel, onSel }) {
|
|
|
59
55
|
aria-current={!current ? 'page' : undefined}
|
|
60
56
|
className={['fluid mb-4 block w-full rounded-lg px-3 py-2 text-left text-sm transition-colors', !current ? 'bg-white/[0.08] text-[var(--color-fg)]' : 'text-[var(--color-dim)] hover:bg-white/[0.04]'].join(' ')}
|
|
61
57
|
>
|
|
62
|
-
Como
|
|
58
|
+
Como o Gate 3 decide
|
|
63
59
|
</button>
|
|
64
60
|
{GROUPS.map((g) => (
|
|
65
61
|
<div key={g.title} className="mb-5">
|
|
@@ -85,7 +81,7 @@ export function RubricsView({ sel, onSel }) {
|
|
|
85
81
|
</nav>
|
|
86
82
|
<div className="bezel min-w-0">
|
|
87
83
|
<div className="bezel-core min-w-0 px-6 py-8 md:px-12 md:py-10">
|
|
88
|
-
{!current && (
|
|
84
|
+
{!current && (CHECKLIST ? <KnowledgeMarkdown text={CHECKLIST.content} /> : <p className="text-sm text-[var(--color-dim)]">Escolha uma rubrica.</p>)}
|
|
89
85
|
{current?.doc && <KnowledgeMarkdown text={current.doc.content} />}
|
|
90
86
|
{current?.validator && <ValidatorRubric v={current.validator} />}
|
|
91
87
|
</div>
|
|
@@ -53,7 +53,6 @@ export function scanDocs(frameworkDir) {
|
|
|
53
53
|
|
|
54
54
|
return {
|
|
55
55
|
guide,
|
|
56
|
-
rubrics: mdDir(join(frameworkDir, 'evals'
|
|
57
|
-
proofRubrics: mdDir(join(frameworkDir, 'evals', 'proof-rubrics')),
|
|
56
|
+
rubrics: mdDir(join(frameworkDir, 'evals')).filter((d) => d.id === 'review-checklist'),
|
|
58
57
|
};
|
|
59
58
|
}
|
|
@@ -221,8 +221,8 @@ function summarize(name, resolved) {
|
|
|
221
221
|
verification: feature.verification
|
|
222
222
|
? { status: feature.verification.status ?? null, scope: feature.verification.scope ?? null, ts: feature.verification.ts ?? null }
|
|
223
223
|
: null,
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
verdict: verdict?.verdict ?? null,
|
|
225
|
+
blockers: verdict?.verdict ? verdict.blockers.length : null,
|
|
226
226
|
createdAt: feature.createdAt || null,
|
|
227
227
|
updatedAt: feature.updatedAt || feature.archivedAt || feature.createdAt || null,
|
|
228
228
|
archivedAt: feature.archivedAt || null,
|
|
@@ -325,13 +325,12 @@ export function readFeatureDetail(projectDir, name, { resolveTree = resolveFeatu
|
|
|
325
325
|
tasksList: tasks.list.map((t) => ({
|
|
326
326
|
...t,
|
|
327
327
|
verification: feature.taskVerifications?.[t.id] || null,
|
|
328
|
-
score: feature.taskScores?.[t.id] || null,
|
|
329
328
|
})),
|
|
330
329
|
tasksErrors: tasks.errors,
|
|
331
330
|
gateDecisions: feature.gateDecisions || {},
|
|
332
331
|
verificationNodes: feature.verification?.nodes || null,
|
|
333
332
|
featureVerification: feature.verifications?.feature || null,
|
|
334
|
-
|
|
333
|
+
blockerList: verdict?.blockers ?? [],
|
|
335
334
|
documents: listDocuments(resolved.dir),
|
|
336
335
|
raw: resolved.feature,
|
|
337
336
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
export function scanRubrics(frameworkDir) {
|
|
5
|
-
const dir = join(frameworkDir, 'rubrics');
|
|
6
|
-
if (!existsSync(dir)) return [];
|
|
7
|
-
return readdirSync(dir).filter(f => f.endsWith('.json')).map(f => {
|
|
8
|
-
const d = JSON.parse(readFileSync(join(dir, f), 'utf8'));
|
|
9
|
-
return { id: d.id, version: d.version || '1.0.0', description: d.description || '',
|
|
10
|
-
categories: d.categories || {}, thresholds: d.thresholds || { approve: 9.0, retry: 6.0 } };
|
|
11
|
-
});
|
|
12
|
-
}
|
|
1
|
+
import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
export function scanRubrics(frameworkDir) {
|
|
5
|
+
const dir = join(frameworkDir, 'rubrics');
|
|
6
|
+
if (!existsSync(dir)) return [];
|
|
7
|
+
return readdirSync(dir).filter(f => f.endsWith('.json')).map(f => {
|
|
8
|
+
const d = JSON.parse(readFileSync(join(dir, f), 'utf8'));
|
|
9
|
+
return { id: d.id, version: d.version || '1.0.0', description: d.description || '',
|
|
10
|
+
categories: d.categories || {}, thresholds: d.thresholds || { approve: 9.0, retry: 6.0 } };
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { defineConfig } from 'vite';
|
|
2
|
-
import react from '@vitejs/plugin-react';
|
|
3
|
-
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
-
import morphDataPlugin from './vite-plugin-morph-data.js';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
import { dirname, resolve } from 'path';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const frameworkDir = resolve(__dirname, '..', '..', 'framework');
|
|
12
|
-
const projectDir = process.env.MORPH_PROJECT_DIR || process.cwd();
|
|
13
|
-
|
|
14
|
-
export default defineConfig({
|
|
15
|
-
root: __dirname,
|
|
16
|
-
plugins: [
|
|
17
|
-
react(),
|
|
18
|
-
tailwindcss(),
|
|
19
|
-
morphDataPlugin(frameworkDir, projectDir),
|
|
20
|
-
],
|
|
21
|
-
server: {
|
|
22
|
-
port: 3200,
|
|
23
|
-
},
|
|
24
|
-
});
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
import morphDataPlugin from './vite-plugin-morph-data.js';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, resolve } from 'path';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const frameworkDir = resolve(__dirname, '..', '..', 'framework');
|
|
12
|
+
const projectDir = process.env.MORPH_PROJECT_DIR || process.cwd();
|
|
13
|
+
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
root: __dirname,
|
|
16
|
+
plugins: [
|
|
17
|
+
react(),
|
|
18
|
+
tailwindcss(),
|
|
19
|
+
morphDataPlugin(frameworkDir, projectDir),
|
|
20
|
+
],
|
|
21
|
+
server: {
|
|
22
|
+
port: 3200,
|
|
23
|
+
},
|
|
24
|
+
});
|