ultra-builder-pro-cli 0.14.0 → 0.16.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/.ultra-template/docs/research/README.md +11 -7
- package/.ultra-template/specs/architecture.md +36 -36
- package/.ultra-template/specs/discovery.md +46 -33
- package/.ultra-template/specs/product.md +38 -29
- package/.ultra-template/specs/research-distillate.md +48 -0
- package/.ultra-template/tasks/contexts/TEMPLATE.md +11 -28
- package/CHANGELOG.md +77 -1
- package/README.md +46 -24
- package/adapters/_shared/codex-assets.cjs +6 -2
- package/adapters/_shared/file-ops.cjs +6 -2
- package/adapters/_shared/handbook.cjs +2 -1
- package/adapters/claude.js +3 -2
- package/adapters/codex.js +1 -0
- package/adapters/kimi.js +3 -2
- package/adapters/opencode.js +3 -2
- package/agents/review-coordinator.md +9 -6
- package/commands/ultra-change.md +1 -1
- package/commands/ultra-plan.md +1 -1
- package/commands/ultra-research.md +1 -1
- package/commands/ultra-think.md +2 -2
- package/docs/AGENT-CONTEXT.md +43 -19
- package/docs/ARCHITECTURE.md +54 -13
- package/docs/PLAN.zh-CN.md +4 -2
- package/docs/ROADMAP.md +10 -4
- package/docs/RUNTIME-COMPAT-MATRIX.md +4 -4
- package/docs/STATE-DB-ACCESS-POLICY.md +33 -29
- package/docs/USER-HANDBOOK-CONTRACT.md +8 -2
- package/docs/WORKFLOW-LIFECYCLE.md +342 -0
- package/hooks/README.md +6 -1
- package/hooks/health_check.py +12 -3
- package/hooks/subagent_tracker.py +52 -77
- package/mcp-server/hook-event.cjs +34 -0
- package/mcp-server/lib/archive-journal.cjs +17 -5
- package/mcp-server/lib/baseline-workflow.cjs +158 -18
- package/mcp-server/lib/change-workflow.cjs +598 -85
- package/mcp-server/lib/context-spine.cjs +305 -25
- package/mcp-server/lib/decision-dialogue.cjs +713 -0
- package/mcp-server/lib/doctor.cjs +42 -3
- package/mcp-server/lib/init-project.cjs +210 -16
- package/mcp-server/lib/plan-store.cjs +4 -1
- package/mcp-server/lib/pricing.cjs +7 -10
- package/mcp-server/lib/project-breadcrumb.cjs +15 -0
- package/mcp-server/lib/projector.cjs +57 -3
- package/mcp-server/lib/spec-learning.cjs +79 -2
- package/mcp-server/lib/state-db.cjs +168 -8
- package/mcp-server/lib/state-ops.cjs +160 -9
- package/mcp-server/lib/workflow-state.cjs +2305 -0
- package/mcp-server/server.cjs +215 -15
- package/orchestrator/daemon.cjs +1 -2
- package/orchestrator/dispatch-rules.cjs +5 -31
- package/orchestrator/planner/plan-builder.cjs +21 -12
- package/package.json +13 -3
- package/skills/ultra-change/SKILL.md +94 -80
- package/skills/ultra-change/references/change-contract.md +36 -0
- package/skills/ultra-deliver/SKILL.md +76 -79
- package/skills/ultra-deliver/references/baseline-reconciliation.md +20 -0
- package/skills/ultra-dev/SKILL.md +62 -59
- package/skills/ultra-doctor/SKILL.md +61 -82
- package/skills/ultra-init/SKILL.md +101 -103
- package/skills/ultra-init/references/brownfield-adoption.md +2 -2
- package/skills/ultra-plan/SKILL.md +99 -85
- package/skills/ultra-plan/references/semantic-preflight.md +18 -0
- package/skills/ultra-research/SKILL.md +102 -60
- package/skills/ultra-research/references/00-problem-validation.md +36 -0
- package/skills/ultra-research/references/01-opportunity-discovery.md +33 -0
- package/skills/ultra-research/references/02-market-assessment.md +30 -0
- package/skills/ultra-research/references/03-competitive-landscape.md +31 -0
- package/skills/ultra-research/references/04-product-strategy.md +32 -0
- package/skills/ultra-research/references/05-assumptions-validation.md +31 -0
- package/skills/ultra-research/references/10-user-personas.md +29 -0
- package/skills/ultra-research/references/11-user-scenarios.md +28 -0
- package/skills/ultra-research/references/20-user-stories.md +30 -0
- package/skills/ultra-research/references/21-features-scope.md +29 -0
- package/skills/ultra-research/references/22-success-metrics.md +28 -0
- package/skills/ultra-research/references/30-architecture-context.md +30 -0
- package/skills/ultra-research/references/31-solution-strategy.md +28 -0
- package/skills/ultra-research/references/32-building-blocks.md +28 -0
- package/skills/ultra-research/references/40-deployment.md +29 -0
- package/skills/ultra-research/references/41-quality-risks.md +28 -0
- package/skills/ultra-research/references/99-synthesis.md +35 -0
- package/skills/ultra-review/SKILL.md +63 -73
- package/skills/ultra-review/references/review-modes.md +27 -0
- package/skills/ultra-review/references/unified-schema.md +25 -0
- package/skills/ultra-review/scripts/review_wait.py +40 -3
- package/skills/ultra-status/SKILL.md +41 -41
- package/skills/ultra-test/SKILL.md +54 -55
- package/skills/ultra-think/SKILL.md +26 -12
- package/skills/ultra-think/references/decision-dialogue.md +130 -0
- package/skills/ultra-verify/SKILL.md +1 -1
- package/skills/ultra-verify/references/{confidence-system.md → evidence-status.md} +3 -3
- package/spec/cli-protocol.md +17 -3
- package/spec/mcp-tools.yaml +683 -64
- package/spec/schemas/baseline-reconciliation.v1.schema.json +73 -0
- package/spec/schemas/delivery-report.v1.schema.json +111 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +140 -1
- package/spec/schemas/tasks.v4.5.schema.json +8 -1
- package/spec/schemas/test-report.v1.schema.json +194 -0
- package/ultra-tools/cli.cjs +1 -1
- package/ultra-tools/commands/migrate.cjs +3 -3
- package/ultra-tools/commands/status.cjs +228 -12
- package/.ultra-template/delivery-report.json +0 -9
- package/.ultra-template/test-report.json +0 -8
- package/adapters/_shared/tests/file-ops.test.cjs +0 -54
- package/adapters/_shared/tests/frontmatter.test.cjs +0 -47
- package/adapters/_shared/tests/handbook.test.cjs +0 -101
- package/adapters/_shared/tests/path-rewrite.test.cjs +0 -60
- package/adapters/_shared/tests/provenance.test.cjs +0 -133
- package/adapters/_shared/tests/resolve-target.test.cjs +0 -75
- package/adapters/_shared/tests/runtime-assets.test.cjs +0 -149
- package/adapters/_shared/tests/settings-merge.test.cjs +0 -85
- package/adapters/_shared/tests/validate.test.cjs +0 -30
- package/adapters/tests/claude.test.cjs +0 -138
- package/adapters/tests/codex-hook.test.cjs +0 -142
- package/adapters/tests/codex.test.cjs +0 -430
- package/adapters/tests/kimi-hook.test.cjs +0 -149
- package/adapters/tests/kimi.test.cjs +0 -259
- package/adapters/tests/opencode.test.cjs +0 -357
- package/mcp-server/lib/archive-journal.test.cjs +0 -35
- package/mcp-server/lib/baseline-workflow.test.cjs +0 -477
- package/mcp-server/lib/change-workflow.test.cjs +0 -971
- package/mcp-server/lib/context-spine.test.cjs +0 -93
- package/mcp-server/lib/doctor.test.cjs +0 -148
- package/mcp-server/lib/plan-store.test.cjs +0 -132
- package/mcp-server/lib/prd-parser.test.cjs +0 -82
- package/mcp-server/lib/project-breadcrumb.test.cjs +0 -112
- package/mcp-server/lib/runtime-state.test.cjs +0 -92
- package/mcp-server/lib/spec-learning.test.cjs +0 -67
- package/mcp-server/lib/state-db.test.cjs +0 -314
- package/mcp-server/lib/task-expander.test.cjs +0 -169
- package/mcp-server/lib/topo.test.cjs +0 -158
- package/mcp-server/tests/circuit-breaker.test.cjs +0 -125
- package/mcp-server/tests/commit-hash-flow.test.cjs +0 -117
- package/mcp-server/tests/concurrency-update.test.cjs +0 -96
- package/mcp-server/tests/concurrency.test.cjs +0 -112
- package/mcp-server/tests/db-ops.test.cjs +0 -121
- package/mcp-server/tests/init-project.test.cjs +0 -393
- package/mcp-server/tests/migrate.test.cjs +0 -232
- package/mcp-server/tests/projector.test.cjs +0 -162
- package/mcp-server/tests/server-contract.test.cjs +0 -1467
- package/mcp-server/tests/staleness.test.cjs +0 -190
- package/mcp-server/tests/state-db.test.cjs +0 -106
- package/mcp-server/tests/state-ops.test.cjs +0 -449
- package/mcp-server/tests/tagged-tasks.test.cjs +0 -163
- package/mcp-server/tests/telemetry.test.cjs +0 -219
- package/orchestrator/planner/plan-builder.test.cjs +0 -165
- package/orchestrator/tests/auto-merge.test.cjs +0 -216
- package/orchestrator/tests/daemon.test.cjs +0 -311
- package/orchestrator/tests/dispatch-rules.test.cjs +0 -124
- package/orchestrator/tests/parallel-orchestrator.test.cjs +0 -233
- package/orchestrator/tests/phase-8b-gate.test.cjs +0 -183
- package/orchestrator/tests/recovery.test.cjs +0 -190
- package/orchestrator/tests/session-runner.test.cjs +0 -314
- package/orchestrator/tests/skill-miner.test.cjs +0 -147
- package/orchestrator/tests/worktree-manager.test.cjs +0 -148
- package/skills/ultra-research/references/architecture.md +0 -38
- package/skills/ultra-research/references/problem-product.md +0 -39
- package/skills/ultra-research/references/quality-delivery.md +0 -34
- package/skills/ultra-research/references/synthesis.md +0 -38
- package/skills/ultra-research/references/users-scope.md +0 -40
- package/ultra-tools/legacy-memory.test.cjs +0 -80
- package/ultra-tools/status-command.test.cjs +0 -181
- package/ultra-tools/system-command.test.cjs +0 -309
|
@@ -7,6 +7,7 @@ const { spawnSync } = require('node:child_process');
|
|
|
7
7
|
|
|
8
8
|
const ops = require('./state-ops.cjs');
|
|
9
9
|
const providerRefs = require('./provider-refs.cjs');
|
|
10
|
+
const workflows = require('./workflow-state.cjs');
|
|
10
11
|
|
|
11
12
|
const BASELINE_ID = /^[a-zA-Z0-9_-]+$/;
|
|
12
13
|
const MODES = new Set(['greenfield', 'brownfield']);
|
|
@@ -124,6 +125,7 @@ function rowToBaseline(row) {
|
|
|
124
125
|
classification: parseJson(row.classification_json, 'classification_json', {}),
|
|
125
126
|
worktree_files: parseJson(row.worktree_files_json, 'worktree_files_json', []),
|
|
126
127
|
worktree_accepted: Boolean(row.worktree_accepted),
|
|
128
|
+
known_red_accepted: Boolean(row.known_red_accepted),
|
|
127
129
|
provider_refs: parseJson(row.provider_refs_json, 'provider_refs_json', {}),
|
|
128
130
|
};
|
|
129
131
|
for (const key of [
|
|
@@ -496,7 +498,8 @@ function recordBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = t
|
|
|
496
498
|
db.prepare(
|
|
497
499
|
`UPDATE baselines SET status = ?, scope_json = ?, repository_revision = ?,
|
|
498
500
|
repository_branch = ?, worktree_state = ?, worktree_digest = ?, worktree_files_json = ?,
|
|
499
|
-
worktree_accepted = 0,
|
|
501
|
+
worktree_accepted = 0, known_red_accepted = 0,
|
|
502
|
+
spec_refs_json = ?, evidence_json = ?, verification_json = ?,
|
|
500
503
|
unknowns_json = ?, gaps_json = ?, classification_json = ?, provider_refs_json = ?,
|
|
501
504
|
approved_by = NULL, approval_note = NULL, converged_at = NULL, updated_at = ? WHERE id = ?`,
|
|
502
505
|
).run(
|
|
@@ -551,7 +554,114 @@ function storedEvidenceBlockers(baseline, rootDir) {
|
|
|
551
554
|
return blockers;
|
|
552
555
|
}
|
|
553
556
|
|
|
554
|
-
function
|
|
557
|
+
function storedReadyInvariantBlockers(baseline) {
|
|
558
|
+
const blockers = [];
|
|
559
|
+
const arrays = ['scope', 'spec_refs', 'evidence', 'verification', 'unknowns', 'gaps'];
|
|
560
|
+
for (const field of arrays) {
|
|
561
|
+
if (!Array.isArray(baseline[field])) blockers.push(`BASELINE_STATE_INVALID:${field}`);
|
|
562
|
+
}
|
|
563
|
+
if (!Array.isArray(baseline.scope) || baseline.scope.length === 0) {
|
|
564
|
+
blockers.push('BASELINE_SCOPE_MISSING');
|
|
565
|
+
}
|
|
566
|
+
if (Array.isArray(baseline.spec_refs)) {
|
|
567
|
+
for (const kind of ['discovery', 'product', 'architecture']) {
|
|
568
|
+
if (!baseline.spec_refs.some((item) => item?.kind === kind)) {
|
|
569
|
+
blockers.push(`BASELINE_SPEC_MISSING:${kind}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (!Array.isArray(baseline.evidence) || baseline.evidence.length === 0) {
|
|
574
|
+
blockers.push('BASELINE_EVIDENCE_MISSING');
|
|
575
|
+
} else if (baseline.mode === 'brownfield'
|
|
576
|
+
&& !baseline.evidence.some((item) => item?.kind === 'source')) {
|
|
577
|
+
blockers.push('BASELINE_SOURCE_EVIDENCE_MISSING');
|
|
578
|
+
}
|
|
579
|
+
if (!Array.isArray(baseline.verification) || baseline.verification.length === 0) {
|
|
580
|
+
blockers.push('BASELINE_VERIFICATION_MISSING');
|
|
581
|
+
} else {
|
|
582
|
+
for (const item of baseline.verification) {
|
|
583
|
+
if (!item || typeof item !== 'object') {
|
|
584
|
+
blockers.push('BASELINE_STATE_INVALID:verification');
|
|
585
|
+
} else if (item.status === 'not_run') {
|
|
586
|
+
blockers.push(`BASELINE_VERIFICATION_NOT_RUN:${item.name || 'unknown'}`);
|
|
587
|
+
} else if (item.status === 'known_red') {
|
|
588
|
+
if (!String(item.rationale || '').trim()) {
|
|
589
|
+
blockers.push(`BASELINE_KNOWN_RED_RATIONALE_MISSING:${item.name || 'unknown'}`);
|
|
590
|
+
}
|
|
591
|
+
if (!baseline.known_red_accepted) {
|
|
592
|
+
blockers.push(`BASELINE_KNOWN_RED_NOT_ACCEPTED:${item.name || 'unknown'}`);
|
|
593
|
+
}
|
|
594
|
+
} else if (item.status !== 'pass') {
|
|
595
|
+
blockers.push(`BASELINE_VERIFICATION_STATUS_INVALID:${item.name || 'unknown'}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (!String(baseline.repository_revision || '').trim()) {
|
|
600
|
+
blockers.push('BASELINE_REVISION_MISSING');
|
|
601
|
+
}
|
|
602
|
+
if (!String(baseline.approved_by || '').trim()) blockers.push('BASELINE_APPROVER_MISSING');
|
|
603
|
+
if (String(baseline.approval_note || '').trim().length < 3) {
|
|
604
|
+
blockers.push('BASELINE_APPROVAL_NOTE_MISSING');
|
|
605
|
+
}
|
|
606
|
+
if (!baseline.converged_at || Number.isNaN(Date.parse(baseline.converged_at))) {
|
|
607
|
+
blockers.push('BASELINE_CONVERGENCE_TIMESTAMP_MISSING');
|
|
608
|
+
}
|
|
609
|
+
if (Array.isArray(baseline.unknowns)) {
|
|
610
|
+
for (const item of baseline.unknowns.filter((unknown) => unknown?.blocking === true)) {
|
|
611
|
+
blockers.push(`BASELINE_UNKNOWN_BLOCKING:${item.summary || 'unknown'}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (Array.isArray(baseline.gaps)) {
|
|
615
|
+
for (const gap of baseline.gaps.filter((item) => (
|
|
616
|
+
item?.status === 'open' && item?.blocking === true
|
|
617
|
+
))) {
|
|
618
|
+
blockers.push(`BASELINE_GAP_BLOCKING:${gap.id || 'unknown'}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return [...new Set(blockers)];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function completedBaselineResearch(db, baseline, rootDir) {
|
|
625
|
+
const acceptedMode = baseline.mode === 'greenfield' ? 'full' : 'adoption';
|
|
626
|
+
const row = db.prepare(
|
|
627
|
+
`SELECT id FROM workflow_runs
|
|
628
|
+
WHERE kind = 'research' AND baseline_id = ? AND mode = ? AND status = 'completed'
|
|
629
|
+
ORDER BY completed_at DESC, rowid DESC LIMIT 1`,
|
|
630
|
+
).get(baseline.id, acceptedMode);
|
|
631
|
+
return row ? workflows.readWorkflow(db, row.id, { rootDir }) : null;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function convergenceResearchBlockers(db, baseline, rootDir) {
|
|
635
|
+
const run = completedBaselineResearch(db, baseline, rootDir);
|
|
636
|
+
if (!run) return ['BASELINE_RESEARCH_INCOMPLETE'];
|
|
637
|
+
return run.artifact_health.blockers.map((item) => `BASELINE_RESEARCH_${item}`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function storedResearchHealth(db, baseline, rootDir) {
|
|
641
|
+
if (!baseline.research_run_id) {
|
|
642
|
+
return { blockers: ['BASELINE_RESEARCH_PROVENANCE_MISSING'], warnings: [] };
|
|
643
|
+
}
|
|
644
|
+
const run = workflows.readWorkflow(db, baseline.research_run_id, { rootDir });
|
|
645
|
+
if (!run || run.kind !== 'research' || run.status !== 'completed') {
|
|
646
|
+
return { blockers: ['BASELINE_RESEARCH_RECORD_INVALID'], warnings: [] };
|
|
647
|
+
}
|
|
648
|
+
const livingSpecifications = new Set(
|
|
649
|
+
(Array.isArray(baseline.spec_refs) ? baseline.spec_refs : [])
|
|
650
|
+
.map((item) => item?.path).filter(Boolean),
|
|
651
|
+
);
|
|
652
|
+
const blockers = run.artifact_health.blockers.filter((item) => {
|
|
653
|
+
const separator = item.indexOf(':');
|
|
654
|
+
if (separator < 0) return true;
|
|
655
|
+
const artifactPath = item.slice(separator + 1);
|
|
656
|
+
return !livingSpecifications.has(artifactPath);
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
blockers: blockers.map((item) => `BASELINE_RESEARCH_${item}`),
|
|
660
|
+
warnings: [],
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function convergenceBlockers(db, baseline, input, rootDir) {
|
|
555
665
|
const blockers = new Set();
|
|
556
666
|
const expected = typeof input.expected_revision === 'string' ? input.expected_revision.trim() : '';
|
|
557
667
|
if (!expected) blockers.add('BASELINE_REVISION_REQUIRED');
|
|
@@ -561,9 +671,10 @@ function convergenceBlockers(baseline, input, rootDir) {
|
|
|
561
671
|
if (baseline.repository_branch && snapshot.branch
|
|
562
672
|
&& baseline.repository_branch !== snapshot.branch) blockers.add('BASELINE_BRANCH_STALE');
|
|
563
673
|
if (!Array.isArray(baseline.scope) || baseline.scope.length === 0) blockers.add('BASELINE_SCOPE_MISSING');
|
|
564
|
-
for (const kind of ['product', 'architecture']) {
|
|
674
|
+
for (const kind of ['discovery', 'product', 'architecture']) {
|
|
565
675
|
if (!baseline.spec_refs.some((ref) => ref.kind === kind)) blockers.add(`BASELINE_SPEC_MISSING:${kind}`);
|
|
566
676
|
}
|
|
677
|
+
for (const blocker of convergenceResearchBlockers(db, baseline, rootDir)) blockers.add(blocker);
|
|
567
678
|
for (const blocker of storedSpecBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
568
679
|
for (const blocker of storedEvidenceBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
569
680
|
for (const blocker of storedScopeBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
@@ -603,7 +714,7 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
|
|
|
603
714
|
if (!['draft', 'adopting', 'blocked'].includes(current.status)) {
|
|
604
715
|
throw new BaselineWorkflowError('BASELINE_NOT_CONVERGEABLE', `baseline ${current.id} is ${current.status}`);
|
|
605
716
|
}
|
|
606
|
-
const blockers = convergenceBlockers(current, input, rootDir);
|
|
717
|
+
const blockers = convergenceBlockers(db, current, input, rootDir);
|
|
607
718
|
const ts = nowIso();
|
|
608
719
|
if (blockers.length > 0) {
|
|
609
720
|
ops.tx(db, () => {
|
|
@@ -614,22 +725,32 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
|
|
|
614
725
|
type: 'baseline_blocked', payload: { baseline_id: current.id, blockers },
|
|
615
726
|
});
|
|
616
727
|
}
|
|
728
|
+
workflows.blockInitializationInTx(db, current.id, blockers);
|
|
617
729
|
});
|
|
618
730
|
return { ready: false, status: 'blocked', blockers, baseline: readBaseline(db, current.id) };
|
|
619
731
|
}
|
|
732
|
+
const research = completedBaselineResearch(db, current, rootDir);
|
|
620
733
|
ops.tx(db, () => {
|
|
621
734
|
db.prepare(
|
|
622
735
|
`UPDATE baselines SET status = 'ready', approved_by = ?, approval_note = ?,
|
|
623
|
-
worktree_accepted = ?,
|
|
736
|
+
worktree_accepted = ?, known_red_accepted = ?, research_run_id = ?,
|
|
737
|
+
converged_at = ?, updated_at = ? WHERE id = ?`,
|
|
624
738
|
).run(
|
|
625
739
|
String(input.approved_by).trim(), String(input.approval_note).trim(),
|
|
626
|
-
input.accept_dirty_worktree === true ? 1 : 0,
|
|
740
|
+
input.accept_dirty_worktree === true ? 1 : 0,
|
|
741
|
+
input.accept_known_red === true ? 1 : 0, research.id, ts, ts, current.id,
|
|
627
742
|
);
|
|
628
743
|
if (emitEvent) {
|
|
629
744
|
ops.appendEventInTx(db, {
|
|
630
745
|
type: 'baseline_converged', payload: { baseline_id: current.id, revision: current.repository_revision },
|
|
631
746
|
});
|
|
632
747
|
}
|
|
748
|
+
workflows.completeInitializationInTx(db, {
|
|
749
|
+
...current,
|
|
750
|
+
approved_by: String(input.approved_by).trim(),
|
|
751
|
+
research_run_id: research.id,
|
|
752
|
+
converged_at: ts,
|
|
753
|
+
});
|
|
633
754
|
});
|
|
634
755
|
return { ready: true, status: 'ready', blockers: [], baseline: readBaseline(db, current.id) };
|
|
635
756
|
}
|
|
@@ -654,12 +775,17 @@ function inspectBaseline(db, { rootDir = process.cwd(), id } = {}) {
|
|
|
654
775
|
status: 'fail', blockers: [`BASELINE_NOT_READY:${baseline.status}`], warnings: [], baseline,
|
|
655
776
|
};
|
|
656
777
|
}
|
|
778
|
+
const research = storedResearchHealth(db, baseline, rootDir);
|
|
657
779
|
const blockers = [
|
|
658
|
-
...
|
|
659
|
-
...
|
|
660
|
-
...
|
|
780
|
+
...storedReadyInvariantBlockers(baseline),
|
|
781
|
+
...(Array.isArray(baseline.scope) ? storedScopeBlockers(baseline, rootDir) : []),
|
|
782
|
+
...(Array.isArray(baseline.spec_refs) ? storedSpecBlockers(baseline, rootDir) : []),
|
|
783
|
+
...(Array.isArray(baseline.evidence) ? storedEvidenceBlockers(baseline, rootDir) : []),
|
|
784
|
+
...research.blockers,
|
|
661
785
|
];
|
|
662
|
-
const snapshot = gitWorktreeSnapshot(
|
|
786
|
+
const snapshot = gitWorktreeSnapshot(
|
|
787
|
+
rootDir, Array.isArray(baseline.scope) && baseline.scope.length > 0 ? baseline.scope : ['.'],
|
|
788
|
+
);
|
|
663
789
|
if (snapshot.head && baseline.repository_revision && snapshot.head !== baseline.repository_revision) {
|
|
664
790
|
blockers.push('BASELINE_HEAD_STALE');
|
|
665
791
|
} else if (!snapshot.head && baseline.repository_revision?.startsWith('workspace:')) {
|
|
@@ -677,10 +803,10 @@ function inspectBaseline(db, { rootDir = process.cwd(), id } = {}) {
|
|
|
677
803
|
if (baseline.worktree_state === 'dirty' && !baseline.worktree_accepted) {
|
|
678
804
|
blockers.push('BASELINE_DIRTY_WORKTREE_NOT_ACCEPTED');
|
|
679
805
|
}
|
|
680
|
-
|
|
681
|
-
blockers.
|
|
682
|
-
|
|
683
|
-
|
|
806
|
+
return {
|
|
807
|
+
status: blockers.length === 0 ? 'pass' : 'fail', blockers: [...new Set(blockers)].sort(),
|
|
808
|
+
warnings: research.warnings, baseline,
|
|
809
|
+
};
|
|
684
810
|
}
|
|
685
811
|
|
|
686
812
|
function inferSpecKind(file) {
|
|
@@ -688,7 +814,9 @@ function inferSpecKind(file) {
|
|
|
688
814
|
return SPEC_KINDS.has(name) ? name : 'other';
|
|
689
815
|
}
|
|
690
816
|
|
|
691
|
-
function reconcileBaseline(db, {
|
|
817
|
+
function reconcileBaseline(db, {
|
|
818
|
+
baseline_updates = [], change_id = null, reconciliation = null,
|
|
819
|
+
} = {}, {
|
|
692
820
|
rootDir = process.cwd(), emitEvent = true,
|
|
693
821
|
} = {}) {
|
|
694
822
|
const current = readBaseline(db);
|
|
@@ -717,6 +845,12 @@ function reconcileBaseline(db, { baseline_updates = [], change_id = null } = {},
|
|
|
717
845
|
}
|
|
718
846
|
}
|
|
719
847
|
const specs = [...refs.values()];
|
|
848
|
+
const resolvedGapIds = new Set(reconciliation?.resolved_gap_ids || []);
|
|
849
|
+
const resolvedUnknowns = new Set(reconciliation?.resolved_unknowns || []);
|
|
850
|
+
const gaps = current.gaps.map((gap) => (
|
|
851
|
+
resolvedGapIds.has(gap.id) ? { ...gap, status: 'resolved' } : gap
|
|
852
|
+
));
|
|
853
|
+
const unknowns = current.unknowns.filter((unknown) => !resolvedUnknowns.has(unknown.summary));
|
|
720
854
|
const snapshot = gitWorktreeSnapshot(rootDir, current.scope);
|
|
721
855
|
const revision = snapshot.head || workspaceRevision({
|
|
722
856
|
scope: current.scope, specs, evidence,
|
|
@@ -726,16 +860,22 @@ function reconcileBaseline(db, { baseline_updates = [], change_id = null } = {},
|
|
|
726
860
|
db.prepare(
|
|
727
861
|
`UPDATE baselines SET repository_revision = ?, repository_branch = ?, worktree_state = ?,
|
|
728
862
|
worktree_digest = ?, worktree_files_json = ?, worktree_accepted = 0,
|
|
729
|
-
spec_refs_json = ?, evidence_json = ?,
|
|
863
|
+
spec_refs_json = ?, evidence_json = ?, gaps_json = ?, unknowns_json = ?,
|
|
864
|
+
updated_at = ? WHERE id = ?`,
|
|
730
865
|
).run(
|
|
731
866
|
revision, snapshot.branch, snapshot.state, snapshot.digest, JSON.stringify(snapshot.files),
|
|
732
|
-
JSON.stringify(specs), JSON.stringify(evidence),
|
|
867
|
+
JSON.stringify(specs), JSON.stringify(evidence), JSON.stringify(gaps),
|
|
868
|
+
JSON.stringify(unknowns), ts, current.id,
|
|
733
869
|
);
|
|
734
870
|
if (emitEvent) {
|
|
735
871
|
ops.appendEventInTx(db, {
|
|
736
872
|
type: 'baseline_reconciled',
|
|
737
873
|
change_id,
|
|
738
|
-
payload: {
|
|
874
|
+
payload: {
|
|
875
|
+
baseline_id: current.id, baseline_updates, repository_revision: revision,
|
|
876
|
+
semantic_change_ids: (reconciliation?.semantic_changes || []).map((item) => item.id),
|
|
877
|
+
resolved_gap_ids: [...resolvedGapIds], resolved_unknowns: [...resolvedUnknowns],
|
|
878
|
+
},
|
|
739
879
|
});
|
|
740
880
|
}
|
|
741
881
|
});
|