ultra-builder-pro-cli 0.26.2 → 3.0.2

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 (67) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +64 -29
  2. package/.ultra-template/north-star.md +35 -18
  3. package/.ultra-template/tasks.json +1 -0
  4. package/.ultra-template/test-report.json +17 -1
  5. package/CHANGELOG.md +163 -1
  6. package/README.md +108 -38
  7. package/adapters/_shared/host-profile.cjs +68 -12
  8. package/adapters/_shared/plugin-core.cjs +4 -5
  9. package/adapters/_shared/runtime-assets.cjs +11 -2
  10. package/adapters/zcode.js +375 -0
  11. package/bin/delegate-worker.cjs +20 -0
  12. package/bin/delegate.cjs +94 -25
  13. package/bin/install.js +11 -6
  14. package/docs/ARCHITECTURE.md +84 -23
  15. package/docs/ARTIFACT-AUTHORITY.md +167 -22
  16. package/docs/DECISIONS.md +131 -17
  17. package/docs/PHILOSOPHY.md +58 -7
  18. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -0
  19. package/docs/RUNTIME-COMPAT-MATRIX.md +106 -13
  20. package/docs/SKILL-AUTHORING.md +4 -3
  21. package/docs/WORKFLOW-LIFECYCLE.md +126 -28
  22. package/docs/evals/adversarial-review-2026-08-14.md +84 -0
  23. package/docs/evals/zcode-automation-2026-08-14.md +103 -0
  24. package/hooks/README.md +27 -4
  25. package/hooks/_common.py +1678 -61
  26. package/hooks/adapters/zcode.py +227 -0
  27. package/hooks/compact_context.py +176 -17
  28. package/hooks/mid_workflow_recall.py +27 -5
  29. package/hooks/post_edit_guard.py +133 -23
  30. package/hooks/session_context.py +707 -22
  31. package/package.json +6 -3
  32. package/skills/ultra-change/SKILL.md +40 -9
  33. package/skills/ultra-change/references/change-contract.md +93 -27
  34. package/skills/ultra-change/references/execution-grant.md +109 -0
  35. package/skills/ultra-change/references/primary-transfer.md +273 -0
  36. package/skills/ultra-change/scripts/validate_primary_transfer.cjs +1928 -0
  37. package/skills/ultra-delegate/SKILL.md +24 -2
  38. package/skills/ultra-delegate/references/delegation-contract.md +21 -1
  39. package/skills/ultra-deliver/SKILL.md +85 -11
  40. package/skills/ultra-dev/SKILL.md +171 -41
  41. package/skills/ultra-grilling/SKILL.md +10 -7
  42. package/skills/ultra-grilling/references/reframing.md +7 -3
  43. package/skills/ultra-init/SKILL.md +44 -7
  44. package/skills/ultra-init/scripts/init_project.cjs +968 -21
  45. package/skills/ultra-plan/SKILL.md +70 -44
  46. package/skills/ultra-plan/references/task-evidence-v2.md +300 -0
  47. package/skills/ultra-plan/scripts/validate_task_evidence.cjs +859 -0
  48. package/skills/ultra-research/SKILL.md +38 -10
  49. package/skills/ultra-research/references/04-product-strategy.md +11 -2
  50. package/skills/ultra-research/references/21-features-scope.md +8 -0
  51. package/skills/ultra-research/references/22-success-metrics.md +4 -3
  52. package/skills/ultra-research/references/99-synthesis.md +19 -2
  53. package/skills/ultra-research/references/north-star-v2.md +168 -0
  54. package/skills/ultra-research/references/wayfinding.md +8 -0
  55. package/skills/ultra-research/scripts/validate_north_star.cjs +1174 -0
  56. package/skills/ultra-review/SKILL.md +201 -27
  57. package/skills/ultra-review/references/adversarial-evaluation.md +31 -0
  58. package/skills/ultra-review/references/spec.md +5 -0
  59. package/skills/ultra-review/references/unified-schema.md +146 -11
  60. package/skills/ultra-review/references/worker-packet.md +151 -8
  61. package/skills/ultra-review/scripts/review_wait.py +2653 -63
  62. package/skills/ultra-status/SKILL.md +95 -11
  63. package/skills/ultra-tdd/SKILL.md +11 -4
  64. package/skills/ultra-tdd/references/test-execution.md +6 -1
  65. package/skills/ultra-test/SKILL.md +145 -17
  66. package/skills/ultra-test/scripts/validate_review_transport.cjs +549 -0
  67. package/skills/ultra-test/scripts/worktree_digest.cjs +518 -30
@@ -1,20 +1,578 @@
1
1
  #!/usr/bin/env python3
2
2
  """Inject the accepted North Star or its pre-Research fallback plus task acceptance."""
3
3
 
4
- from _common import current_task, emit_context, markdown_section, project_root, read_payload, task_sections
4
+ import hashlib
5
+ import json
6
+ import re
7
+ import shutil
8
+ import subprocess
9
+ import threading
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from _common import (
15
+ RESUME_NAVIGATION_LIMITATION,
16
+ current_task_selection,
17
+ emit_context,
18
+ find_north_star_validator,
19
+ markdown_section,
20
+ project_root,
21
+ read_payload,
22
+ read_stable_project_file_snapshot,
23
+ render_task_diagnostics,
24
+ task_sections,
25
+ )
26
+
27
+
28
+ NORTH_STAR_SESSIONSTART_BYTE_CEILING = 32 * 1024
29
+ NORTH_STAR_SNAPSHOT_MAX_BYTES = 8 * 1024 * 1024
30
+ PROJECT_BRIEF_SNAPSHOT_MAX_BYTES = 8 * 1024 * 1024
31
+ NORTH_STAR_VALIDATOR_TIMEOUT_SECONDS = 10.0
32
+ NORTH_STAR_VALIDATOR_MAX_OUTPUT_BYTES = 256 * 1024
33
+ ACCEPTED_NORTH_STAR_SECTIONS = (
34
+ "Acceptance and Revision",
35
+ "Problem Reality",
36
+ "First-Principle Propositions",
37
+ "Value Causal Chain",
38
+ "North Star Outcomes",
39
+ "Hard Constraints",
40
+ "Explicit Exclusions",
41
+ "Uncertainties and Revisit Triggers",
42
+ "Research Trace",
43
+ )
44
+ ACCEPTED_ID_SECTIONS = {
45
+ "FP": "First-Principle Propositions",
46
+ "NS": "North Star Outcomes",
47
+ "HC": "Hard Constraints",
48
+ }
5
49
 
6
50
 
7
51
  def settled_section(text: str) -> str:
8
52
  """Return an explicitly populated section, not a packaged unresolved marker."""
9
- if not text or "[NEEDS CLARIFICATION]" in text or "not yet defined" in text.lower():
53
+ if (
54
+ not text
55
+ or "[NEEDS CLARIFICATION]" in text
56
+ or "[NEEDS RESEARCH" in text
57
+ or "not yet defined" in text.lower()
58
+ ):
10
59
  return ""
11
60
  return text.strip()
12
61
 
13
62
 
14
- def read_text(file) -> str:
63
+ def _validator_failure(code: str, message: str, north_star_file: Path) -> dict[str, Any]:
64
+ return {
65
+ "$schema": "ultra-north-star-validation-v1",
66
+ "path": str(north_star_file.absolute()),
67
+ "kind": "unknown",
68
+ "status": None,
69
+ "classification": "unknown",
70
+ "valid": False,
71
+ "ids": {"FP": [], "NS": [], "HC": []},
72
+ "sections": {},
73
+ "input": None,
74
+ "diagnostics": [{
75
+ "code": code,
76
+ "severity": "error",
77
+ "message": message,
78
+ "location": None,
79
+ }],
80
+ }
81
+
82
+
83
+ def _accepted_report_has_structural_projection(
84
+ report: dict[str, Any],
85
+ north_star_bytes: bytes,
86
+ ) -> bool:
87
+ """Validate the accepted report's exact byte projection, not its semantics."""
88
+ ids = report.get("ids")
89
+ sections = report.get("sections")
90
+ if (
91
+ not isinstance(ids, dict)
92
+ or set(ids) != set(ACCEPTED_ID_SECTIONS)
93
+ or not isinstance(sections, dict)
94
+ or set(sections) != set(ACCEPTED_NORTH_STAR_SECTIONS)
95
+ ):
96
+ return False
97
+
98
+ section_bodies: dict[str, str] = {}
99
+ previous_end = 0
100
+ for heading in ACCEPTED_NORTH_STAR_SECTIONS:
101
+ span = sections.get(heading)
102
+ if not isinstance(span, dict) or set(span) != {"body_start", "body_end"}:
103
+ return False
104
+ start = span.get("body_start")
105
+ end = span.get("body_end")
106
+ if (
107
+ not isinstance(start, int)
108
+ or isinstance(start, bool)
109
+ or not isinstance(end, int)
110
+ or isinstance(end, bool)
111
+ or start < previous_end
112
+ or end <= start
113
+ or end > len(north_star_bytes)
114
+ ):
115
+ return False
116
+ try:
117
+ section_bodies[heading] = north_star_bytes[start:end].decode("utf-8")
118
+ except UnicodeDecodeError:
119
+ return False
120
+ previous_end = end
121
+
122
+ for kind, heading in ACCEPTED_ID_SECTIONS.items():
123
+ projected_ids = ids.get(kind)
124
+ if not isinstance(projected_ids, list) or not projected_ids:
125
+ return False
126
+ resolved_ids = re.findall(
127
+ rf"^### ({kind}-[A-Za-z0-9][A-Za-z0-9._-]*)\b[^\n]*$",
128
+ section_bodies[heading],
129
+ re.MULTILINE,
130
+ )
131
+ if projected_ids != resolved_ids or len(projected_ids) != len(set(projected_ids)):
132
+ return False
133
+ return True
134
+
135
+
136
+ def _accepted_report_is_mechanically_bound(
137
+ report: dict[str, Any],
138
+ north_star_bytes: bytes,
139
+ expected_digest: str,
140
+ ) -> bool:
141
+ """Check the exact accepted-publication fields without judging their meaning."""
142
+ revision = report.get("revision")
143
+ binding = report.get("acceptance_binding")
144
+ observations = report.get("source_observations")
145
+ if (
146
+ report.get("kind") != "north-star-v2"
147
+ or report.get("status") != "accepted"
148
+ or report.get("classification") != "accepted"
149
+ or not isinstance(revision, str)
150
+ or not revision
151
+ or revision.strip() != revision
152
+ or revision == "none"
153
+ or not isinstance(binding, dict)
154
+ or set(binding) != {"source", "content_sha256", "git_blob_digest", "snapshot"}
155
+ or not isinstance(observations, list)
156
+ or len(observations) != 2
157
+ or not _accepted_report_has_structural_projection(report, north_star_bytes)
158
+ ):
159
+ return False
160
+
161
+ source = binding.get("source")
162
+ snapshot = binding.get("snapshot")
163
+ source_match = (
164
+ re.fullmatch(
165
+ r"(\.ultra/decisions/[A-Za-z0-9][A-Za-z0-9._-]*\.md)#[a-z0-9][a-z0-9-]*",
166
+ source,
167
+ )
168
+ if isinstance(source, str)
169
+ else None
170
+ )
171
+ snapshot_matches = (
172
+ re.fullmatch(
173
+ r"\.ultra/research/[A-Za-z0-9][A-Za-z0-9._-]*/"
174
+ r"[A-Za-z0-9][A-Za-z0-9._-]*\.accepted\.md",
175
+ snapshot,
176
+ )
177
+ if isinstance(snapshot, str)
178
+ else None
179
+ )
180
+ expected_blob_digest = hashlib.sha1(
181
+ f"blob {len(north_star_bytes)}\0".encode("utf-8") + north_star_bytes
182
+ ).hexdigest()
183
+ if (
184
+ source_match is None
185
+ or snapshot_matches is None
186
+ or binding.get("content_sha256") != expected_digest
187
+ or binding.get("git_blob_digest") != expected_blob_digest
188
+ ):
189
+ return False
190
+
191
+ decision_observation, snapshot_observation = observations
192
+ if (
193
+ not isinstance(decision_observation, dict)
194
+ or set(decision_observation) != {"role", "path", "sha256", "byte_length"}
195
+ or decision_observation.get("role") != "decision"
196
+ or decision_observation.get("path") != source_match.group(1)
197
+ or not isinstance(decision_observation.get("sha256"), str)
198
+ or re.fullmatch(r"[0-9a-f]{64}", decision_observation["sha256"]) is None
199
+ or not isinstance(decision_observation.get("byte_length"), int)
200
+ or isinstance(decision_observation.get("byte_length"), bool)
201
+ or decision_observation["byte_length"] <= 0
202
+ or not isinstance(snapshot_observation, dict)
203
+ or set(snapshot_observation) != {"role", "path", "sha256", "byte_length"}
204
+ or snapshot_observation.get("role") != "snapshot"
205
+ or snapshot_observation.get("path") != snapshot
206
+ or snapshot_observation.get("sha256") != expected_digest
207
+ or snapshot_observation.get("byte_length") != len(north_star_bytes)
208
+ ):
209
+ return False
210
+ return True
211
+
212
+
213
+ def read_north_star_snapshot(
214
+ root: Path,
215
+ north_star_file: Path,
216
+ *,
217
+ max_bytes: int = NORTH_STAR_SNAPSHOT_MAX_BYTES,
218
+ ) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
219
+ """Adapt the shared file snapshot diagnostic to the canonical validator report."""
15
220
  try:
16
- return file.read_text(encoding="utf-8")
221
+ relative = north_star_file.absolute().relative_to(root.absolute())
222
+ except ValueError:
223
+ return None, _validator_failure(
224
+ "north_star_snapshot_read_error",
225
+ "Canonical North Star path must remain within the repository root.",
226
+ north_star_file,
227
+ )
228
+ snapshot, failure = read_stable_project_file_snapshot(
229
+ root,
230
+ relative,
231
+ max_bytes=max_bytes,
232
+ code_prefix="north_star_snapshot",
233
+ label="Canonical North Star",
234
+ )
235
+ if failure is not None:
236
+ return None, _validator_failure(
237
+ failure["code"],
238
+ failure["message"],
239
+ north_star_file,
240
+ )
241
+ return snapshot, None
242
+
243
+
244
+ def read_project_brief_snapshot(
245
+ root: Path,
246
+ project_brief_file: Path,
247
+ *,
248
+ max_bytes: int = PROJECT_BRIEF_SNAPSHOT_MAX_BYTES,
249
+ ) -> tuple[dict[str, Any] | None, dict[str, str] | None]:
250
+ try:
251
+ relative = project_brief_file.absolute().relative_to(root.absolute())
252
+ except ValueError:
253
+ return None, {
254
+ "code": "project_brief_snapshot_read_error",
255
+ "message": "Canonical Project Brief path must remain within the repository root.",
256
+ "path": str(project_brief_file.absolute()),
257
+ }
258
+ return read_stable_project_file_snapshot(
259
+ root,
260
+ relative,
261
+ max_bytes=max_bytes,
262
+ code_prefix="project_brief_snapshot",
263
+ label="Canonical Project Brief",
264
+ )
265
+
266
+
267
+ def project_brief_text(
268
+ snapshot: dict[str, Any] | None,
269
+ failure: dict[str, str] | None,
270
+ ) -> tuple[str, dict[str, str] | None]:
271
+ if snapshot is None:
272
+ return "", failure
273
+ try:
274
+ return snapshot["bytes"].decode("utf-8"), None
275
+ except UnicodeDecodeError:
276
+ return "", {
277
+ "code": "project_brief_snapshot_invalid_utf8",
278
+ "message": "Canonical Project Brief must be valid UTF-8.",
279
+ "path": snapshot["path"],
280
+ }
281
+
282
+
283
+ def project_brief_repair_note(failure: dict[str, str] | None) -> str:
284
+ code = failure.get("code") if isinstance(failure, dict) else "project_brief_snapshot_read_error"
285
+ return (
286
+ f"Ultra Project Brief fallback is unavailable (`{code}`); no Project Brief bytes "
287
+ "were published. Repair `.ultra/project-brief.md` as a stable UTF-8 regular file "
288
+ "inside the repository, then retry SessionStart."
289
+ )
290
+
291
+
292
+ def _terminate_and_reap(process: subprocess.Popen) -> None:
293
+ """Stop one validator attempt and leave no live child behind."""
294
+ if process.poll() is not None:
295
+ return
296
+ try:
297
+ process.terminate()
17
298
  except OSError:
299
+ pass
300
+ try:
301
+ process.wait(timeout=0.25)
302
+ return
303
+ except (OSError, subprocess.TimeoutExpired):
304
+ pass
305
+ try:
306
+ process.kill()
307
+ process.wait(timeout=0.25)
308
+ except (OSError, subprocess.TimeoutExpired):
309
+ pass
310
+
311
+
312
+ def _read_bounded_process_output(
313
+ process: subprocess.Popen,
314
+ timeout_seconds: float,
315
+ max_output_bytes: int,
316
+ input_bytes: bytes,
317
+ ) -> tuple[bytes, bytes, str | None]:
318
+ """Drain both pipes concurrently while retaining at most one combined byte budget."""
319
+ buffers = {"stdout": bytearray(), "stderr": bytearray()}
320
+ lock = threading.Lock()
321
+ stop = threading.Event()
322
+ changed = threading.Event()
323
+ output_too_large = threading.Event()
324
+ total_bytes = 0
325
+
326
+ def drain(name: str, stream) -> None:
327
+ nonlocal total_bytes
328
+ try:
329
+ while not stop.is_set():
330
+ chunk = stream.read1(8192)
331
+ if not chunk:
332
+ break
333
+ with lock:
334
+ remaining = max(0, max_output_bytes - total_bytes)
335
+ retained = chunk[:remaining]
336
+ buffers[name].extend(retained)
337
+ total_bytes += len(retained)
338
+ if len(chunk) > remaining:
339
+ output_too_large.set()
340
+ stop.set()
341
+ changed.set()
342
+ except OSError:
343
+ # Closing a pipe while terminating the child is an expected cleanup path.
344
+ pass
345
+ finally:
346
+ changed.set()
347
+
348
+ readers = [
349
+ threading.Thread(target=drain, args=("stdout", process.stdout), daemon=True),
350
+ threading.Thread(target=drain, args=("stderr", process.stderr), daemon=True),
351
+ ]
352
+
353
+ def feed_stdin() -> None:
354
+ try:
355
+ process.stdin.write(input_bytes)
356
+ process.stdin.flush()
357
+ except (BrokenPipeError, OSError):
358
+ pass
359
+ finally:
360
+ try:
361
+ process.stdin.close()
362
+ except OSError:
363
+ pass
364
+ changed.set()
365
+
366
+ writer = threading.Thread(target=feed_stdin, daemon=True)
367
+ for reader in readers:
368
+ reader.start()
369
+ writer.start()
370
+
371
+ deadline = time.monotonic() + timeout_seconds
372
+ failure = None
373
+ while process.poll() is None:
374
+ if output_too_large.is_set():
375
+ failure = "output_too_large"
376
+ break
377
+ remaining_time = deadline - time.monotonic()
378
+ if remaining_time <= 0:
379
+ failure = "timeout"
380
+ break
381
+ changed.wait(min(remaining_time, 0.05))
382
+ changed.clear()
383
+
384
+ if failure is not None:
385
+ stop.set()
386
+ _terminate_and_reap(process)
387
+ else:
388
+ process.wait()
389
+
390
+ for reader in readers:
391
+ reader.join(timeout=0.5)
392
+ writer.join(timeout=0.5)
393
+ if output_too_large.is_set():
394
+ failure = "output_too_large"
395
+ for stream in (process.stdin, process.stdout, process.stderr):
396
+ try:
397
+ stream.close()
398
+ except OSError:
399
+ pass
400
+ return bytes(buffers["stdout"]), bytes(buffers["stderr"]), failure
401
+
402
+
403
+ def run_north_star_validator(
404
+ root: Path,
405
+ north_star_file: Path,
406
+ north_star_bytes: bytes,
407
+ *,
408
+ validator: Path | None = None,
409
+ node_binary: Path | str | None = None,
410
+ timeout_seconds: float = NORTH_STAR_VALIDATOR_TIMEOUT_SECONDS,
411
+ max_output_bytes: int = NORTH_STAR_VALIDATOR_MAX_OUTPUT_BYTES,
412
+ ) -> dict[str, Any]:
413
+ """Run the canonical JS validator; every bridge failure is typed and recoverable."""
414
+ if validator is None:
415
+ validator = find_north_star_validator(Path(__file__))
416
+ if validator is None:
417
+ return _validator_failure(
418
+ "north_star_validator_missing",
419
+ "Canonical North Star validator is unavailable in this managed layout.",
420
+ north_star_file,
421
+ )
422
+ try:
423
+ validator = Path(validator).resolve(strict=True)
424
+ except (OSError, RuntimeError):
425
+ return _validator_failure(
426
+ "north_star_validator_missing",
427
+ "Canonical North Star validator does not exist.",
428
+ north_star_file,
429
+ )
430
+ if not validator.is_file():
431
+ return _validator_failure(
432
+ "north_star_validator_missing",
433
+ "Canonical North Star validator is not a regular file.",
434
+ north_star_file,
435
+ )
436
+
437
+ resolved_node = str(node_binary) if node_binary is not None else shutil.which("node")
438
+ if not resolved_node:
439
+ return _validator_failure(
440
+ "north_star_validator_node_missing",
441
+ "Node.js is unavailable, so accepted North Star publication cannot be verified.",
442
+ north_star_file,
443
+ )
444
+ try:
445
+ process = subprocess.Popen(
446
+ [
447
+ resolved_node,
448
+ str(validator),
449
+ "--stdin",
450
+ "--path",
451
+ str(north_star_file.absolute()),
452
+ ],
453
+ cwd=root,
454
+ stdin=subprocess.PIPE,
455
+ stdout=subprocess.PIPE,
456
+ stderr=subprocess.PIPE,
457
+ shell=False,
458
+ )
459
+ except OSError as error:
460
+ return _validator_failure(
461
+ "north_star_validator_execution_error",
462
+ f"Canonical North Star validator could not execute: {error}",
463
+ north_star_file,
464
+ )
465
+
466
+ stdout, _stderr, failure = _read_bounded_process_output(
467
+ process,
468
+ timeout_seconds,
469
+ max_output_bytes,
470
+ north_star_bytes,
471
+ )
472
+ if failure == "timeout":
473
+ return _validator_failure(
474
+ "north_star_validator_timeout",
475
+ "Canonical North Star validation timed out; retry after checking Node.js and the installation.",
476
+ north_star_file,
477
+ )
478
+ if failure == "output_too_large":
479
+ return _validator_failure(
480
+ "north_star_validator_output_too_large",
481
+ "Canonical North Star validator exceeded its bounded output contract.",
482
+ north_star_file,
483
+ )
484
+ try:
485
+ output = stdout.decode("utf-8")
486
+ report = json.loads(output)
487
+ except (UnicodeDecodeError, json.JSONDecodeError):
488
+ return _validator_failure(
489
+ "north_star_validator_invalid_json",
490
+ "Canonical North Star validator returned invalid UTF-8 JSON.",
491
+ north_star_file,
492
+ )
493
+ if (
494
+ not isinstance(report, dict)
495
+ or report.get("$schema") != "ultra-north-star-validation-v1"
496
+ or not isinstance(report.get("valid"), bool)
497
+ or not isinstance(report.get("diagnostics"), list)
498
+ or not isinstance(report.get("sections"), dict)
499
+ or process.returncode not in {0, 1}
500
+ or (report["valid"] and process.returncode != 0)
501
+ or (not report["valid"] and process.returncode != 1)
502
+ ):
503
+ return _validator_failure(
504
+ "north_star_validator_invalid_report",
505
+ "Canonical North Star validator returned an inconsistent report contract.",
506
+ north_star_file,
507
+ )
508
+ expected_path = str(north_star_file.absolute())
509
+ if report.get("path") != expected_path:
510
+ return _validator_failure(
511
+ "north_star_validator_path_mismatch",
512
+ "Canonical North Star validator reported a different authority path.",
513
+ north_star_file,
514
+ )
515
+ expected_digest = hashlib.sha256(north_star_bytes).hexdigest()
516
+ receipt = report.get("input")
517
+ if (
518
+ not isinstance(receipt, dict)
519
+ or receipt.get("path") != expected_path
520
+ or receipt.get("byte_length") != len(north_star_bytes)
521
+ or receipt.get("sha256") != expected_digest
522
+ ):
523
+ return _validator_failure(
524
+ "north_star_validator_input_mismatch",
525
+ "Canonical North Star validator did not bind its verdict to the supplied snapshot bytes.",
526
+ north_star_file,
527
+ )
528
+ claims_accepted_publication = (
529
+ report.get("valid") is True
530
+ and (
531
+ report.get("status") == "accepted"
532
+ or report.get("classification") == "accepted"
533
+ )
534
+ )
535
+ if claims_accepted_publication and not _accepted_report_is_mechanically_bound(
536
+ report,
537
+ north_star_bytes,
538
+ expected_digest,
539
+ ):
540
+ return _validator_failure(
541
+ "north_star_validator_invalid_report",
542
+ "Canonical North Star validator returned an incomplete accepted-publication report.",
543
+ north_star_file,
544
+ )
545
+ return report
546
+
547
+
548
+ def snapshot_section(
549
+ snapshot: dict[str, Any] | None,
550
+ validation: dict[str, Any],
551
+ heading: str,
552
+ ) -> str:
553
+ """Slice one validator-identified section from the exact caller-owned bytes."""
554
+ if snapshot is None:
555
+ return ""
556
+ sections = validation.get("sections")
557
+ span = sections.get(heading) if isinstance(sections, dict) else None
558
+ if not isinstance(span, dict):
559
+ return ""
560
+ start = span.get("body_start")
561
+ end = span.get("body_end")
562
+ data = snapshot["bytes"]
563
+ if (
564
+ not isinstance(start, int)
565
+ or isinstance(start, bool)
566
+ or not isinstance(end, int)
567
+ or isinstance(end, bool)
568
+ or start < 0
569
+ or end < start
570
+ or end > len(data)
571
+ ):
572
+ return ""
573
+ try:
574
+ return data[start:end].decode("utf-8").strip()
575
+ except UnicodeDecodeError:
18
576
  return ""
19
577
 
20
578
 
@@ -24,30 +582,157 @@ def main() -> int:
24
582
  if root is None:
25
583
  return 0
26
584
  ultra = root / ".ultra"
27
- north_star = read_text(ultra / "north-star.md")
28
- project_brief = read_text(ultra / "project-brief.md")
585
+ north_star_file = ultra / "north-star.md"
586
+ snapshot, snapshot_failure = read_north_star_snapshot(root, north_star_file)
587
+ validation = (
588
+ run_north_star_validator(root, north_star_file, snapshot["bytes"])
589
+ if snapshot is not None
590
+ else snapshot_failure
591
+ )
592
+ if validation is None:
593
+ validation = _validator_failure(
594
+ "north_star_snapshot_read_error",
595
+ "Canonical North Star snapshot was unavailable.",
596
+ north_star_file,
597
+ )
29
598
  parts = []
30
- direction = settled_section(markdown_section(north_star, "Project Direction"))
31
- if direction:
32
- baseline = [f"Project Direction:\n{direction}"]
33
- for heading in ("North Star Outcome", "Hard Constraints"):
34
- section = settled_section(markdown_section(north_star, heading))
35
- if section:
36
- baseline.append(f"{heading}:\n{section}")
37
- baseline_text = "\n\n".join(baseline)
38
- parts.append(f"Ultra accepted north star:\n{baseline_text}")
599
+ classification = validation.get("classification", "unknown")
600
+ v2_sections = [
601
+ (heading, snapshot_section(snapshot, validation, heading))
602
+ for heading in (
603
+ "Problem Reality",
604
+ "First-Principle Propositions",
605
+ "North Star Outcomes",
606
+ "Hard Constraints",
607
+ )
608
+ ]
609
+ accepted_publication = (
610
+ validation.get("valid") is True
611
+ and validation.get("kind") == "north-star-v2"
612
+ and validation.get("status") == "accepted"
613
+ and classification == "accepted"
614
+ and all(body for _, body in v2_sections)
615
+ )
616
+ binding = validation.get("acceptance_binding")
617
+ validated_input = validation.get("input")
618
+ expected_digest = binding.get("content_sha256") if isinstance(binding, dict) else None
619
+ current_digest = (
620
+ snapshot["sha256"]
621
+ if snapshot is not None
622
+ else None
623
+ )
624
+ if accepted_publication and current_digest != expected_digest:
625
+ accepted_publication = False
626
+ validation = _validator_failure(
627
+ "north_star_changed_during_validation",
628
+ "North Star bytes changed after canonical validation; retry SessionStart.",
629
+ north_star_file,
630
+ )
631
+ if accepted_publication:
632
+ baseline = "\n\n".join(f"{heading}:\n{body}" for heading, body in v2_sections)
633
+ baseline_bytes = len(baseline.encode("utf-8"))
634
+ if baseline_bytes <= NORTH_STAR_SESSIONSTART_BYTE_CEILING:
635
+ parts.append(f"Ultra accepted North Star v2:\n{baseline}")
636
+ else:
637
+ revision = validation.get("revision") or "unknown"
638
+ status = validation.get("status") or "accepted"
639
+ accepted_snapshot = binding["snapshot"]
640
+ validated_sha256 = validated_input["sha256"]
641
+ validated_byte_length = validated_input["byte_length"]
642
+ parts.append(
643
+ "Ultra accepted North Star v2 requires direct read:\n"
644
+ "- Path: `.ultra/north-star.md`\n"
645
+ f"- Immutable accepted snapshot: `{accepted_snapshot}`\n"
646
+ f"- Revision: `{revision}`\n"
647
+ f"- Status: `{status}`\n"
648
+ f"- Validated North Star SHA-256: `{validated_sha256}`\n"
649
+ f"- Validated North Star byte length: `{validated_byte_length}`\n"
650
+ "- Read the immutable accepted snapshot and verify both values before "
651
+ "using it. Do not treat different bytes as accepted authority.\n"
652
+ f"- Reason: the complete selected canonical sections are {baseline_bytes} "
653
+ f"UTF-8 bytes, exceeding the {NORTH_STAR_SESSIONSTART_BYTE_CEILING}-byte "
654
+ "SessionStart ceiling; semantic truncation is forbidden."
655
+ )
39
656
  else:
40
- brief_line = settled_section(markdown_section(project_brief, "One-line"))
41
- legacy_line = settled_section(markdown_section(north_star, "One-line"))
42
- if brief_line:
43
- parts.append(f"Ultra project brief (Research not yet accepted):\n{brief_line}")
44
- elif legacy_line:
657
+ diagnostics = validation.get("diagnostics")
658
+ first_code = (
659
+ diagnostics[0].get("code")
660
+ if isinstance(diagnostics, list)
661
+ and diagnostics
662
+ and isinstance(diagnostics[0], dict)
663
+ else None
664
+ )
665
+ if first_code and first_code.startswith("north_star_validator_"):
666
+ parts.append(
667
+ f"Ultra North Star validation is unavailable (`{first_code}`); accepted "
668
+ "session authority was not published. Repair or retry the local validator; "
669
+ "the project brief remains the recoverable fallback."
670
+ )
671
+ elif first_code in {"north_star_changed_during_validation", "north_star_snapshot_changed"}:
672
+ parts.append(
673
+ "Ultra North Star changed during validation; accepted session authority was "
674
+ "not published. Retry SessionStart after writes settle."
675
+ )
676
+ elif classification == "accepted":
677
+ parts.append(
678
+ "Ultra North Star v2 publication binding is invalid; it is not accepted "
679
+ "session authority. Repair `.ultra/north-star.md`, its owner decision, or "
680
+ "its immutable accepted snapshot, then rerun Research validation."
681
+ )
682
+ elif validation.get("valid") is False and first_code:
683
+ parts.append(
684
+ f"Ultra North Star validation rejected the current artifact (`{first_code}`); "
685
+ "accepted session authority was not published. Repair it through Research; "
686
+ "the project brief remains the recoverable fallback."
687
+ )
688
+ direction = settled_section(snapshot_section(snapshot, validation, "Project Direction"))
689
+ outcome = settled_section(snapshot_section(snapshot, validation, "North Star Outcome"))
690
+ constraints = settled_section(snapshot_section(snapshot, validation, "Hard Constraints"))
691
+ legacy_line = settled_section(snapshot_section(snapshot, validation, "One-line"))
692
+ if validation.get("valid") is True and classification == "legacy" and direction and outcome and constraints:
693
+ baseline = [f"Project Direction:\n{direction}"]
694
+ baseline.append(f"North Star Outcome:\n{outcome}")
695
+ baseline.append(f"Hard Constraints:\n{constraints}")
696
+ baseline_text = "\n\n".join(baseline)
697
+ parts.append(f"Ultra legacy v0.26 North Star:\n{baseline_text}")
698
+ elif validation.get("valid") is True and classification == "legacy" and legacy_line:
45
699
  parts.append(f"Ultra legacy project intent:\n{legacy_line}")
46
- task = current_task(root)
700
+ else:
701
+ project_brief_file = ultra / "project-brief.md"
702
+ brief_snapshot, brief_failure = read_project_brief_snapshot(
703
+ root,
704
+ project_brief_file,
705
+ )
706
+ project_brief, brief_failure = project_brief_text(
707
+ brief_snapshot,
708
+ brief_failure,
709
+ )
710
+ if brief_failure is not None:
711
+ parts.append(project_brief_repair_note(brief_failure))
712
+ else:
713
+ brief_line = settled_section(markdown_section(project_brief, "One-line"))
714
+ if brief_line:
715
+ parts.append(f"Ultra project brief (Research not yet accepted):\n{brief_line}")
716
+ selection = current_task_selection(root)
717
+ task = selection["task"]
47
718
  if task:
48
- acceptance = task_sections(root, task)["acceptance"]
719
+ sections = task_sections(root, task)
720
+ acceptance = sections["acceptance"]
49
721
  if acceptance:
50
722
  parts.append(f"Current task {task.get('id')} acceptance:\n{acceptance}")
723
+ resume = sections["resume"]
724
+ if resume:
725
+ parts.append(
726
+ f"Current task {task.get('id')} resume note:\n{resume}\n\n"
727
+ + RESUME_NAVIGATION_LIMITATION
728
+ )
729
+ diagnostics = render_task_diagnostics(sections["diagnostics"])
730
+ if diagnostics:
731
+ parts.append(f"Current task {task.get('id')} diagnostics:\n{diagnostics}")
732
+ else:
733
+ diagnostics = render_task_diagnostics(selection["diagnostics"])
734
+ if diagnostics:
735
+ parts.append(f"Task ledger diagnostics:\n{diagnostics}")
51
736
  emit_context("SessionStart", "\n\n".join(parts))
52
737
  return 0
53
738