ultra-builder-pro-cli 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ultra-template/changes/active/.gitkeep +1 -0
- package/.ultra-template/changes/archive/.gitkeep +1 -0
- package/CHANGELOG.md +90 -14
- package/README.md +38 -32
- package/adapters/_shared/codex-assets.cjs +32 -151
- package/adapters/_shared/frontmatter.cjs +2 -2
- package/adapters/_shared/handbook.cjs +10 -10
- package/adapters/_shared/path-rewrite.cjs +0 -3
- package/adapters/_shared/runtime-assets.cjs +13 -4
- package/adapters/_shared/tests/handbook.test.cjs +4 -1
- package/adapters/_shared/tests/path-rewrite.test.cjs +1 -3
- package/adapters/_shared/tests/resolve-target.test.cjs +1 -11
- package/adapters/_shared/tests/runtime-assets.test.cjs +7 -6
- package/adapters/opencode.js +82 -26
- package/adapters/tests/claude.test.cjs +1 -4
- package/adapters/tests/codex.test.cjs +9 -8
- package/adapters/tests/opencode.test.cjs +17 -11
- package/bin/install.js +6 -28
- package/bin/orchestrator.js +12 -30
- package/commands/ultra-change.md +47 -0
- package/commands/ultra-deliver.md +14 -7
- package/commands/ultra-dev.md +6 -3
- package/commands/ultra-doctor.md +36 -0
- package/commands/ultra-init.md +4 -3
- package/commands/ultra-status.md +7 -5
- package/commands/ultra-test.md +2 -1
- package/docs/AGENT-CONTEXT.md +35 -28
- package/docs/ARCHITECTURE.md +49 -21
- package/docs/PLAN.zh-CN.md +67 -68
- package/docs/ROADMAP.md +17 -17
- package/docs/RUNTIME-COMPAT-MATRIX.md +63 -62
- package/hooks/README.md +8 -7
- package/hooks/active_task_context.py +22 -15
- package/hooks/health_check.py +136 -38
- package/hooks/workflow_context.py +120 -30
- package/mcp-server/lib/change-workflow.cjs +556 -0
- package/mcp-server/lib/change-workflow.test.cjs +259 -0
- package/mcp-server/lib/doctor.cjs +112 -0
- package/mcp-server/lib/doctor.test.cjs +86 -0
- package/mcp-server/lib/init-project.cjs +32 -6
- package/mcp-server/lib/pricing.cjs +0 -6
- package/mcp-server/lib/runtime-state.cjs +222 -0
- package/mcp-server/lib/runtime-state.test.cjs +92 -0
- package/mcp-server/lib/state-db.cjs +31 -8
- package/mcp-server/lib/state-ops.cjs +24 -3
- package/mcp-server/lib/telemetry.cjs +4 -0
- package/mcp-server/server.cjs +102 -10
- package/mcp-server/tests/db-ops.test.cjs +5 -1
- package/mcp-server/tests/init-project.test.cjs +45 -0
- package/mcp-server/tests/server-contract.test.cjs +140 -1
- package/mcp-server/tests/state-db.test.cjs +44 -7
- package/mcp-server/tests/state-ops.test.cjs +42 -0
- package/mcp-server/tests/telemetry.test.cjs +14 -0
- package/orchestrator/daemon.cjs +39 -0
- package/orchestrator/dispatch-rules.cjs +15 -4
- package/orchestrator/tests/daemon.test.cjs +63 -12
- package/orchestrator/tests/dispatch-rules.test.cjs +12 -10
- package/package.json +5 -7
- package/settings.json +1 -2
- package/skills/ultra-change/SKILL.md +140 -0
- package/skills/ultra-deliver/SKILL.md +81 -15
- package/skills/ultra-dev/SKILL.md +32 -12
- package/skills/ultra-doctor/SKILL.md +90 -0
- package/skills/ultra-init/SKILL.md +8 -6
- package/skills/ultra-review/SKILL.md +16 -0
- package/skills/ultra-status/SKILL.md +30 -7
- package/skills/ultra-test/SKILL.md +14 -2
- package/skills/ultra-verify/SKILL.md +22 -41
- package/skills/ultra-verify/evals/evals.json +22 -24
- package/skills/ultra-verify/references/confidence-system.md +15 -32
- package/skills/ultra-verify/references/cross-verify-modes.md +14 -59
- package/skills/ultra-verify/scripts/verify_wait.py +44 -81
- package/spec/cli-protocol.md +12 -1
- package/spec/mcp-tools.yaml +340 -4
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/skill-manifest.schema.json +2 -2
- package/spec/schemas/state-db.sql +119 -2
- package/ultra-tools/cli.cjs +3 -0
- package/ultra-tools/commands/system.cjs +57 -0
- package/ultra-tools/system-command.test.cjs +53 -0
- package/adapters/_shared/md-to-toml.cjs +0 -54
- package/adapters/_shared/rtk-detect.cjs +0 -86
- package/adapters/_shared/tests/md-to-toml.test.cjs +0 -48
- package/adapters/_shared/tests/rtk-detect.test.cjs +0 -116
- package/adapters/gemini.js +0 -204
- package/adapters/tests/gemini.test.cjs +0 -112
- package/docs/LEGACY-HERMES.md +0 -1175
- package/orchestrator/code-graph-watcher.cjs +0 -121
- package/orchestrator/tests/code-graph-watcher.test.cjs +0 -183
- package/skills/gemini-collab/SKILL.md +0 -51
- package/skills/gemini-collab/evals/evals.json +0 -42
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ultra-verify
|
|
3
|
-
description: Run
|
|
3
|
+
description: Run independent two-model verification with the current host as primary and a read-only Codex advisor, then verify and synthesize the evidence.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Ultra Verify —
|
|
6
|
+
# Ultra Verify — Independent Two-Model Verification
|
|
7
7
|
|
|
8
8
|
Claude Code remains primary. It writes the first independent analysis, verifies consequential
|
|
9
|
-
claims, and owns the final answer.
|
|
10
|
-
|
|
9
|
+
claims, and owns the final answer. Codex is an untrusted read-only advisor. Use this workflow only
|
|
10
|
+
when the user explicitly asks for cross-model verification.
|
|
11
11
|
|
|
12
12
|
## Modes
|
|
13
13
|
|
|
@@ -18,11 +18,11 @@ workflow only when the user explicitly asks for cross-model or three-way verific
|
|
|
18
18
|
|
|
19
19
|
## Preconditions
|
|
20
20
|
|
|
21
|
-
1. Confirm `
|
|
21
|
+
1. Confirm `codex --version` plus authentication.
|
|
22
22
|
2. Define the exact workspace, question, evidence standard, and response shape.
|
|
23
23
|
3. Do not send secrets, unrelated files, or an unbounded home directory.
|
|
24
|
-
4. Track
|
|
25
|
-
|
|
24
|
+
4. Track four steps in the current host plan: primary analysis, advisor launch, completion wait,
|
|
25
|
+
and verified synthesis.
|
|
26
26
|
|
|
27
27
|
## 1. Create the session and write the primary view
|
|
28
28
|
|
|
@@ -33,25 +33,11 @@ mkdir -p "${SESSION_PATH}"
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Write Claude Code's evidence-backed analysis to `${SESSION_PATH}/claude-analysis.md` before
|
|
36
|
-
reading
|
|
36
|
+
reading the advisor output. Record the mode, checkout, scope, and evidence boundary.
|
|
37
37
|
|
|
38
|
-
## 2. Launch
|
|
38
|
+
## 2. Launch the advisor read-only
|
|
39
39
|
|
|
40
|
-
Give
|
|
41
|
-
Launch them concurrently when the host supports independent shell sessions; otherwise start both
|
|
42
|
-
before collecting either result.
|
|
43
|
-
|
|
44
|
-
Gemini:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
gemini --approval-mode plan \
|
|
48
|
-
--output-format text \
|
|
49
|
-
-p "<bounded prompt>" \
|
|
50
|
-
> "${SESSION_PATH}/gemini-output.md" \
|
|
51
|
-
2> "${SESSION_PATH}/gemini-error.log"
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Codex:
|
|
40
|
+
Give the advisor the bounded raw question and evidence without the primary conclusion.
|
|
55
41
|
|
|
56
42
|
```bash
|
|
57
43
|
codex exec -s read-only \
|
|
@@ -60,10 +46,9 @@ codex exec -s read-only \
|
|
|
60
46
|
2> "${SESSION_PATH}/codex-error.log"
|
|
61
47
|
```
|
|
62
48
|
|
|
63
|
-
Never enable
|
|
64
|
-
either advisor.
|
|
49
|
+
Never enable write-capable automation, danger-full-access, or permission bypass for the advisor.
|
|
65
50
|
|
|
66
|
-
## 3. Wait for completed
|
|
51
|
+
## 3. Wait for completed output
|
|
67
52
|
|
|
68
53
|
Run the bundled waiter in an asynchronous or yielded shell session and poll it at bounded
|
|
69
54
|
intervals; never hold one blocking tool call longer than 60 seconds.
|
|
@@ -73,28 +58,26 @@ python3 "$CLAUDE_PLUGIN_ROOT/skills/ultra-verify/scripts/verify_wait.py" \
|
|
|
73
58
|
"${SESSION_PATH}" --timeout 1200
|
|
74
59
|
```
|
|
75
60
|
|
|
76
|
-
The waiter reports
|
|
77
|
-
|
|
78
|
-
error log when it fails.
|
|
61
|
+
The waiter reports the advisor as `complete`, `failed`, `empty`, or `pending`. Read the output only
|
|
62
|
+
after its status is `complete` and the file is non-empty. Preserve the error log when it fails.
|
|
79
63
|
|
|
80
64
|
## 4. Verify and synthesize
|
|
81
65
|
|
|
82
|
-
Compare `claude-analysis.md` with
|
|
66
|
+
Compare `claude-analysis.md` with `codex-output.md`:
|
|
83
67
|
|
|
84
68
|
1. Verify claims against the current checkout, tests, runtime, or primary documentation.
|
|
85
|
-
2. Separate
|
|
86
|
-
3. Explain scope, version, and assumption differences before
|
|
69
|
+
2. Separate verified agreement, useful dissent, and unsupported assertions.
|
|
70
|
+
3. Explain scope, version, and assumption differences before judging the result.
|
|
87
71
|
4. Write `synthesis.md` and `metadata.json`, then return one Claude Code-owned conclusion.
|
|
88
72
|
|
|
89
|
-
Use `references/confidence-system.md` for
|
|
90
|
-
`references/cross-verify-modes.md` for mode-specific
|
|
91
|
-
|
|
73
|
+
Use `references/confidence-system.md` for evidence-based confidence and
|
|
74
|
+
`references/cross-verify-modes.md` for mode-specific expectations. Model agreement never overrides
|
|
75
|
+
failing tests or authoritative runtime evidence.
|
|
92
76
|
|
|
93
77
|
## Degraded operation
|
|
94
78
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
- Never block the underlying user task solely because an advisor is absent, unauthenticated, or
|
|
79
|
+
- Advisor failure: return the primary analysis with an explicit single-source warning.
|
|
80
|
+
- Never block the underlying user task solely because the advisor is absent, unauthenticated, or
|
|
98
81
|
slow.
|
|
99
82
|
|
|
100
83
|
## Session files
|
|
@@ -102,8 +85,6 @@ overrides failing tests or authoritative runtime evidence.
|
|
|
102
85
|
```text
|
|
103
86
|
.ultra/collab/<SESSION_ID>/
|
|
104
87
|
claude-analysis.md
|
|
105
|
-
gemini-output.md
|
|
106
|
-
gemini-error.log
|
|
107
88
|
codex-output.md
|
|
108
89
|
codex-error.log
|
|
109
90
|
metadata.json
|
|
@@ -4,47 +4,45 @@
|
|
|
4
4
|
{
|
|
5
5
|
"id": 1,
|
|
6
6
|
"prompt": "/ultra-verify decision 'Should we use PostgreSQL or MongoDB for our new microservice?'",
|
|
7
|
-
"expected_output": "Claude
|
|
7
|
+
"expected_output": "Claude writes its analysis first, launches Codex read-only, verifies both analyses, and returns one evidence-backed synthesis",
|
|
8
8
|
"assertions": [
|
|
9
|
-
"Claude writes its own analysis
|
|
10
|
-
"Launches
|
|
11
|
-
"Creates session directory under .ultra/collab/
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"Includes per-AI recommendation summary and trade-off matrix"
|
|
9
|
+
"Claude writes its own analysis before reading advisor output",
|
|
10
|
+
"Launches Codex with a read-only sandbox",
|
|
11
|
+
"Creates a session directory under .ultra/collab/",
|
|
12
|
+
"Verifies consequential claims against authoritative evidence",
|
|
13
|
+
"Separates verified agreement, useful dissent, and unsupported assertions"
|
|
15
14
|
]
|
|
16
15
|
},
|
|
17
16
|
{
|
|
18
17
|
"id": 2,
|
|
19
|
-
"prompt": "/ultra-verify diagnose 'API returns 500 intermittently under load
|
|
20
|
-
"expected_output": "
|
|
18
|
+
"prompt": "/ultra-verify diagnose 'API returns 500 intermittently under load'",
|
|
19
|
+
"expected_output": "Independent hypotheses are reconciled through discriminating checks and evidence strength",
|
|
21
20
|
"assertions": [
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"Each hypothesis includes
|
|
25
|
-
"
|
|
21
|
+
"Both analyses provide ranked hypotheses",
|
|
22
|
+
"Duplicate hypotheses are merged by root cause",
|
|
23
|
+
"Each retained hypothesis includes a verification step",
|
|
24
|
+
"Investigation order follows evidence strength and verification cost"
|
|
26
25
|
]
|
|
27
26
|
},
|
|
28
27
|
{
|
|
29
28
|
"id": 3,
|
|
30
29
|
"prompt": "/ultra-verify audit",
|
|
31
|
-
"expected_output": "
|
|
30
|
+
"expected_output": "Findings are deduplicated and verified against the current checkout without vote-based severity",
|
|
32
31
|
"assertions": [
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
32
|
+
"Uses the same scope and severity model for both analyses",
|
|
33
|
+
"Verifies every reported location",
|
|
34
|
+
"Retains uniquely reported findings when source evidence confirms them",
|
|
35
|
+
"Severity does not depend on model agreement"
|
|
37
36
|
]
|
|
38
37
|
},
|
|
39
38
|
{
|
|
40
39
|
"id": 4,
|
|
41
|
-
"prompt": "/ultra-verify estimate 'Implement OAuth2 login with
|
|
42
|
-
"expected_output": "
|
|
40
|
+
"prompt": "/ultra-verify estimate 'Implement OAuth2 login with two providers'",
|
|
41
|
+
"expected_output": "Two estimates are reconciled by scope, assumptions, dependencies, and risk",
|
|
43
42
|
"assertions": [
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"Final recommended estimate with confidence level"
|
|
43
|
+
"Both analyses include work breakdowns and assumptions",
|
|
44
|
+
"Large differences are explained rather than averaged mechanically",
|
|
45
|
+
"Final range is supported by the reconciled work breakdown"
|
|
48
46
|
]
|
|
49
47
|
}
|
|
50
48
|
]
|
|
@@ -1,38 +1,21 @@
|
|
|
1
|
-
# Confidence
|
|
1
|
+
# Evidence-Based Confidence
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cross-model agreement is a signal, not an authority. Assign confidence only after checking claims
|
|
4
|
+
against source code, tests, runtime output, or primary documentation.
|
|
4
5
|
|
|
5
6
|
## Levels
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **
|
|
10
|
-
|
|
8
|
+
- **Verified agreement**: both analyses agree and the consequential claims have authoritative
|
|
9
|
+
supporting evidence.
|
|
10
|
+
- **Resolved dissent**: the analyses differ, but current evidence supports one position and explains
|
|
11
|
+
the disagreement.
|
|
12
|
+
- **Unresolved**: available evidence cannot distinguish the positions or key assumptions remain
|
|
13
|
+
untested.
|
|
14
|
+
- **Single source**: the advisor failed or returned no usable output; no cross-model corroboration is
|
|
15
|
+
available.
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
- **Confidence**: High
|
|
14
|
-
- **Action**: Recommended — investigate the dissenting view for edge cases or alternative perspectives
|
|
15
|
-
- **Display**: `[MAJORITY 2/3]` + note which AI dissented and why
|
|
17
|
+
## Reporting
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- **Display**: `[NO CONSENSUS]` + summary of each position
|
|
21
|
-
|
|
22
|
-
## Application by Mode
|
|
23
|
-
|
|
24
|
-
| Mode | Consensus | Majority | No Consensus |
|
|
25
|
-
|------|-----------|----------|--------------|
|
|
26
|
-
| decision | Strong recommendation | Recommended + dissent analysis | Present all options, suggest decomposition |
|
|
27
|
-
| diagnose | Investigate first | Investigate second | Check all, may need more data |
|
|
28
|
-
| audit | Critical — fix now | High — likely real | Investigate — may be false positive |
|
|
29
|
-
| estimate | Use average, high confidence | Investigate outlier | Decompose task further |
|
|
30
|
-
|
|
31
|
-
## Degraded Confidence
|
|
32
|
-
|
|
33
|
-
When only 2 AIs respond (one failed):
|
|
34
|
-
|
|
35
|
-
- **2/2 Agree**: Equivalent to Majority (not Consensus — missing perspective)
|
|
36
|
-
- **2/2 Differ**: Low confidence — note missing third opinion
|
|
37
|
-
|
|
38
|
-
When only 1 AI responds: Claude-only analysis — explicitly mark as single-source, no consensus scoring.
|
|
19
|
+
Record which claims were verified, the evidence used, useful dissent, unsupported assertions, and
|
|
20
|
+
remaining uncertainty. Model agreement never upgrades a claim contradicted by tests or runtime
|
|
21
|
+
evidence, and a uniquely reported finding remains valid when independently verified.
|
|
@@ -1,73 +1,28 @@
|
|
|
1
1
|
# Ultra Verify Modes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All four modes compare an independently written primary analysis with one read-only external
|
|
4
|
+
advisor. Agreement is useful corroboration, not proof; evidence decides the final result.
|
|
4
5
|
|
|
5
6
|
## 1. Decision (`decision`)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
1. Present the decision context, constraints, and options to all three AIs independently
|
|
11
|
-
2. Each AI provides: recommendation, pros/cons, risks, trade-offs
|
|
12
|
-
3. Synthesis ranks options by consensus:
|
|
13
|
-
- 3/3 recommend same → **Strong recommendation**
|
|
14
|
-
- 2/3 agree → **Recommended** with dissenting view analyzed
|
|
15
|
-
- All different → Present all three with trade-off matrix
|
|
16
|
-
|
|
17
|
-
**Output structure:**
|
|
18
|
-
- Per-AI recommendation summary
|
|
19
|
-
- Consensus matrix (which AI recommends what)
|
|
20
|
-
- Trade-off analysis
|
|
21
|
-
- Final recommendation with confidence level
|
|
8
|
+
Give both analyses the same decision context, constraints, and options. Compare recommendation,
|
|
9
|
+
trade-offs, risks, and assumptions. When they differ, identify the assumption or evidence that
|
|
10
|
+
caused the split and resolve it before recommending an option.
|
|
22
11
|
|
|
23
12
|
## 2. Diagnose (`diagnose`)
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
1. Provide symptoms, error messages, relevant code, and recent changes to all three AIs
|
|
29
|
-
2. Each AI provides its top-3 hypotheses with evidence and verification steps
|
|
30
|
-
3. Synthesis:
|
|
31
|
-
- Hypotheses appearing in 3/3 lists → **Most likely** (investigate first)
|
|
32
|
-
- Hypotheses in 2/3 lists → **Probable** (investigate second)
|
|
33
|
-
- Unique hypotheses → **Worth checking** (may catch edge cases)
|
|
34
|
-
|
|
35
|
-
**Output structure:**
|
|
36
|
-
- Ranked hypothesis list (by consensus count)
|
|
37
|
-
- Per-hypothesis: description, evidence, verification steps, suggested fix
|
|
38
|
-
- Recommended investigation order
|
|
14
|
+
Provide symptoms, exact errors, relevant code, and recent changes. Each analysis should rank its
|
|
15
|
+
root-cause hypotheses and provide discriminating checks. Merge duplicate hypotheses, retain useful
|
|
16
|
+
unique ones, and order investigation by evidence strength and verification cost.
|
|
39
17
|
|
|
40
18
|
## 3. Audit (`audit`)
|
|
41
19
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
1. Provide the code scope to all three AIs with the same audit prompt
|
|
46
|
-
2. Each AI reports findings with severity and location
|
|
47
|
-
3. Synthesis grades findings by consensus:
|
|
48
|
-
- Found by 3/3 → **Critical** — fix immediately
|
|
49
|
-
- Found by 2/3 → **High** — likely real, investigate
|
|
50
|
-
- Found by 1/3 → **Investigate** — may be false positive or edge case
|
|
51
|
-
|
|
52
|
-
**Output structure:**
|
|
53
|
-
- Findings table: description, severity, consensus count, which AIs found it
|
|
54
|
-
- Grouped by consensus level (Critical → High → Investigate)
|
|
55
|
-
- Action items prioritized by consensus
|
|
20
|
+
Use the same code scope and severity model for both analyses. Deduplicate findings by root cause,
|
|
21
|
+
verify every location against the current checkout, and retain a single-model finding when source
|
|
22
|
+
evidence confirms it. Never derive severity from model agreement alone.
|
|
56
23
|
|
|
57
24
|
## 4. Estimate (`estimate`)
|
|
58
25
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
1. Describe the task, requirements, and constraints to all three AIs
|
|
63
|
-
2. Each AI provides: estimate (time/effort), breakdown, assumptions, risks
|
|
64
|
-
3. Synthesis evaluates convergence:
|
|
65
|
-
- All within 20% of each other → **High confidence** estimate (use average)
|
|
66
|
-
- One outlier → **Moderate confidence** — investigate the outlier's reasoning
|
|
67
|
-
- All significantly different → **Low confidence** — task needs decomposition
|
|
68
|
-
|
|
69
|
-
**Output structure:**
|
|
70
|
-
- Per-AI estimate with breakdown and assumptions
|
|
71
|
-
- Convergence analysis
|
|
72
|
-
- Recommended estimate with confidence level
|
|
73
|
-
- Risk factors that could shift the estimate
|
|
26
|
+
Require a breakdown, assumptions, dependencies, and risk range from both analyses. Explain large
|
|
27
|
+
differences by locating mismatched scope or assumptions. Return a range supported by the reconciled
|
|
28
|
+
work breakdown rather than averaging estimates mechanically.
|
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""File-based completion waiter for the Ultra Verify advisor.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Two exit conditions only:
|
|
8
|
-
1. Both AIs have output (non-empty + stable size) → exit 0, status="complete"
|
|
9
|
-
2. Timeout → exit 0, status="timeout" (JSON status field tells the story)
|
|
10
|
-
|
|
11
|
-
Expected files:
|
|
12
|
-
- gemini-output.md (Gemini)
|
|
13
|
-
- codex-output.md (Codex)
|
|
14
|
-
|
|
15
|
-
Usage:
|
|
16
|
-
python3 verify_wait.py <session_path> [--timeout SECONDS]
|
|
17
|
-
|
|
18
|
-
Exit codes:
|
|
19
|
-
0 - Always (result expressed via JSON status field)
|
|
20
|
-
2 - Invalid arguments (missing path, bad --timeout)
|
|
4
|
+
Poll the session directory until the expected output is non-empty and stable, or until timeout.
|
|
5
|
+
The JSON result carries the operational status; timeout is not a process error.
|
|
21
6
|
"""
|
|
22
7
|
|
|
23
8
|
import json
|
|
@@ -25,106 +10,84 @@ import sys
|
|
|
25
10
|
import time
|
|
26
11
|
from pathlib import Path
|
|
27
12
|
|
|
28
|
-
POLL_INTERVAL = 3
|
|
29
|
-
DEFAULT_TIMEOUT = 1200
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
EXPECTED = {
|
|
33
|
-
"gemini": "gemini-output.md",
|
|
34
|
-
"codex": "codex-output.md",
|
|
35
|
-
}
|
|
13
|
+
POLL_INTERVAL = 3
|
|
14
|
+
DEFAULT_TIMEOUT = 1200
|
|
15
|
+
ADVISOR = "codex"
|
|
16
|
+
OUTPUT_FILE = "codex-output.md"
|
|
36
17
|
|
|
37
18
|
|
|
38
19
|
def _file_size(path: Path) -> int:
|
|
39
|
-
"""Return file size in bytes, or -1 if not found."""
|
|
40
20
|
try:
|
|
41
21
|
return path.stat().st_size
|
|
42
22
|
except (FileNotFoundError, OSError):
|
|
43
23
|
return -1
|
|
44
24
|
|
|
45
25
|
|
|
46
|
-
def _check_output(session_path: Path
|
|
47
|
-
|
|
48
|
-
output = session_path / EXPECTED[name]
|
|
26
|
+
def _check_output(session_path: Path) -> dict:
|
|
27
|
+
output = session_path / OUTPUT_FILE
|
|
49
28
|
if _file_size(output) > 0:
|
|
50
|
-
return {"name":
|
|
51
|
-
return {"name":
|
|
29
|
+
return {"name": ADVISOR, "status": "complete", "file": str(output)}
|
|
30
|
+
return {"name": ADVISOR, "status": "pending", "file": None}
|
|
52
31
|
|
|
53
32
|
|
|
54
|
-
def _timeout_status(session_path: Path
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
error = session_path / f"{name}-error.log"
|
|
33
|
+
def _timeout_status(session_path: Path) -> dict:
|
|
34
|
+
output = session_path / OUTPUT_FILE
|
|
35
|
+
error = session_path / f"{ADVISOR}-error.log"
|
|
58
36
|
output_size = _file_size(output)
|
|
59
37
|
if output_size > 0:
|
|
60
|
-
return {"name":
|
|
38
|
+
return {"name": ADVISOR, "status": "complete", "file": str(output)}
|
|
61
39
|
if _file_size(error) > 0:
|
|
62
|
-
return {"name":
|
|
40
|
+
return {"name": ADVISOR, "status": "failed", "file": str(error)}
|
|
63
41
|
if output_size == 0:
|
|
64
|
-
return {"name":
|
|
65
|
-
return {"name":
|
|
42
|
+
return {"name": ADVISOR, "status": "empty", "file": str(output)}
|
|
43
|
+
return {"name": ADVISOR, "status": "pending", "file": None}
|
|
66
44
|
|
|
67
45
|
|
|
68
|
-
def main():
|
|
46
|
+
def main() -> None:
|
|
69
47
|
if len(sys.argv) < 2:
|
|
70
48
|
print(__doc__, file=sys.stderr)
|
|
71
49
|
sys.exit(2)
|
|
72
50
|
|
|
73
51
|
session_path = Path(sys.argv[1])
|
|
74
52
|
timeout = DEFAULT_TIMEOUT
|
|
75
|
-
|
|
76
53
|
if "--timeout" in sys.argv:
|
|
77
54
|
idx = sys.argv.index("--timeout")
|
|
78
|
-
if idx + 1
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
if idx + 1 >= len(sys.argv):
|
|
56
|
+
print("Error: --timeout requires an integer", file=sys.stderr)
|
|
57
|
+
sys.exit(2)
|
|
58
|
+
try:
|
|
59
|
+
timeout = int(sys.argv[idx + 1])
|
|
60
|
+
except ValueError:
|
|
61
|
+
print(f"Error: --timeout must be integer: {sys.argv[idx + 1]}", file=sys.stderr)
|
|
62
|
+
sys.exit(2)
|
|
63
|
+
if timeout < 0:
|
|
64
|
+
print("Error: --timeout must be non-negative", file=sys.stderr)
|
|
65
|
+
sys.exit(2)
|
|
84
66
|
|
|
85
67
|
if not session_path.is_dir():
|
|
86
68
|
print(f"Error: session directory not found: {session_path}", file=sys.stderr)
|
|
87
69
|
sys.exit(2)
|
|
88
70
|
|
|
89
71
|
deadline = time.monotonic() + timeout
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# Poll loop — exit condition 1: both outputs ready + stable
|
|
72
|
+
previous_size = _file_size(session_path / OUTPUT_FILE)
|
|
93
73
|
while time.monotonic() < deadline:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
gemini_done = gemini["status"] == "complete" and cur_sizes["gemini"] == prev_sizes["gemini"]
|
|
99
|
-
codex_done = codex["status"] == "complete" and cur_sizes["codex"] == prev_sizes["codex"]
|
|
100
|
-
|
|
101
|
-
if gemini_done and codex_done:
|
|
74
|
+
current_size = _file_size(session_path / OUTPUT_FILE)
|
|
75
|
+
advisor = _check_output(session_path)
|
|
76
|
+
stable = advisor["status"] == "complete" and current_size == previous_size
|
|
77
|
+
if stable:
|
|
102
78
|
elapsed = int(timeout - (deadline - time.monotonic()))
|
|
103
|
-
print(json.dumps({"status": "complete",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
remaining = int(deadline - time.monotonic())
|
|
110
|
-
parts = []
|
|
111
|
-
for name, done, result in [("gemini", gemini_done, gemini), ("codex", codex_done, codex)]:
|
|
112
|
-
if done:
|
|
113
|
-
parts.append(f"{name}:complete")
|
|
114
|
-
elif result["status"] == "complete":
|
|
115
|
-
parts.append(f"{name}:stabilizing")
|
|
116
|
-
else:
|
|
117
|
-
parts.append(f"{name}:waiting")
|
|
118
|
-
done_count = int(gemini_done) + int(codex_done)
|
|
119
|
-
sys.stderr.write(f"\r [{done_count}/2] {' | '.join(parts)} ({remaining}s remaining) ")
|
|
79
|
+
print(json.dumps({"status": "complete", ADVISOR: advisor, "elapsed_seconds": elapsed}))
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
state = "stabilizing" if advisor["status"] == "complete" else "waiting"
|
|
83
|
+
remaining = max(0, int(deadline - time.monotonic()))
|
|
84
|
+
sys.stderr.write(f"\r [0/1] {ADVISOR}:{state} ({remaining}s remaining) ")
|
|
120
85
|
sys.stderr.flush()
|
|
86
|
+
previous_size = current_size
|
|
121
87
|
time.sleep(POLL_INTERVAL)
|
|
122
88
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
codex = _timeout_status(session_path, "codex")
|
|
126
|
-
print(json.dumps({"status": "timeout", "gemini": gemini, "codex": codex, "elapsed_seconds": timeout}))
|
|
127
|
-
sys.exit(0)
|
|
89
|
+
advisor = _timeout_status(session_path)
|
|
90
|
+
print(json.dumps({"status": "timeout", ADVISOR: advisor, "elapsed_seconds": timeout}))
|
|
128
91
|
|
|
129
92
|
|
|
130
93
|
if __name__ == "__main__":
|
package/spec/cli-protocol.md
CHANGED
|
@@ -100,13 +100,24 @@ as a fallback unless `ultra-tools <family> --help` lists it.
|
|
|
100
100
|
| `session.admission_check` | `session admission` | 4.5 | any |
|
|
101
101
|
| `session.heartbeat` | `session heartbeat` | 4.5 | mcp |
|
|
102
102
|
| `session.subscribe_events`| `session subscribe` | 4.5 | any |
|
|
103
|
+
| `change.create` | `change create` | 9 | mcp |
|
|
104
|
+
| `change.update` | `change update` | 9 | mcp |
|
|
105
|
+
| `change.get` | `change get` | 9 | any |
|
|
106
|
+
| `change.list` | `change list` | 9 | any |
|
|
107
|
+
| `change.context` | `change context` | 9 | mcp |
|
|
108
|
+
| `change.converge` | `change converge` | 9 | mcp |
|
|
109
|
+
| `change.archive` | `change archive` | 9 | mcp |
|
|
110
|
+
| `system.doctor` | `system doctor` | 9 | mcp |
|
|
103
111
|
| `plan.export` | `plan export` | 8a | mcp |
|
|
104
112
|
| `plan.get` | `plan get` | 8a | any |
|
|
105
113
|
|
|
106
114
|
Current executable maintenance surfaces are `task init-project`,
|
|
107
115
|
`session close|get|list|admission|heartbeat|subscribe|reap`, `status`, `db`,
|
|
108
116
|
`migrate`, and `legacy-memory`. In particular, `task create|update|list|get`
|
|
109
|
-
are not CLI fallbacks.
|
|
117
|
+
and all `change` lifecycle verbs are not CLI fallbacks. Change state must use
|
|
118
|
+
the live MCP server; `system doctor` may additionally be exposed by the
|
|
119
|
+
maintenance CLI because it is the recovery path when MCP startup is degraded.
|
|
120
|
+
The full access policy lives in
|
|
110
121
|
`docs/STATE-DB-ACCESS-POLICY.md` (Phase 2.2 / R25).
|
|
111
122
|
|
|
112
123
|
## 6. Versioning
|