ultra-builder-pro-cli 0.26.1 → 0.26.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.
- package/.ultra-template/north-star.md +20 -31
- package/.ultra-template/project-brief.md +28 -0
- package/.ultra-template/specs/product.md +4 -0
- package/.ultra-template/specs/research-distillate.md +8 -0
- package/.ultra-template/test-report.json +1 -0
- package/CHANGELOG.md +64 -0
- package/README.md +60 -28
- package/docs/ARCHITECTURE.md +32 -9
- package/docs/ARTIFACT-AUTHORITY.md +42 -13
- package/docs/DECISIONS.md +20 -3
- package/docs/PHILOSOPHY.md +9 -7
- package/docs/SKILL-AUTHORING.md +10 -6
- package/docs/WORKFLOW-LIFECYCLE.md +99 -39
- package/hooks/README.md +7 -2
- package/hooks/_common.py +54 -5
- package/hooks/session_context.py +34 -8
- package/package.json +1 -1
- package/skills/ultra-change/SKILL.md +52 -63
- package/skills/ultra-change/references/change-contract.md +37 -5
- package/skills/ultra-delegate/SKILL.md +14 -3
- package/skills/ultra-deliver/SKILL.md +48 -43
- package/skills/ultra-deliver/references/baseline-reconciliation.md +5 -3
- package/skills/ultra-dev/SKILL.md +8 -8
- package/skills/ultra-grilling/SKILL.md +9 -9
- package/skills/ultra-init/SKILL.md +62 -87
- package/skills/ultra-init/references/brownfield-adoption.md +23 -40
- package/skills/ultra-plan/SKILL.md +29 -18
- package/skills/ultra-research/SKILL.md +91 -68
- package/skills/ultra-research/references/{03-competitive-landscape.md → 03-alternatives.md} +6 -6
- package/skills/ultra-research/references/22-success-metrics.md +8 -4
- package/skills/ultra-research/references/99-synthesis.md +8 -1
- package/skills/ultra-research/references/wayfinding.md +42 -0
- package/skills/ultra-review/SKILL.md +4 -1
- package/skills/ultra-status/SKILL.md +62 -32
- package/skills/ultra-tdd/SKILL.md +5 -2
- package/skills/ultra-test/SKILL.md +26 -12
- package/skills/ultra-test/scripts/worktree_digest.cjs +42 -8
- package/skills/ultra-think/SKILL.md +5 -10
- package/skills/ultra-think/references/interaction-boundary.md +2 -1
package/docs/PHILOSOPHY.md
CHANGED
|
@@ -43,9 +43,11 @@ one or more.
|
|
|
43
43
|
|
|
44
44
|
### C1 — Goal-Always-Present
|
|
45
45
|
|
|
46
|
-
The agent never stops seeing the
|
|
47
|
-
the
|
|
48
|
-
|
|
46
|
+
The agent never stops seeing the current steering contract. Before Research accepts a
|
|
47
|
+
baseline, the session Hook injects the raw Project Brief fallback. After Research, it
|
|
48
|
+
injects the accepted North Star: Project Direction, any settled North Star Outcome, and
|
|
49
|
+
Hard Constraints. Current task acceptance is appended in both cases. The cost of
|
|
50
|
+
repetition is always less than the cost of drift.
|
|
49
51
|
|
|
50
52
|
> **Test**: pick a random in-progress task, ask the agent "what's the acceptance criteria?" — if
|
|
51
53
|
> it cannot answer in one sentence, this commandment is broken.
|
|
@@ -185,15 +187,15 @@ Skill instruction. Do not touch it casually.
|
|
|
185
187
|
|
|
186
188
|
| Consumer | Reads | Source of truth | Failure mode if drifted |
|
|
187
189
|
|---|---|---|---|
|
|
188
|
-
| `session_context` | headings `## One-line`, `##
|
|
189
|
-
| `session_context` | active `in_progress` task → its context | `tasks.json[].context_file` | acceptance
|
|
190
|
+
| `session_context` | North Star headings `## Project Direction`, `## North Star Outcome`, `## Hard Constraints`; Project Brief `## One-line`, or legacy North Star `## One-line` | `.ultra-template/north-star.md`, `.ultra-template/project-brief.md`, and the Hook's legacy-read branch | SessionStart shows no accepted North Star or Project Brief fallback; C1 dies |
|
|
191
|
+
| `session_context` | unique active `change_id` → one matching `in_progress` task, else first dependency-ready `pending` task → its context | active Change directory plus `tasks.json[].change_id`, dependencies, status, and `context_file` | abandoned or blocked acceptance is injected, or current acceptance never surfaces |
|
|
190
192
|
| `mid_workflow_recall` | heading `## Acceptance Criteria` | `.ultra-template/contexts/TEMPLATE.md` | hook injects nothing; C1 dies silently |
|
|
191
193
|
| `ultra-tdd` | path `references/templates/*` | `skills/ultra-tdd/references/templates/` | guidance points at a missing runnable alternative; C2 dies |
|
|
192
|
-
| `post_edit_guard` | writes `.ultra/progress/<task-id>.json` | `tasks.json[].id`, created on demand |
|
|
194
|
+
| `post_edit_guard` | writes `.ultra/progress/<task-id>.json` for the active Change's current task | active Change directory plus `tasks.json[].change_id` and `id`, created on demand | historical work receives current observations; C4 weakens |
|
|
193
195
|
| `ultra-dev` and `post_edit_guard` | 6 keys: `tests_written`, `tests_passed`, `persistence_real`, `feature_flags_audit`, `vertical_slice`, `spec_trace` | the six-dimension table in `skills/ultra-dev/SKILL.md` | sensor and workflow report different evidence |
|
|
194
196
|
| planning and status | marker `[NEEDS CLARIFICATION]` | `.ultra-template/specs/*.md` | unresolved product truth is hidden |
|
|
195
197
|
| `ultra-plan` / `ultra-dev` / hooks | `trace_to` form `.ultra/specs/file.md#anchor` | `tasks.json[].trace_to` + GFM heading slugify | traces appear resolved when their heading is absent |
|
|
196
|
-
| `ultra-deliver` / `ultra-status` |
|
|
198
|
+
| `ultra-deliver` / `ultra-status` | fields `change_id`, `task_ids`, `intent_digest`, `git_commit`, and `worktree.diff_digest` | `.ultra/test-report.json` plus `worktree_digest.cjs` | stale Change, intent, task set, or product results pass as current |
|
|
197
199
|
| `compact_context` | `.ultra/.runtime/compact-snapshot.md` | derived from canonical files and Git | compact recovery loses acceleration, not authority |
|
|
198
200
|
| every hook | existence of `.ultra/` | the project directory | Ultra taxes projects that never opted in (see `docs/PLUGIN-ISOLATION-CONTRACT.md`) |
|
|
199
201
|
| `ultra-init` | the project data skeleton | `.ultra-template/` and the exact file list in its Skill | later Skills read missing authority paths; C1 dies |
|
package/docs/SKILL-AUTHORING.md
CHANGED
|
@@ -41,14 +41,15 @@ Every Skill has one outcome-led title and these semantic sections:
|
|
|
41
41
|
## References
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
User workflows
|
|
45
|
-
`context_file` and Resume Note,
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
User workflows first resolve at most one active `change_id`, then read only matching
|
|
45
|
+
rows from `.ultra/tasks.json`, the frontier task's `context_file` and Resume Note,
|
|
46
|
+
`CONTEXT.md`, and relevant decisions. Historical or abandoned rows never become current
|
|
47
|
+
from status alone. They name the files they write and read them back. Completion
|
|
48
|
+
criteria must be observable without a semantic validator.
|
|
48
49
|
|
|
49
50
|
Use positive leading words consistently: tracer bullet, seam, deep module, red,
|
|
50
|
-
frontier, and
|
|
51
|
-
reason for choosing it.
|
|
51
|
+
frontier, and research boundary. Explain a branch by its checkable result, not the
|
|
52
|
+
model's reason for choosing it.
|
|
52
53
|
|
|
53
54
|
## Progressive disclosure
|
|
54
55
|
|
|
@@ -56,6 +57,9 @@ Keep resident `SKILL.md` short enough to load on every invocation. Move focused
|
|
|
56
57
|
into `references/` and deterministic validation or waiting into `scripts/`.
|
|
57
58
|
|
|
58
59
|
- Load one research step or review lens at a time.
|
|
60
|
+
- Keep Research's semantic lenses and optional Wayfinding map inside
|
|
61
|
+
`ultra-research/references/`; they are stages of that public workflow, not reusable
|
|
62
|
+
model-invoked Skills.
|
|
59
63
|
- Keep one canonical copy of grilling, TDD, review, domain language, and autonomy rules.
|
|
60
64
|
- Cross-reference another model-invoked Skill by relative path.
|
|
61
65
|
- Do not hide the primary workflow in a script.
|
|
@@ -6,9 +6,9 @@ criteria; repository files record facts; the host model chooses the route.
|
|
|
6
6
|
## Capability flow
|
|
7
7
|
|
|
8
8
|
```text
|
|
9
|
-
ultra-init
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
ultra-init ──raw Project Brief──► ultra-research ──accepted baseline──► ultra-change
|
|
10
|
+
▲ │
|
|
11
|
+
└──── scoped consequential evidence gap ────┘
|
|
12
12
|
|
|
13
13
|
ultra-change ─► ultra-plan ─► ultra-dev (per task) ─► ultra-test ─► ultra-deliver
|
|
14
14
|
▲ │ │
|
|
@@ -19,51 +19,78 @@ ultra-delegate orthogonal bounded execution on another CLI
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Arrows are recommendations, not automatic invocation. The owner explicitly selects
|
|
22
|
-
each public workflow.
|
|
22
|
+
each public workflow. Delivery archives one stable `change_id`; the next request opens
|
|
23
|
+
a new active id while prior task rows remain in the append-only ledger.
|
|
23
24
|
|
|
24
25
|
## Common entry contract
|
|
25
26
|
|
|
26
27
|
Every user-invoked Skill reads, in order:
|
|
27
28
|
|
|
28
|
-
1. `.ultra/
|
|
29
|
-
2.
|
|
30
|
-
3. `
|
|
31
|
-
4. the
|
|
29
|
+
1. zero or one `.ultra/changes/active/<change_id>/intent.md`; more than one is a conflict;
|
|
30
|
+
2. `.ultra/tasks.json`, selecting only tasks whose `change_id` matches that active id;
|
|
31
|
+
3. the matching frontier task's `context_file` and closing `## Resume Note`;
|
|
32
|
+
4. `.ultra/project-brief.md`, the accepted `.ultra/north-star.md`, `CONTEXT.md`, and
|
|
33
|
+
relevant `.ultra/decisions/`, where those artifacts already exist;
|
|
34
|
+
5. the active Change, specifications, evidence, maintained documentation, and Git state
|
|
35
|
+
relevant to its scope.
|
|
32
36
|
|
|
33
37
|
Missing files produce a precise recovery or initialization recommendation. They do not
|
|
34
38
|
cause the Skill to invent a state transition.
|
|
35
39
|
|
|
36
40
|
## Initialization
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
Init writes the raw Project Brief and stable empty skeleton. `ultra-init` preserves the
|
|
43
|
+
owner's one-line request, broad outline, explicit inputs, and open questions without
|
|
44
|
+
turning them into validated product truth. It initializes Git when absent and may
|
|
45
|
+
preserve repository-observable brownfield facts as Research inputs. It does not define
|
|
46
|
+
success, establish the North Star, create `CONTEXT.md`, populate specifications, or
|
|
47
|
+
launch another workflow.
|
|
42
48
|
|
|
43
49
|
## Research
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
Research turns the brief into an accepted North Star and evidence-backed baseline.
|
|
52
|
+
`ultra-research` uses Wayfinding only when a multi-lens path is unclear, then maps the
|
|
53
|
+
question to the smallest useful subset of seventeen focused references. The first six
|
|
54
|
+
are semantic lenses with real dependencies: `00` precedes `01`; conditional `02` and
|
|
55
|
+
`03` may fan out after the boundary is known; `04` consumes their relevant conclusions
|
|
56
|
+
and is the first owner checkpoint; `05` extracts assumptions from `00` through `04`.
|
|
57
|
+
Grilling controls how missing owner inputs are asked, Think tests one consequential
|
|
58
|
+
decision, and Domain Modeling writes settled vocabulary; none owns the Research route.
|
|
59
|
+
The final synthesis promotes accepted conclusions into the North Star, `CONTEXT.md`,
|
|
60
|
+
three specifications, and a source-hash-bound distillate.
|
|
49
61
|
|
|
50
62
|
## Change reconciliation
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
Change updates only the touched baseline rather than rebuilding it. `ultra-change`
|
|
65
|
+
first compares requested behavior with accepted product truth and current code. It
|
|
66
|
+
classifies changes by the resulting commitments:
|
|
54
67
|
|
|
55
68
|
- expansion and correction may proceed inside accepted intent;
|
|
56
69
|
- a reduction waits for explicit owner authority.
|
|
57
70
|
|
|
58
71
|
The output is one active Change intent with observable outcome, acceptance, seams,
|
|
59
|
-
non-goals, and
|
|
72
|
+
non-goals, trace anchors, and an explicit Research Disposition. `none` cites sufficient
|
|
73
|
+
existing evidence; `bounded` or `required` names the exact question, lenses, and exit
|
|
74
|
+
evidence. Unsatisfied exit evidence routes to Research, then back through Change
|
|
75
|
+
reconciliation before planning when the accepted contract needs an update.
|
|
76
|
+
|
|
77
|
+
A micro edit that makes no specification sentence false stays outside the Ultra
|
|
78
|
+
lifecycle and uses ordinary repository TDD/verification. Once an active Change exists,
|
|
79
|
+
even a `quick` profile receives one minimal Plan task so evidence and delivery remain
|
|
80
|
+
bound to its id. A separate request cannot open a second active Change: finish or
|
|
81
|
+
deliver the current one, abandon it with owner authority, or explicitly reconcile the
|
|
82
|
+
new request into the same stable id.
|
|
60
83
|
|
|
61
84
|
## Planning
|
|
62
85
|
|
|
63
|
-
`ultra-plan` confirms
|
|
64
|
-
one context file per task
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
`ultra-plan` confirms a material scope posture and records public seams, then appends a
|
|
87
|
+
current-Change task graph and one context file per task without deleting historical
|
|
88
|
+
rows. The model chooses ordinary technical seams; the owner is asked only when a seam
|
|
89
|
+
changes a public contract, accepted risk, or another material trade-off. Every task
|
|
90
|
+
stores the stable `change_id`; dependencies stay inside that Change and ids remain
|
|
91
|
+
globally unique. Feature tasks are tracer bullets. A contract task precedes cross-process
|
|
92
|
+
or cross-top-level-boundary implementations; integration checkpoints appear after
|
|
93
|
+
several slices. Cycles and broken trace paths are repaired before handoff.
|
|
67
94
|
|
|
68
95
|
## Task development
|
|
69
96
|
|
|
@@ -78,15 +105,26 @@ duplication or coupling visible.
|
|
|
78
105
|
|
|
79
106
|
## Whole-system audit
|
|
80
107
|
|
|
81
|
-
`ultra-test` runs once after the
|
|
108
|
+
`ultra-test` runs once after tasks for the active Change are complete. It checks anti-patterns, coverage,
|
|
82
109
|
wiring, E2E behavior, performance, and security. It names the exact Git commit and
|
|
83
|
-
|
|
110
|
+
binds the report to `change_id`, exact current task ids, intent SHA-256, and the
|
|
111
|
+
product-worktree digest. `passed: true` implies complete current tasks and no required
|
|
112
|
+
area left `not_run`; task completion does not force a pass, and dispositioned gaps stay
|
|
113
|
+
visible. It never turns an orphan,
|
|
114
|
+
failed command, stub, or omission into permission to alter scope.
|
|
84
115
|
|
|
85
116
|
## Delivery
|
|
86
117
|
|
|
87
|
-
`ultra-deliver` reconciles actual outcome,
|
|
88
|
-
|
|
89
|
-
|
|
118
|
+
`ultra-deliver` uses two observable passes. The first reconciles actual outcome,
|
|
119
|
+
specifications, documentation, aggregate review, omissions, and rollback; if semantic
|
|
120
|
+
or product files change, it stops and recommends a fresh Test. The second begins only
|
|
121
|
+
when report Change/task/intent/HEAD/product identities match, applies the changed-export
|
|
122
|
+
gate, runs build and non-publishing package inspection, records version impact, writes
|
|
123
|
+
delivery metadata, and archives the same stable id with `git mv`. Delivery metadata and
|
|
124
|
+
the directory move do not invalidate their own product snapshot. Producing a release
|
|
125
|
+
package remains blocked while a changed export has neither a non-test consumer nor an
|
|
126
|
+
owner disposition. That disposition is recorded against the finding in the current
|
|
127
|
+
test report without changing the finding or `passed`, then carried into `delivery.md`.
|
|
90
128
|
|
|
91
129
|
Commit, push, tag, publication, release, and deployment are independent external
|
|
92
130
|
effects. Readiness grants none automatically.
|
|
@@ -95,27 +133,44 @@ effects. Readiness grants none automatically.
|
|
|
95
133
|
|
|
96
134
|
`ultra-status` infers the smallest next route from observable files:
|
|
97
135
|
|
|
98
|
-
- no healthy skeleton → init;
|
|
99
|
-
-
|
|
136
|
+
- no healthy skeleton or usable raw brief → init;
|
|
137
|
+
- raw brief without an accepted baseline, or a consequential evidence gap → research;
|
|
100
138
|
- requested outcome without active Change → change;
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
139
|
+
- draft or blocking active intent → change;
|
|
140
|
+
- unsatisfied Research Disposition → research;
|
|
141
|
+
- active Change without matching executable tasks → plan;
|
|
142
|
+
- matching pending or in-progress task → dev;
|
|
143
|
+
- completed current-Change tasks without a matching fresh report → test;
|
|
144
|
+
- false report → route by finding and owner disposition, not by the boolean alone;
|
|
145
|
+
- fresh, dispositioned report and unarchived Change → deliver.
|
|
105
146
|
|
|
106
147
|
It also checks installation provenance, stale reports, old in-progress work, and
|
|
107
148
|
unresolved markers. It does not mutate artifacts.
|
|
108
149
|
|
|
150
|
+
## Delegation
|
|
151
|
+
|
|
152
|
+
`ultra-delegate` is orthogonal to the lifecycle and requires explicit owner authority.
|
|
153
|
+
Its immutable instruction selects task execution/continuation, scoped Research evidence,
|
|
154
|
+
or aggregate Change review/verification. Task execution reads one matching task context;
|
|
155
|
+
the latter two may be read-only and do not require a task, so both pre-Plan evidence and
|
|
156
|
+
post-task review remain reachable. Every scope still binds the one active `change_id`, a
|
|
157
|
+
clean isolated worktree, strict permission/result schemas, and primary-host inspection.
|
|
158
|
+
The worker cannot write canonical `.ultra` files or perform external effects.
|
|
159
|
+
|
|
109
160
|
## Artifact closure
|
|
110
161
|
|
|
111
162
|
Every durable output has a later reader:
|
|
112
163
|
|
|
113
|
-
- init's
|
|
114
|
-
|
|
164
|
+
- init's Project Brief is consumed by Research, Status, and the pre-baseline session
|
|
165
|
+
Hook fallback; its empty skeleton supplies stable paths rather than product claims;
|
|
166
|
+
- Research's accepted North Star and domain/specification baseline are read by every
|
|
167
|
+
later workflow, while selected reports trace into Change reconciliation, planning,
|
|
115
168
|
and delivery;
|
|
116
|
-
- Change intent
|
|
117
|
-
delivery;
|
|
118
|
-
|
|
169
|
+
- active Change intent and its touched specification deltas are consumed through
|
|
170
|
+
planning, development, audit, review, status, and delivery; an abandoned intent has
|
|
171
|
+
an exact `## Abandonment` closure consumed by future Change reconciliation and Status;
|
|
172
|
+
- the append-only task ledger and contexts are the common resume input after filtering
|
|
173
|
+
by active `change_id`, while task evidence feeds
|
|
119
174
|
review, whole-system audit, and delivery;
|
|
120
175
|
- the current test report is consumed by status and delivery;
|
|
121
176
|
- delivery moves with the Change into archive and remains historical evidence.
|
|
@@ -126,6 +181,11 @@ an unowned drift log, Change plan, technical-debt report, or second delivery sum
|
|
|
126
181
|
|
|
127
182
|
## Interruption and cross-host continuation
|
|
128
183
|
|
|
184
|
+
There is one primary writer for canonical `.ultra` files. Review workers are read-only;
|
|
185
|
+
delegated source edits use isolated worktrees and return results for explicit
|
|
186
|
+
integration. Parallel canonical writes are unsupported because silently merging intent,
|
|
187
|
+
task, evidence, or report authority would lose meaning.
|
|
188
|
+
|
|
129
189
|
Before stopping, the active task context records current facts, exact verification,
|
|
130
190
|
open questions, Completion, and a closing Resume Note. The next session or host repeats
|
|
131
191
|
the common entry contract and compares the working tree with those claims.
|
package/hooks/README.md
CHANGED
|
@@ -5,8 +5,8 @@ directory and exits silently when the workflow is not active.
|
|
|
5
5
|
|
|
6
6
|
| Hook | Lifecycle | Purpose |
|
|
7
7
|
|---|---|---|
|
|
8
|
-
| `session_context.py` | Session start | Inject the
|
|
9
|
-
| `mid_workflow_recall.py` | Before source reads or edits | Restate
|
|
8
|
+
| `session_context.py` | Session start | Inject the accepted North Star or Project Brief fallback, plus acceptance from the unique active Change's current task |
|
|
9
|
+
| `mid_workflow_recall.py` | Before source reads or edits | Restate acceptance only for a task matching the unique active `change_id` |
|
|
10
10
|
| `compact_context.py` | Before/after compaction | Save and restore a disposable snapshot derived from files and Git |
|
|
11
11
|
| `post_edit_guard.py` | After edits | Record mechanical evidence observations without deciding completion |
|
|
12
12
|
| `block_dangerous_commands.py` | Before shell execution | Advise on additive protected-branch publication; block history rewrites and a narrow destructive set until the exact command is owner-authorized |
|
|
@@ -14,6 +14,11 @@ directory and exits silently when the workflow is not active.
|
|
|
14
14
|
`_common.py` is a shared library, not a lifecycle registration. Compact snapshots and
|
|
15
15
|
progress observations live under ignored `.ultra/.runtime/` or `.ultra/progress/`; the
|
|
16
16
|
canonical semantic facts remain the owner-readable files and Git.
|
|
17
|
+
Legacy task rows with no `change_id` remain readable from an active-path `change_ref`
|
|
18
|
+
during migration; a present canonical `change_id` always wins, and archived or
|
|
19
|
+
abandoned paths are never revived as current work. Task
|
|
20
|
+
selection prefers the single `in_progress` row, then the first `pending` row whose
|
|
21
|
+
same-Change dependencies are complete; ambiguous concurrent work stays silent.
|
|
17
22
|
|
|
18
23
|
Claude Code runs the hooks directly. Codex, Kimi Code, and Grok Build use the small
|
|
19
24
|
wire adapters under `hooks/adapters/`. OpenCode calls the same scripts from its native
|
package/hooks/_common.py
CHANGED
|
@@ -47,12 +47,61 @@ def read_tasks(root: Path) -> list[dict[str, Any]]:
|
|
|
47
47
|
return [item for item in tasks if isinstance(item, dict)] if isinstance(tasks, list) else []
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def active_change_id(root: Path) -> str | None:
|
|
51
|
+
active = root / ".ultra" / "changes" / "active"
|
|
52
|
+
try:
|
|
53
|
+
change_ids = sorted(
|
|
54
|
+
entry.name
|
|
55
|
+
for entry in active.iterdir()
|
|
56
|
+
if entry.is_dir() and (entry / "intent.md").is_file()
|
|
57
|
+
)
|
|
58
|
+
except OSError:
|
|
59
|
+
return None
|
|
60
|
+
if len(change_ids) != 1:
|
|
61
|
+
return None
|
|
62
|
+
change_id = change_ids[0]
|
|
63
|
+
return change_id if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", change_id) else None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def legacy_task_change_id(task: dict[str, Any]) -> str | None:
|
|
67
|
+
value = task.get("change_ref")
|
|
68
|
+
if not isinstance(value, str):
|
|
69
|
+
return None
|
|
70
|
+
match = re.fullmatch(
|
|
71
|
+
r"(?:\.ultra/)?changes/active/([A-Za-z0-9][A-Za-z0-9._-]*)/intent\.md",
|
|
72
|
+
value,
|
|
73
|
+
)
|
|
74
|
+
return match.group(1) if match else None
|
|
75
|
+
|
|
76
|
+
|
|
50
77
|
def current_task(root: Path) -> dict[str, Any] | None:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
change_id = active_change_id(root)
|
|
79
|
+
if change_id is None:
|
|
80
|
+
return None
|
|
81
|
+
tasks = [
|
|
82
|
+
task for task in read_tasks(root)
|
|
83
|
+
if task.get("change_id") == change_id
|
|
84
|
+
or ("change_id" not in task and legacy_task_change_id(task) == change_id)
|
|
85
|
+
]
|
|
86
|
+
in_progress = [task for task in tasks if task.get("status") == "in_progress"]
|
|
87
|
+
if len(in_progress) == 1:
|
|
88
|
+
return in_progress[0]
|
|
89
|
+
if len(in_progress) > 1:
|
|
90
|
+
return None
|
|
91
|
+
by_id = {task.get("id"): task for task in tasks if isinstance(task.get("id"), str)}
|
|
92
|
+
for task in tasks:
|
|
93
|
+
if task.get("status") != "pending":
|
|
94
|
+
continue
|
|
95
|
+
dependencies = task.get("dependencies", [])
|
|
96
|
+
if not isinstance(dependencies, list):
|
|
97
|
+
continue
|
|
98
|
+
if all(
|
|
99
|
+
isinstance(dependency, str)
|
|
100
|
+
and dependency in by_id
|
|
101
|
+
and by_id[dependency].get("status") == "completed"
|
|
102
|
+
for dependency in dependencies
|
|
103
|
+
):
|
|
104
|
+
return task
|
|
56
105
|
return None
|
|
57
106
|
|
|
58
107
|
|
package/hooks/session_context.py
CHANGED
|
@@ -1,22 +1,48 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Inject the
|
|
2
|
+
"""Inject the accepted North Star or its pre-Research fallback plus task acceptance."""
|
|
3
3
|
|
|
4
4
|
from _common import current_task, emit_context, markdown_section, project_root, read_payload, task_sections
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def settled_section(text: str) -> str:
|
|
8
|
+
"""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():
|
|
10
|
+
return ""
|
|
11
|
+
return text.strip()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_text(file) -> str:
|
|
15
|
+
try:
|
|
16
|
+
return file.read_text(encoding="utf-8")
|
|
17
|
+
except OSError:
|
|
18
|
+
return ""
|
|
19
|
+
|
|
20
|
+
|
|
7
21
|
def main() -> int:
|
|
8
22
|
payload = read_payload()
|
|
9
23
|
root = project_root(payload) # Idle guard: no `.ultra/` means silent exit.
|
|
10
24
|
if root is None:
|
|
11
25
|
return 0
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
north_star = ""
|
|
26
|
+
ultra = root / ".ultra"
|
|
27
|
+
north_star = read_text(ultra / "north-star.md")
|
|
28
|
+
project_brief = read_text(ultra / "project-brief.md")
|
|
16
29
|
parts = []
|
|
17
|
-
|
|
18
|
-
if
|
|
19
|
-
|
|
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}")
|
|
39
|
+
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:
|
|
45
|
+
parts.append(f"Ultra legacy project intent:\n{legacy_line}")
|
|
20
46
|
task = current_task(root)
|
|
21
47
|
if task:
|
|
22
48
|
acceptance = task_sections(root, task)["acceptance"]
|
package/package.json
CHANGED
|
@@ -1,59 +1,56 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ultra-change
|
|
3
|
-
description: Open one bounded unit of work by
|
|
3
|
+
description: Open or reconcile one bounded unit of work by comparing specification promises with actual code, then writing the intent that closes the gap. Use when no Change is active and a feature, fix, redesign, or incident response is requested, or when an active Change's draft, researched evidence, or accepted boundary must be reconciled.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Reconcile first, then write down what this Change commits to
|
|
7
7
|
|
|
8
|
-
A Change
|
|
9
|
-
|
|
10
|
-
intent on top of specifications that already disagree with the code buries the
|
|
11
|
-
disagreement instead of resolving it.
|
|
8
|
+
A Change connects research, plan, implementation, verification, review, and delivery.
|
|
9
|
+
Reconcile first so new intent does not bury an existing specification/code conflict.
|
|
12
10
|
|
|
13
11
|
## Before you start
|
|
14
12
|
|
|
15
|
-
1.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
1. List `.ultra/changes/active/`; more than one `intent.md` is a conflict, not a choice.
|
|
14
|
+
One directory name is the stable `change_id`. Read `.ultra/tasks.json`, then only its
|
|
15
|
+
unfinished `context_file` values. Update the same `change_id`; never open a second active Change.
|
|
16
|
+
2. Read `.ultra/project-brief.md`, `CONTEXT.md`, relevant `.ultra/decisions/`, and
|
|
17
|
+
`.ultra/north-star.md` for raw provenance, accepted language, and direction.
|
|
18
|
+
3. Grep archive and abandoned Changes that touched this area. Prefer archived
|
|
19
|
+
`delivery.md`; otherwise read `archive-summary.md`, `verification.md`, `intent.md`,
|
|
20
|
+
and remaining Markdown. Read abandoned Changes as history of rejected boundaries and
|
|
21
|
+
reusable evidence, never current intent. A missing `delivery.md` does not mean no history;
|
|
22
|
+
repeated modules are an architecture signal.
|
|
23
23
|
|
|
24
24
|
## Whether this skill applies at all
|
|
25
25
|
|
|
26
26
|
| Situation | Where it goes |
|
|
27
27
|
|---|---|
|
|
28
|
-
| No
|
|
28
|
+
| No active Change, and a new bounded request exists | Here — create one stable id |
|
|
29
|
+
| One active Change needs its draft, Research evidence, or accepted boundary reconciled | Here — update the same `change_id` |
|
|
30
|
+
| A separate request arrives while one Change remains active | Do not create a second Change; finish or deliver the current one, abandon it with owner authority, or explicitly fold the request into its boundary |
|
|
29
31
|
| A task is in progress and the specification turns out not to match reality | Not here — the dual-write path inside `ultra-dev` |
|
|
30
|
-
| The change makes no sentence in the specifications false |
|
|
32
|
+
| The change makes no sentence in the specifications false | Outside the Ultra lifecycle — use ordinary repository TDD and verification |
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
If no specification sentence becomes untrue, there is nothing to reconcile. Use
|
|
35
|
+
`../ultra-tdd/SKILL.md` when helpful, but create no Change artifacts or Ultra
|
|
36
|
+
Test/Deliver claim for that micro change.
|
|
35
37
|
|
|
36
38
|
## Definition of done
|
|
37
39
|
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
- The draft stays editable until then; the owner's acceptance is what turns it
|
|
43
|
-
into authority.
|
|
40
|
+
- Active `intent.md` states outcome, executable acceptance, non-goals, public seams,
|
|
41
|
+
and Research Disposition.
|
|
42
|
+
- Every reconciliation bucket is empty or dispositioned with the owner's answer.
|
|
43
|
+
- The draft remains editable until owner acceptance makes it authority.
|
|
44
44
|
|
|
45
45
|
## Reconcile against a bounded scope
|
|
46
46
|
|
|
47
|
-
A
|
|
48
|
-
|
|
47
|
+
A Change does not rebuild the project baseline; reconcile only the touched specification sections
|
|
48
|
+
and leave unrelated gaps visible with four rerunnable steps:
|
|
49
49
|
|
|
50
50
|
1. Decide which specification sections this request touches.
|
|
51
|
-
2.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
last changed.
|
|
55
|
-
4. `git diff <that commit>..HEAD -- <those code files>` is the reconciliation
|
|
56
|
-
scope.
|
|
51
|
+
2. Collect past tasks whose `trace_to` points there and the code files they changed.
|
|
52
|
+
3. Use `git log -- .ultra/specs/<file>` to find its last specification change.
|
|
53
|
+
4. Inspect `git diff <that commit>..HEAD -- <those code files>`.
|
|
57
54
|
|
|
58
55
|
Sort what you find into three buckets, each with your recommended disposition:
|
|
59
56
|
|
|
@@ -63,47 +60,39 @@ Sort what you find into three buckets, each with your recommended disposition:
|
|
|
63
60
|
| Code implements it, no specification says so | Built and unpromised |
|
|
64
61
|
| Both speak and they conflict | Contradictory |
|
|
65
62
|
|
|
66
|
-
Take
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
something already promised goes one at a time with your recommendation; the
|
|
70
|
-
remaining low-risk rows go as a single list with a recommended disposition each,
|
|
71
|
-
for one confirmation. Twenty separate questions about rows that change nothing
|
|
72
|
-
spends the owner's attention where it was not needed, and trains them to approve
|
|
73
|
-
without reading.
|
|
63
|
+
Take non-empty buckets to the owner because disposition can withdraw a commitment.
|
|
64
|
+
Ask one recommended question per `HC-<n>` impact or removed promise; group remaining
|
|
65
|
+
low-risk rows into one recommended list for confirmation.
|
|
74
66
|
|
|
75
67
|
## Write the intent
|
|
76
68
|
|
|
77
|
-
Read `references/change-contract.md` and use its exact
|
|
78
|
-
|
|
79
|
-
its defined artifacts; do not invent a parallel semantic record.
|
|
69
|
+
Read `references/change-contract.md` and use its exact headings. Until planning or
|
|
70
|
+
delivery, the active directory contains only this contract; add no parallel record.
|
|
80
71
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`../ultra-domain-modeling/SKILL.md`.
|
|
72
|
+
Use `../ultra-grilling/SKILL.md` to settle the boundary and
|
|
73
|
+
`../ultra-domain-modeling/SKILL.md` for new domain terms.
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
breakdown. What comes out is a question whose answer is a decision, not an
|
|
88
|
-
implementation slice.
|
|
75
|
+
Use `../ultra-think/SKILL.md` only for one consequential evidenced trade-off. If the
|
|
76
|
+
whole path is unclear, recommend Research instead of manufacturing tasks.
|
|
89
77
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
Record `## Research Disposition` exactly. `none` needs enough evidence to plan;
|
|
79
|
+
`bounded` or `required` names one question, selected lenses, and exit evidence. After
|
|
80
|
+
Research, reconcile outputs here; an unverified path or bare claim is not evidence.
|
|
93
81
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
82
|
+
A quick active Change still goes through one-task `ultra-plan`; once durable intent
|
|
83
|
+
exists, that minimal task is what binds development, evidence, audit, and delivery to
|
|
84
|
+
the stable `change_id`.
|
|
85
|
+
|
|
86
|
+
Output active `intent.md` under a stable id, justified specification patches, qualifying
|
|
87
|
+
decision or vocabulary updates. For an evidence gap recommend `ultra-research`; when
|
|
88
|
+
evidenced recommend `ultra-plan`. Invoke neither.
|
|
98
89
|
|
|
99
90
|
## Abandoning a Change
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
`git mv .ultra/changes/active/<id> .ultra/changes/abandoned/<id
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
a Change kept alive to avoid admitting it was wrong poisons every later
|
|
106
|
-
reconciliation, which then measures the code against promises nobody intends to keep.
|
|
92
|
+
Append the exact `## Abandonment` closure from `references/change-contract.md`, then
|
|
93
|
+
`git mv .ultra/changes/active/<id> .ultra/changes/abandoned/<id>`. Keep completed-task
|
|
94
|
+
evidence. Future Change and Status consume this history; it is neither current intent
|
|
95
|
+
nor an orphan. Abandonment is the owner's call.
|
|
107
96
|
|
|
108
97
|
## When the owner decides
|
|
109
98
|
|