ultra-builder-pro-cli 0.11.0 → 0.13.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 (75) hide show
  1. package/.ultra-template/docs/research/README.md +21 -44
  2. package/.ultra-template/specs/architecture.md +54 -223
  3. package/.ultra-template/specs/discovery.md +41 -228
  4. package/.ultra-template/specs/product.md +44 -200
  5. package/.ultra-template/tasks/contexts/TEMPLATE.md +36 -0
  6. package/.ultra-template/tasks/tasks.json +6 -0
  7. package/CHANGELOG.md +94 -1
  8. package/README.md +37 -9
  9. package/adapters/_shared/codex-assets.cjs +1 -1
  10. package/adapters/_shared/handbook.cjs +5 -4
  11. package/adapters/_shared/tests/handbook.test.cjs +4 -0
  12. package/adapters/opencode.js +8 -2
  13. package/adapters/tests/codex.test.cjs +10 -2
  14. package/adapters/tests/kimi-hook.test.cjs +1 -1
  15. package/adapters/tests/opencode.test.cjs +36 -1
  16. package/commands/ultra-init.md +3 -3
  17. package/docs/AGENT-CONTEXT.md +17 -8
  18. package/docs/ARCHITECTURE.md +51 -8
  19. package/docs/PLAN.zh-CN.md +9 -6
  20. package/docs/ROADMAP.md +5 -3
  21. package/docs/RUNTIME-COMPAT-MATRIX.md +8 -5
  22. package/docs/STATE-DB-ACCESS-POLICY.md +19 -13
  23. package/docs/USER-HANDBOOK-CONTRACT.md +9 -5
  24. package/hooks/README.md +4 -3
  25. package/hooks/adapters/codex.py +1 -1
  26. package/hooks/adapters/kimi.py +1 -1
  27. package/hooks/context_spine.py +153 -16
  28. package/hooks/health_check.py +11 -0
  29. package/hooks/pre_stop_check.py +6 -5
  30. package/mcp-server/lib/archive-journal.cjs +192 -0
  31. package/mcp-server/lib/archive-journal.test.cjs +35 -0
  32. package/mcp-server/lib/baseline-workflow.cjs +781 -0
  33. package/mcp-server/lib/baseline-workflow.test.cjs +477 -0
  34. package/mcp-server/lib/change-workflow.cjs +256 -85
  35. package/mcp-server/lib/change-workflow.test.cjs +436 -6
  36. package/mcp-server/lib/context-spine.cjs +74 -9
  37. package/mcp-server/lib/context-spine.test.cjs +20 -0
  38. package/mcp-server/lib/doctor.cjs +28 -1
  39. package/mcp-server/lib/doctor.test.cjs +63 -1
  40. package/mcp-server/lib/init-project.cjs +334 -16
  41. package/mcp-server/lib/provider-refs.cjs +42 -0
  42. package/mcp-server/lib/spec-learning.test.cjs +5 -0
  43. package/mcp-server/lib/state-authority.cjs +9 -3
  44. package/mcp-server/lib/state-db.cjs +202 -21
  45. package/mcp-server/lib/state-db.test.cjs +140 -3
  46. package/mcp-server/lib/state-ops.cjs +68 -6
  47. package/mcp-server/lib/task-expander.cjs +7 -1
  48. package/mcp-server/lib/task-expander.test.cjs +71 -0
  49. package/mcp-server/server.cjs +62 -6
  50. package/mcp-server/tests/init-project.test.cjs +216 -9
  51. package/mcp-server/tests/migrate.test.cjs +33 -0
  52. package/mcp-server/tests/server-contract.test.cjs +478 -3
  53. package/mcp-server/tests/state-db.test.cjs +23 -2
  54. package/mcp-server/tests/state-ops.test.cjs +108 -0
  55. package/package.json +1 -1
  56. package/skills/ultra-change/SKILL.md +32 -13
  57. package/skills/ultra-deliver/SKILL.md +19 -5
  58. package/skills/ultra-dev/SKILL.md +8 -2
  59. package/skills/ultra-doctor/SKILL.md +30 -9
  60. package/skills/ultra-init/SKILL.md +107 -66
  61. package/skills/ultra-init/references/brownfield-adoption.md +93 -0
  62. package/skills/ultra-plan/SKILL.md +15 -5
  63. package/skills/ultra-research/SKILL.md +17 -5
  64. package/skills/ultra-status/SKILL.md +15 -6
  65. package/spec/cli-protocol.md +15 -1
  66. package/spec/mcp-tools.yaml +316 -11
  67. package/spec/schemas/mcp-tools.schema.json +1 -1
  68. package/spec/schemas/state-db.sql +44 -2
  69. package/ultra-tools/cli.cjs +9 -5
  70. package/ultra-tools/commands/migrate.cjs +62 -36
  71. package/ultra-tools/commands/status.cjs +29 -2
  72. package/ultra-tools/commands/system.cjs +340 -6
  73. package/ultra-tools/commands/task.cjs +11 -2
  74. package/ultra-tools/status-command.test.cjs +49 -0
  75. package/ultra-tools/system-command.test.cjs +256 -0
@@ -3,11 +3,11 @@
3
3
  # Validated by spec/schemas/mcp-tools.schema.json (run `npm run test:spec`).
4
4
  # Trace: PLAN §7.3, decisions D12 / D29 / D31-D34.
5
5
 
6
- version: '1.1'
6
+ version: '1.2'
7
7
 
8
8
  info:
9
9
  title: Ultra Builder Pro MCP tool contracts
10
- phase: '1.1'
10
+ phase: '1.2'
11
11
  trace_to: 'PLAN docs/PLAN.zh-CN.md §7.3'
12
12
  notes: |
13
13
  This published contract contains only tools registered by the MCP server.
@@ -23,6 +23,7 @@ families:
23
23
  - { name: task, description: 'authoritative task lifecycle on state.db', phase: '2' }
24
24
  - { name: session, description: 'execution-unit lifecycle with single-active-lease admission (D33)', phase: '4.5' }
25
25
  - { name: plan, description: 'execution-plan artifact export and query (D34)', phase: '8a' }
26
+ - { name: baseline, description: 'project adoption, baseline evidence, convergence, and drift authority', phase: '11' }
26
27
  - { name: change, description: 'continuous post-delivery change lifecycle and convergence', phase: '9' }
27
28
  - { name: system, description: 'read-only diagnostics and explicit backup-first repair', phase: '9' }
28
29
 
@@ -31,7 +32,7 @@ tools:
31
32
  # ─────────────────────────── task family ────────────────────────────────
32
33
  - name: task.create
33
34
  family: task
34
- description: Create a new task in state.db. Emits task_created event. If `id` is omitted the server mints a uuid.
35
+ description: Create a new task in state.db. Emits task_created event. If `id` is omitted the server mints a uuid. Child tasks inherit the parent's change ownership; conflicting ownership is rejected.
35
36
  phase: '2'
36
37
  writer_role: mcp
37
38
  input_schema:
@@ -47,10 +48,10 @@ tools:
47
48
  estimated_days: { type: number, exclusiveMinimum: 0, description: 'estimated implementation effort in days' }
48
49
  deps: { type: array, items: { type: string } }
49
50
  files_modified: { type: array, items: { type: string }, description: 'Phase 8A conflict detection' }
50
- parent_id: { type: string, description: 'Phase 8A.3 parent reference for task.expand children' }
51
+ parent_id: { type: string, description: 'Phase 8A.3 parent reference; the child inherits this task change ownership' }
51
52
  tag: { type: string, description: 'git branch tag (Phase 7.2)' }
52
53
  trace_to: { type: string, description: 'spec anchor (e.g. spec/mcp-tools.yaml#task.create)' }
53
- change_id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$', description: 'continuous change unit owning this task' }
54
+ change_id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$', description: 'active or blocked continuous change unit owning this task; inherited from parent_id when omitted' }
54
55
  output_schema:
55
56
  type: object
56
57
  required: [id, status, created_at]
@@ -62,6 +63,11 @@ tools:
62
63
  errors:
63
64
  - { code: VALIDATION_ERROR, description: 'input failed schema validation' }
64
65
  - { code: DUPLICATE_TASK_ID, description: 'task id already exists' }
66
+ - { code: TASK_NOT_FOUND, description: 'parent task does not exist' }
67
+ - { code: CHANGE_NOT_FOUND, description: 'assigned change does not exist' }
68
+ - { code: CHANGE_NOT_MUTABLE, description: 'assigned change no longer accepts tasks' }
69
+ - { code: TASK_CHANGE_OWNERSHIP_MISMATCH, description: 'child ownership conflicts with its parent change' }
70
+ - { code: BASELINE_NOT_READY, description: 'ordinary task creation requires a ready baseline or an already-authorized active change' }
65
71
  - { code: LEGACY_STATE_MIGRATION_REQUIRED, description: 'non-empty v4.4 projection must be migrated before state-backed tools can run' }
66
72
  - { code: STATE_DB_LOCKED, description: 'database is locked', retriable: true }
67
73
  cli_subcommand: task create
@@ -105,6 +111,9 @@ tools:
105
111
  - { code: TASK_NOT_FOUND, description: 'no task with given id' }
106
112
  - { code: ILLEGAL_STATUS_TRANSITION, description: 'transition rejected by state machine' }
107
113
  - { code: VALIDATION_ERROR, description: 'patch failed schema validation' }
114
+ - { code: CHANGE_NOT_FOUND, description: 'assigned change does not exist' }
115
+ - { code: CHANGE_NOT_MUTABLE, description: 'assigned change no longer accepts task updates' }
116
+ - { code: TASK_CHANGE_OWNERSHIP_MISMATCH, description: 'task ownership conflicts with its parent or children' }
108
117
  - { code: LEGACY_STATE_MIGRATION_REQUIRED, description: 'non-empty v4.4 projection must be migrated before state-backed tools can run' }
109
118
  cli_subcommand: task update
110
119
  emits_events: [task_started, task_completed, task_blocked, task_stale_marked]
@@ -217,9 +226,9 @@ tools:
217
226
  - name: task.init_project
218
227
  family: task
219
228
  description: |
220
- Bootstrap a fresh `.ultra/` skeleton in target_dir from the bundled
221
- templates/.ultra/ source. Copies spec templates, tasks.json, context
222
- template, and report stubs. Phase 3.1 entry point for /ultra-init.
229
+ Bootstrap `.ultra/` and start an authoritative greenfield or brownfield
230
+ baseline. Auto mode detects meaningful existing project content before
231
+ copying specification templates and creating state.db.
223
232
  phase: '3'
224
233
  writer_role: mcp
225
234
  input_schema:
@@ -240,6 +249,22 @@ tools:
240
249
  stack:
241
250
  type: string
242
251
  description: optional tech stack descriptor (comma-separated for fullstack)
252
+ mode:
253
+ type: string
254
+ enum: [auto, greenfield, brownfield]
255
+ default: auto
256
+ description: auto detects whether meaningful project content already exists
257
+ resume:
258
+ type: boolean
259
+ default: false
260
+ description: preserve an existing `.ultra/`, upgrade its schema, and install only missing current scaffold files
261
+ scope:
262
+ type: array
263
+ minItems: 1
264
+ uniqueItems: true
265
+ items: { type: string, minLength: 1 }
266
+ default: ['.']
267
+ description: project-relative repository roots governed by the initial baseline
243
268
  overwrite:
244
269
  type: boolean
245
270
  default: false
@@ -249,7 +274,7 @@ tools:
249
274
  description: override path to the template root (defaults to bundled templates/.ultra/)
250
275
  output_schema:
251
276
  type: object
252
- required: [created_path, state_db_path, status, copied_files]
277
+ required: [created_path, state_db_path, status, mode, baseline, copied_files, repository_profile]
253
278
  additionalProperties: false
254
279
  properties:
255
280
  created_path:
@@ -260,21 +285,38 @@ tools:
260
285
  description: absolute path to the initialized authoritative SQLite database
261
286
  status:
262
287
  type: string
263
- enum: [created, overwritten]
288
+ enum: [created, overwritten, resumed]
289
+ mode:
290
+ type: string
291
+ enum: [greenfield, brownfield]
292
+ baseline:
293
+ type: object
294
+ required: [id, mode, status, scope]
295
+ additionalProperties: true
264
296
  copied_files:
265
297
  type: array
266
298
  items: { type: string }
267
299
  description: paths relative to created_path
300
+ repository_profile:
301
+ type: object
302
+ description: bounded classification evidence used to route greenfield or brownfield adoption
268
303
  backup_path:
269
304
  type: string
270
305
  description: set only when status=overwritten
306
+ migration_backup_path:
307
+ type: string
308
+ description: pre-migration database backup created while resuming an older schema
271
309
  errors:
272
310
  - { code: ULTRA_DIR_EXISTS, description: '.ultra/ already exists and overwrite=false' }
273
311
  - { code: TEMPLATE_MISSING, description: 'source_template path does not exist' }
274
312
  - { code: TARGET_NOT_DIR, description: 'target_dir is not a directory' }
313
+ - { code: BASELINE_SCOPE_MISSING, description: 'a selected baseline scope does not exist' }
314
+ - { code: LEGACY_STATE_MIGRATION_REQUIRED, description: 'projection-only tasks must be imported before resume' }
315
+ - { code: STATE_AUTHORITY_CONFLICT, description: 'projection tasks and authoritative state cannot be reconciled automatically' }
316
+ - { code: STATE_PROJECTION_INVALID, description: 'the existing task projection is malformed' }
275
317
  - { code: IO_ERROR, description: 'filesystem write failed', retriable: true }
276
318
  cli_subcommand: task init-project
277
- emits_events: [project_initialized]
319
+ emits_events: [project_initialized, project_resumed]
278
320
  trace_to: 'PLAN §6 Phase 3.1'
279
321
 
280
322
  - name: task.expand
@@ -300,6 +342,9 @@ tools:
300
342
  errors:
301
343
  - { code: TASK_NOT_FOUND, description: 'parent id missing' }
302
344
  - { code: ALREADY_EXPANDED, description: 'task is already expanded' }
345
+ - { code: CHANGE_NOT_FOUND, description: 'the parent task change does not exist' }
346
+ - { code: CHANGE_NOT_MUTABLE, description: 'the parent task change no longer accepts children' }
347
+ - { code: BASELINE_NOT_READY, description: 'child creation requires a ready baseline or the parent task to belong to an already-authorized active change' }
303
348
  cli_subcommand: task expand
304
349
  emits_events: [task_expanded]
305
350
  trace_to: '§7.3, D34'
@@ -316,6 +361,7 @@ tools:
316
361
  prd_path: { type: string }
317
362
  prd_text: { type: string }
318
363
  tag: { type: string }
364
+ change_id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$', description: 'continuous change unit that will own every persisted parsed task' }
319
365
  dry_run: { type: boolean, default: false, description: 'when true, do not persist to state.db — used by ultra-plan gate to preview before approve' }
320
366
  output_schema:
321
367
  type: object
@@ -327,6 +373,9 @@ tools:
327
373
  errors:
328
374
  - { code: NO_INPUT, description: 'one of prd_path or prd_text required' }
329
375
  - { code: PARSE_FAILED, description: 'PRD could not be parsed into tasks' }
376
+ - { code: CHANGE_NOT_FOUND, description: 'assigned change does not exist' }
377
+ - { code: CHANGE_NOT_MUTABLE, description: 'assigned change no longer accepts tasks' }
378
+ - { code: BASELINE_NOT_READY, description: 'persisting parsed tasks requires a ready baseline or an already-authorized active change' }
330
379
  cli_subcommand: task parse-prd
331
380
  emits_events: [task_created]
332
381
  trace_to: '§7.3, D34'
@@ -629,6 +678,250 @@ tools:
629
678
  emits_events: []
630
679
  trace_to: '§7.3, D31'
631
680
 
681
+ # ───────────────────────── baseline family ─────────────────────────────
682
+ - name: baseline.start
683
+ family: baseline
684
+ description: Start a greenfield baseline or brownfield adoption without overwriting a ready baseline implicitly.
685
+ phase: '11'
686
+ writer_role: mcp
687
+ input_schema:
688
+ type: object
689
+ required: [project_name, mode]
690
+ additionalProperties: false
691
+ allOf:
692
+ - if:
693
+ required: [replace_ready]
694
+ properties: { replace_ready: { const: true } }
695
+ then: { required: [replacement_authorization] }
696
+ - if: { required: [replacement_authorization] }
697
+ then:
698
+ required: [replace_ready]
699
+ properties: { replace_ready: { const: true } }
700
+ properties:
701
+ id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$', default: project-baseline }
702
+ project_name: { type: string, minLength: 1 }
703
+ project_type: { type: string, enum: [web, api, cli, fullstack, other] }
704
+ stack: { type: string }
705
+ mode: { type: string, enum: [greenfield, brownfield] }
706
+ scope: { type: array, minItems: 1, items: { type: string, minLength: 1 }, default: ['.'] }
707
+ repository_revision: { type: string, minLength: 1 }
708
+ replace_ready: { type: boolean, default: false }
709
+ replacement_authorization:
710
+ type: object
711
+ required: [approved_by, reason]
712
+ additionalProperties: false
713
+ properties:
714
+ approved_by: { type: string, minLength: 1 }
715
+ reason: { type: string, minLength: 3 }
716
+ replace_migrated: { type: boolean, default: false, description: explicitly supersede a compatibility-only migrated baseline and start evidence-backed adoption }
717
+ classification: { type: object, description: bounded repository classification evidence; content is limited to 32 KiB }
718
+ provider_refs:
719
+ type: object
720
+ additionalProperties: false
721
+ properties:
722
+ memory:
723
+ type: object
724
+ required: [provider]
725
+ additionalProperties: false
726
+ properties:
727
+ provider: { type: string }
728
+ project: { type: string }
729
+ reference: { type: string }
730
+ revision: { type: string }
731
+ indexed_head: { type: string }
732
+ status: { type: string, enum: [available, unavailable, fresh, stale, unknown] }
733
+ code_graph:
734
+ type: object
735
+ required: [provider]
736
+ additionalProperties: false
737
+ properties:
738
+ provider: { type: string }
739
+ project: { type: string }
740
+ reference: { type: string }
741
+ revision: { type: string }
742
+ indexed_head: { type: string }
743
+ status: { type: string, enum: [available, unavailable, fresh, stale, unknown] }
744
+ output_schema:
745
+ type: object
746
+ required: [baseline]
747
+ additionalProperties: false
748
+ properties:
749
+ baseline: { type: object }
750
+ errors:
751
+ - { code: BASELINE_EXISTS, description: 'a ready baseline requires explicit replacement' }
752
+ - { code: BASELINE_IN_PROGRESS, description: 'another adoption is already active' }
753
+ - { code: BASELINE_REPLACEMENT_AUTHORIZATION_REQUIRED, description: 'ready-baseline replacement lacks durable owner approval and rationale' }
754
+ - { code: BASELINE_REPLACEMENT_INVALID, description: 'replacement flags do not match the current baseline lifecycle or authorization payload' }
755
+ - { code: BASELINE_SCOPE_MISSING, description: 'a selected baseline scope does not exist' }
756
+ - { code: PROVIDER_CONTENT_FORBIDDEN, description: 'provider payload content must remain external' }
757
+ cli_subcommand: baseline start
758
+ emits_events: [baseline_started]
759
+
760
+ - name: baseline.record
761
+ family: baseline
762
+ description: Replace the current adoption snapshot with bounded specification digests, evidence references, verification, unknowns, and provider metadata.
763
+ phase: '11'
764
+ writer_role: mcp
765
+ input_schema:
766
+ type: object
767
+ required: [id]
768
+ additionalProperties: false
769
+ properties:
770
+ id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$' }
771
+ repository_revision: { type: string, minLength: 1 }
772
+ scope: { type: array, minItems: 1, items: { type: string, minLength: 1 } }
773
+ spec_refs:
774
+ type: array
775
+ items:
776
+ type: object
777
+ required: [kind, path]
778
+ additionalProperties: false
779
+ properties:
780
+ kind: { type: string, enum: [discovery, product, architecture, quality, delivery, other] }
781
+ path: { type: string, minLength: 1 }
782
+ evidence:
783
+ type: array
784
+ items:
785
+ type: object
786
+ required: [kind, ref, summary]
787
+ additionalProperties: false
788
+ properties:
789
+ kind: { type: string, enum: [source, docs, runtime, test, deploy, external] }
790
+ ref: { type: string, minLength: 1 }
791
+ summary: { type: string, minLength: 1 }
792
+ verification:
793
+ type: array
794
+ items:
795
+ type: object
796
+ required: [name, command, status, evidence]
797
+ additionalProperties: false
798
+ properties:
799
+ name: { type: string, minLength: 1 }
800
+ command: { type: string, minLength: 1 }
801
+ status: { type: string, enum: [pass, known_red, not_run] }
802
+ evidence: { type: string, minLength: 1 }
803
+ rationale: { type: string, minLength: 1 }
804
+ unknowns:
805
+ type: array
806
+ items:
807
+ type: object
808
+ required: [summary, blocking]
809
+ additionalProperties: false
810
+ properties:
811
+ summary: { type: string, minLength: 1 }
812
+ blocking: { type: boolean }
813
+ owner: { type: string, minLength: 1 }
814
+ gaps:
815
+ type: array
816
+ items:
817
+ type: object
818
+ required: [id, category, summary]
819
+ additionalProperties: false
820
+ properties:
821
+ id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$' }
822
+ category: { type: string, enum: [baseline_blocker, documentation_drift, known_defect, technical_debt, unknown, future_change] }
823
+ status: { type: string, enum: [open, accepted, resolved, deferred], default: open }
824
+ blocking: { type: boolean, default: false }
825
+ summary: { type: string, minLength: 1 }
826
+ evidence_refs: { type: array, uniqueItems: true, items: { type: string, minLength: 1 }, default: [] }
827
+ owner: { type: ['string', 'null'], minLength: 1 }
828
+ resolution: { type: string, minLength: 1 }
829
+ classification: { type: object, description: bounded repository classification evidence; content is limited to 32 KiB }
830
+ provider_refs:
831
+ type: object
832
+ additionalProperties: false
833
+ properties:
834
+ memory:
835
+ type: object
836
+ required: [provider]
837
+ additionalProperties: false
838
+ properties:
839
+ provider: { type: string }
840
+ project: { type: string }
841
+ reference: { type: string }
842
+ revision: { type: string }
843
+ indexed_head: { type: string }
844
+ status: { type: string, enum: [available, unavailable, fresh, stale, unknown] }
845
+ code_graph:
846
+ type: object
847
+ required: [provider]
848
+ additionalProperties: false
849
+ properties:
850
+ provider: { type: string }
851
+ project: { type: string }
852
+ reference: { type: string }
853
+ revision: { type: string }
854
+ indexed_head: { type: string }
855
+ status: { type: string, enum: [available, unavailable, fresh, stale, unknown] }
856
+ output_schema:
857
+ type: object
858
+ required: [baseline]
859
+ additionalProperties: false
860
+ properties:
861
+ baseline: { type: object }
862
+ errors:
863
+ - { code: BASELINE_NOT_FOUND, description: 'baseline id does not exist' }
864
+ - { code: BASELINE_NOT_MUTABLE, description: 'ready and superseded baselines are immutable' }
865
+ - { code: BASELINE_SPEC_MISSING, description: 'a declared specification file does not exist' }
866
+ - { code: BASELINE_SCOPE_MISSING, description: 'a selected baseline scope does not exist' }
867
+ - { code: PROVIDER_CONTENT_FORBIDDEN, description: 'provider payload content must remain external' }
868
+ cli_subcommand: baseline record
869
+ emits_events: [baseline_recorded]
870
+
871
+ - name: baseline.get
872
+ family: baseline
873
+ description: Read the current or named authoritative baseline with live digest and repository-revision health.
874
+ phase: '11'
875
+ readonly: true
876
+ input_schema:
877
+ type: object
878
+ additionalProperties: false
879
+ properties:
880
+ id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$' }
881
+ output_schema:
882
+ type: object
883
+ required: [baseline, health]
884
+ additionalProperties: false
885
+ properties:
886
+ baseline: { type: object }
887
+ health: { type: object }
888
+ errors:
889
+ - { code: BASELINE_NOT_FOUND, description: 'no matching baseline exists' }
890
+ cli_subcommand: baseline get
891
+ emits_events: []
892
+
893
+ - name: baseline.converge
894
+ family: baseline
895
+ description: Approve a baseline only when its revision, specification digests, evidence, verification, and blocking unknowns satisfy deterministic gates.
896
+ phase: '11'
897
+ writer_role: mcp
898
+ input_schema:
899
+ type: object
900
+ required: [id, expected_revision, approved_by, approval_note]
901
+ additionalProperties: false
902
+ properties:
903
+ id: { type: string, pattern: '^[a-zA-Z0-9_\-]+$' }
904
+ expected_revision: { type: string, minLength: 1 }
905
+ approved_by: { type: string, minLength: 1 }
906
+ approval_note: { type: string, minLength: 3 }
907
+ accept_known_red: { type: boolean, default: false }
908
+ accept_dirty_worktree: { type: boolean, default: false, description: explicitly freeze the exact recorded dirty-worktree digest into the baseline }
909
+ output_schema:
910
+ type: object
911
+ required: [ready, status, blockers, baseline]
912
+ additionalProperties: false
913
+ properties:
914
+ ready: { type: boolean }
915
+ status: { type: string, enum: [blocked, ready] }
916
+ blockers: { type: array, items: { type: string } }
917
+ baseline: { type: object }
918
+ errors:
919
+ - { code: BASELINE_NOT_FOUND, description: 'baseline id does not exist' }
920
+ - { code: BASELINE_NOT_CONVERGEABLE, description: 'baseline lifecycle does not allow convergence' }
921
+ - { code: BASELINE_NOT_READY, description: 'baseline evidence, gaps, revision, branch, worktree, or verification still blocks convergence' }
922
+ cli_subcommand: baseline converge
923
+ emits_events: [baseline_blocked, baseline_converged]
924
+
632
925
  # ────────────────────────── change family ──────────────────────────────
633
926
  - name: change.create
634
927
  family: change
@@ -645,6 +938,14 @@ tools:
645
938
  kind: { type: string, enum: [quick, standard, major, incident] }
646
939
  intent: { type: string, minLength: 3 }
647
940
  base_commit: { type: string, pattern: '^[0-9a-f]{7,40}$' }
941
+ baseline_bypass:
942
+ type: object
943
+ required: [reason, approved_by]
944
+ additionalProperties: false
945
+ properties:
946
+ reason: { type: string, minLength: 3 }
947
+ approved_by: { type: string, minLength: 1 }
948
+ description: incident-only break-glass approval when the project baseline is unhealthy
648
949
  docs_impact:
649
950
  type: object
650
951
  additionalProperties: false
@@ -690,6 +991,8 @@ tools:
690
991
  - { code: DUPLICATE_CHANGE_ID, description: 'change id already exists' }
691
992
  - { code: PROVIDER_CONTENT_FORBIDDEN, description: 'provider payload content must remain external' }
692
993
  - { code: VALIDATION_ERROR, description: 'change input failed validation' }
994
+ - { code: BASELINE_NOT_READY, description: 'ordinary work is blocked until the project baseline is healthy' }
995
+ - { code: BASELINE_BYPASS_REQUIRED, description: 'an incident on an unhealthy baseline requires explicit break-glass approval' }
693
996
  cli_subcommand: change create
694
997
  emits_events: [change_created, context_updated]
695
998
 
@@ -1048,6 +1351,8 @@ tools:
1048
1351
  errors:
1049
1352
  - { code: CHANGE_NOT_READY, description: 'change has not passed convergence' }
1050
1353
  - { code: BASELINE_RECONCILIATION_REQUIRED, description: 'baseline update or explicit no-change reason required' }
1354
+ - { code: BASELINE_FILE_MISSING, description: 'a declared baseline update does not exist' }
1355
+ - { code: BASELINE_RECONCILIATION_INCOMPLETE, description: 'repository revision or tracked specification digests remain stale after reconciliation' }
1051
1356
  - { code: ARCHIVE_EXISTS, description: 'archive target already exists' }
1052
1357
  cli_subcommand: change archive
1053
1358
  emits_events: [change_archived]
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "family": {
62
62
  "type": "string",
63
- "enum": ["task", "memory", "review", "impact", "skill", "session", "ask", "plan", "change", "system"]
63
+ "enum": ["task", "memory", "review", "impact", "skill", "session", "ask", "plan", "baseline", "change", "system"]
64
64
  },
65
65
  "description": { "type": "string", "minLength": 10 },
66
66
  "phase": {
@@ -1,9 +1,9 @@
1
1
  -- Ultra Builder Pro — authoritative state schema
2
2
  -- Phase 2 builds .ultra/state.db from this file. Source of truth for all
3
- -- task / session / event / telemetry data. tasks.json and context md status
3
+ -- baseline / change / task / session / event / telemetry data. tasks.json and context md status
4
4
  -- header are projections (PLAN §7.1, D18, D32).
5
5
  --
6
- -- Trace: docs/PLAN.zh-CN.md §7.1; decisions D18/D30/D31/D32/D37.
6
+ -- Trace: docs/PLAN.zh-CN.md §7.1; decisions D18/D30/D31/D32/D37/D52/D54.
7
7
  --
8
8
  -- PRAGMAs are applied by mcp-server/lib/state-db.ts on connection open;
9
9
  -- they cannot be persisted in CREATE statements but are documented here.
@@ -12,6 +12,43 @@
12
12
  -- PRAGMA busy_timeout=5000; -- block up to 5s on lock (R25)
13
13
  -- PRAGMA foreign_keys=ON; -- enforce FK constraints
14
14
 
15
+ -- ──────────────────────────── project baseline ─────────────────────────────
16
+ -- The baseline is the approved, repository-scoped snapshot that every change
17
+ -- is measured against. It stores digests and evidence references, never source,
18
+ -- prompt, transcript, memory, or code-graph payloads.
19
+ CREATE TABLE IF NOT EXISTS baselines (
20
+ id TEXT PRIMARY KEY,
21
+ project_name TEXT NOT NULL,
22
+ project_type TEXT,
23
+ stack TEXT,
24
+ mode TEXT NOT NULL CHECK (mode IN ('greenfield', 'brownfield', 'migrated')),
25
+ status TEXT NOT NULL DEFAULT 'draft'
26
+ CHECK (status IN ('draft', 'adopting', 'blocked', 'ready', 'superseded')),
27
+ repository_root TEXT NOT NULL DEFAULT '.',
28
+ scope_json TEXT NOT NULL DEFAULT '["."]',
29
+ repository_revision TEXT,
30
+ repository_branch TEXT,
31
+ worktree_state TEXT NOT NULL DEFAULT 'unavailable'
32
+ CHECK (worktree_state IN ('clean', 'dirty', 'unavailable')),
33
+ worktree_digest TEXT,
34
+ worktree_files_json TEXT NOT NULL DEFAULT '[]',
35
+ worktree_accepted INTEGER NOT NULL DEFAULT 0 CHECK (worktree_accepted IN (0, 1)),
36
+ spec_refs_json TEXT NOT NULL DEFAULT '[]',
37
+ evidence_json TEXT NOT NULL DEFAULT '[]',
38
+ verification_json TEXT NOT NULL DEFAULT '[]',
39
+ unknowns_json TEXT NOT NULL DEFAULT '[]',
40
+ gaps_json TEXT NOT NULL DEFAULT '[]',
41
+ classification_json TEXT NOT NULL DEFAULT '{}',
42
+ provider_refs_json TEXT NOT NULL DEFAULT '{}',
43
+ approved_by TEXT,
44
+ approval_note TEXT,
45
+ started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
46
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
47
+ converged_at TEXT
48
+ );
49
+
50
+ CREATE INDEX IF NOT EXISTS baselines_status ON baselines(status, updated_at);
51
+
15
52
  -- ──────────────────────────── changes ─────────────────────────────────────
16
53
  -- A project baseline is continuous; each feature, fix, incident, or redesign
17
54
  -- is an independently converged change unit. Only changes become terminal.
@@ -24,6 +61,7 @@ CREATE TABLE IF NOT EXISTS changes (
24
61
  intent TEXT NOT NULL,
25
62
  docs_impact_json TEXT NOT NULL DEFAULT '{"status":"unknown","files":[],"rationale":null}',
26
63
  provider_refs_json TEXT NOT NULL DEFAULT '{}',
64
+ baseline_bypass_json TEXT,
27
65
  base_commit TEXT,
28
66
  artifact_root TEXT NOT NULL,
29
67
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
@@ -305,3 +343,7 @@ INSERT OR IGNORE INTO schema_version (version, description)
305
343
  VALUES ('9.1', 'Kimi runtime support in durable events and sessions');
306
344
  INSERT OR IGNORE INTO schema_version (version, description)
307
345
  VALUES ('10.0', 'Role-scoped context snapshots, deterministic breadcrumbs, and approval-gated specification learning');
346
+ INSERT OR IGNORE INTO schema_version (version, description)
347
+ VALUES ('11.0', 'Greenfield and brownfield baseline adoption, convergence, and drift authority');
348
+ INSERT OR IGNORE INTO schema_version (version, description)
349
+ VALUES ('12.0', 'Evidence-backed repository snapshots, gap ledger, safe re-adoption, and incident break-glass governance');
@@ -20,7 +20,7 @@ const path = require('node:path');
20
20
 
21
21
  const VERSION = (() => {
22
22
  try {
23
- const pkg = require(path.join(__dirname, '..', 'package.json'));
23
+ const pkg = require('../package.json');
24
24
  return pkg.version;
25
25
  } catch (_err) {
26
26
  return '0.1.0';
@@ -35,10 +35,10 @@ USAGE:
35
35
  SUBCOMMANDS:
36
36
  task init-project
37
37
  session close | get | list | admission | heartbeat | subscribe | reap
38
- system doctor [--repair]
38
+ system doctor [--repair] | restore | rebaseline
39
39
  status [--cost] [--since <duration>] [--json]
40
40
  db init | checkpoint | vacuum | integrity | backup (Phase 2)
41
- migrate --from=4.4 --to=4.5 [--dry|--rollback] (Phase 2)
41
+ migrate --from=<4.4|4.5> --to=<4.5|12.0> [--dry|--rollback]
42
42
  legacy-memory inspect | archive | prune --confirm DELETE_ULTRA_LEGACY_MEMORY
43
43
 
44
44
  --help / -h show this message
@@ -68,7 +68,11 @@ const statusCommand = require('./commands/status.cjs');
68
68
  const legacyMemoryCommand = require('./commands/legacy-memory.cjs');
69
69
  const systemCommand = require('./commands/system.cjs');
70
70
 
71
- // Phase 6.2 — CLI telemetry: best-effort, never blocks the subcommand.
71
+ // Session commands run only against healthy authority and may emit best-effort
72
+ // telemetry. Initialization, diagnosis, migration, and recovery commands must
73
+ // never open state.db before their own evidence-preserving checks.
74
+ const TELEMETRY_SUBCOMMANDS = new Set(['session']);
75
+
72
76
  function emitCliTelemetry(sub, rest) {
73
77
  try {
74
78
  const dbPath = path.resolve('.ultra', 'state.db');
@@ -112,7 +116,7 @@ function main(argv) {
112
116
  const [sub, ...rest] = args;
113
117
  const handler = SUBCOMMANDS[sub];
114
118
  if (!handler) fail(`unknown subcommand: ${sub}\n\n${USAGE}`);
115
- emitCliTelemetry(sub, rest);
119
+ if (TELEMETRY_SUBCOMMANDS.has(sub)) emitCliTelemetry(sub, rest);
116
120
  handler(rest);
117
121
  }
118
122