baldart 3.34.0 → 3.35.1
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/CHANGELOG.md +35 -0
- package/VERSION +1 -1
- package/framework/.claude/commands/codexreview.md +56 -1
- package/framework/.claude/skills/new/SKILL.md +73 -8
- package/package.json +1 -1
- package/src/commands/add.js +38 -3
- package/src/commands/doctor.js +4 -3
- package/src/commands/update.js +45 -6
- package/src/commands/version.js +1 -2
- package/src/utils/git.js +11 -0
- package/src/utils/state.js +24 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ All notable changes to BALDART will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.35.1] - 2026-05-30
|
|
9
|
+
|
|
10
|
+
Fixes a **HIGH-severity** bug where `baldart update --reset` could leave a consumer **committed-without-`.framework/`** (broken install, manual recovery only). The reset path wiped `.framework/`, committed the deletion, then handed off to `add()` **programmatically with no repository** — the bin-layer default (`antbald/BALDART`) only applies on the CLI path, so `repo` arrived `undefined`, crashing at `repo.startsWith(...)` ("Cannot read properties of undefined") *after* the wipe was already committed. Three layers now make this unreachable: (1) repo resolved **before** any destructive step, (2) reinstall failures roll back to the pre-reset backup tag, (3) defensive guards + legible errors. **No new `baldart.config.yml` keys** — the schema-propagation rule does not apply.
|
|
11
|
+
|
|
12
|
+
### Fixed — `update --reset` never leaves the consumer without `.framework/`
|
|
13
|
+
|
|
14
|
+
- **[src/commands/update.js](src/commands/update.js)** (`runReset`): the upstream repo is now resolved via `State.resolveRepo()` **before** the backup tag / `rm -rf` / reset commit, then threaded **explicitly** into `add()` (was `addCmd(undefined, { yes: true })` — the root cause). The stale v3.28.1 comment claiming "undefined → use default from package.json" (which never existed in `add.js`) is corrected.
|
|
15
|
+
- **[src/commands/update.js](src/commands/update.js)**: the reinstall is wrapped in a `try/catch` that, on **any** failure (undefined repo, network, ignored path, missing hooks), auto-runs `git reset --hard <backupTag>` to restore the pre-reset `.framework/` and prints an explicit recovery command. Safe because reset is gated on a clean working tree — the backup tag holds everything but the reset commit.
|
|
16
|
+
- **[src/commands/add.js](src/commands/add.js)**: new `options.throwOnError` makes `add()` **re-throw** instead of `process.exit(1)` at every reachable failure point (download catch, gitignore-still-ignored, hooks-missing post-flight), so a programmatic caller can recover. CLI behavior is unchanged (flag absent → historical exit-1). Also resolves a falsy `repo` from the ledger as a last resort and fails fast **before** download with a recovery hint.
|
|
17
|
+
- **[src/utils/git.js](src/utils/git.js)** (`normalizeRepoUrl`): guards a falsy/non-string `repo` with a legible `Error` ("repository non specificato …") instead of the cryptic `Cannot read properties of undefined (reading 'startsWith')`.
|
|
18
|
+
|
|
19
|
+
### Changed — shared repo resolver (de-dup)
|
|
20
|
+
|
|
21
|
+
- **[src/utils/state.js](src/utils/state.js)**: new exported `resolveRepo(cwd)` (cascade `state.framework_repo → DEFAULT_REPO`, never `undefined`, preserves a consumer's fork) + exported `DEFAULT_REPO` constant. `recordInstall` now uses the constant.
|
|
22
|
+
- **[src/commands/version.js](src/commands/version.js)**: drops its local `REPO_DEFAULT` duplicate in favor of the shared `State.DEFAULT_REPO`.
|
|
23
|
+
|
|
24
|
+
### Known follow-up (separate fix)
|
|
25
|
+
|
|
26
|
+
- `--on-divergence` (v3.29.0) is rejected by an **older global CLI** with "unknown option" *before* the `npx baldart@latest` auto-relaunch can self-upgrade (commander validates options first). Tracked separately — workaround: `npm i -g baldart@latest` before using new flags.
|
|
27
|
+
|
|
28
|
+
## [3.35.0] - 2026-05-30
|
|
29
|
+
|
|
30
|
+
De-duplicates the review chain in `/new` and adds a **Review Profile Selector** so the per-card pre-merge gate scales its *depth* by risk instead of running the full multi-agent `/codexreview` on every card — including trivial ones. The per-card gate stays **unconditional** (it always runs); only its breadth varies, and the safety-critical Codex adversarial pass + false-positive gate run on every card regardless of profile. Copertura invariata: every line is still code-reviewed once, every doc doc-reviewed once. **No new `baldart.config.yml` keys** — the profile is computed at runtime from signals already present (the Phase 3.7 high-risk-trigger detector + the QA profile), and lean mode is an internal caller→skill file contract, so the schema-propagation rule does not apply.
|
|
31
|
+
|
|
32
|
+
### Changed — `/new` review chain (de-dup, coverage-invariant)
|
|
33
|
+
|
|
34
|
+
- **[framework/.claude/skills/new/SKILL.md](framework/.claude/skills/new/SKILL.md)** + **[framework/.claude/commands/codexreview.md](framework/.claude/commands/codexreview.md)**: per-card Phase 3.7 (and Team Mode D.4b) now hand `/codexreview` a lean contract file (`/tmp/codexreview-lean-<CARD-ID>.json`) so it **reuses the Phase-1 architecture baseline** (`/tmp/arch-baseline-<CARD-ID>.md`) instead of re-spawning `codebase-architect`, and **skips the duplicate `doc-reviewer`** (Phase 3 already audits the same diff). `/codexreview` gains a backward-compatible **Step -0.5 (lean-mode detection)** with consume-once semantics; standalone `/codexreview <CARD-ID>` (no contract file) behaves exactly as before.
|
|
35
|
+
- **doc-reviewer lens-merge**: Phase 3's `doc-reviewer` mandate now also carries the *spec/docs-drift → bug* lens that was previously exclusive to `/codexreview` agent #4 — so dropping the duplicate loses no coverage.
|
|
36
|
+
- **Final review de-dup**: single-card batches short-circuit F.1–F.4 (the lone card already passed Phase 3.7 unchanged) while still running the F.5 final build; multi-card batches scope the cross-card review to a **computed** surface (files touched by ≥2 cards ∪ `depends_on`-linked pairs), never a model-decided skip.
|
|
37
|
+
- **Team Mode doc-reviewer collapse**: `doc-reviewer` went from ~3× per card (D.2 + D.4a + D.4b) to ~1× per group — D.2 attributes findings per-card, D.4a consumes them (no re-spawn), D.4b skips its copy via the lean contract.
|
|
38
|
+
|
|
39
|
+
### Added — Review Profile Selector (data-gated)
|
|
40
|
+
|
|
41
|
+
- **[framework/.claude/skills/new/SKILL.md](framework/.claude/skills/new/SKILL.md)**: Phase 3.7 Step C selects `light` ⟺ (0 high-risk triggers AND QA profile ∈ {skip, light}), else `full`. `light` drops the breadth agents (qa-sentinel, api-perf-cost-auditor, doc-reviewer) + Step 3.5 CoVe but ALWAYS keeps `code-reviewer` + Codex adversarial + the false-positive gate (BUG-0530 invariant preserved — `light` ≠ skip). Mirrors the existing QA Profile Selector pattern. Ships behind a **data gate**: the mapping stays hard-coded to `full` until consumer telemetry (`docs/metrics/` Fix Application Log) confirms `light`-eligible cards have not historically produced verified BLOCKER/HIGH at full depth.
|
|
42
|
+
|
|
8
43
|
## [3.34.0] - 2026-05-30
|
|
9
44
|
|
|
10
45
|
Adds an **assert-active git-hooks health check** to `baldart doctor` and the `worktree-manager` verify step. A consumer had `core.hooksPath` pointing at `.git/hooks` (empty) while the repo's versioned hooks lived in `scripts/git-hooks/` — the pre-commit schema guard and pre-push were **silently inactive for days**. The existing worktree-manager verify (`git config core.hooksPath || true`) only *read* the value without asserting anything, so it never caught the drift. Same class as the LSP bug: the framework "verifies" by reading a proxy instead of asserting the real state. This moves it from read-only to assert-active. **WARN-only** — the check never mutates `core.hooksPath` (a wrong heuristic + auto-fix would be worse than the original bug); it surfaces the exact remediation command and lets the user apply it. **No new `baldart.config.yml` keys** — it's a universal, always-on, filesystem-autodetected check, so the schema-propagation rule does not apply.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.35.1
|
|
@@ -27,6 +27,48 @@ Deduplicate, then:
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
+
## Step -0.5 — Lean Mode Detection (caller-supplied contract)
|
|
31
|
+
|
|
32
|
+
`/new` invokes this skill per-card as a pre-merge gate (Phase 3.7 / Team D.4b) and passes an
|
|
33
|
+
out-of-band **contract file** so it can reuse work already done in earlier phases and scale review
|
|
34
|
+
depth. Detection is deterministic and applies to THIS run only.
|
|
35
|
+
|
|
36
|
+
For the (single) card in scope, check for `/tmp/codexreview-lean-<CARD-ID>.json`.
|
|
37
|
+
|
|
38
|
+
- **Absent** → **full mode**. This is the default for standalone `/codexreview <CARD-ID>`
|
|
39
|
+
invocations — nothing below is skipped; behave exactly as documented in Steps 0–4.
|
|
40
|
+
- **Present** → parse it:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"profile": "light" | "full",
|
|
45
|
+
"arch_baseline_path": "/tmp/arch-baseline-<CARD-ID>.md",
|
|
46
|
+
"diff_summary_path": "<completion-report or git-diff path>",
|
|
47
|
+
"skip_doc_reviewer": true
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Apply:
|
|
52
|
+
- `arch_baseline_path` readable → **skip Step 1** (do NOT re-spawn `codebase-architect`); load the
|
|
53
|
+
baseline from that file and ALSO attach the `diff_summary_path` contents to every review agent so
|
|
54
|
+
they see what changed against the existing-architecture map. *(A1)*
|
|
55
|
+
- `skip_doc_reviewer: true` → **omit agent #4 (`doc-reviewer`)** in Step 2. Doc concerns are covered
|
|
56
|
+
by `/new` Phase 3, which runs `doc-reviewer` (with the spec/docs-drift lens) on the same diff
|
|
57
|
+
immediately before this gate. *(A2)*
|
|
58
|
+
- `profile: "light"` → ALSO omit agent #2 (`qa-sentinel`) and agent #3 (`api-perf-cost-auditor`),
|
|
59
|
+
and **skip Step 3.5 (CoVe)**. Agent #1 (`code-reviewer`), agent #5 (**Codex adversarial**), and
|
|
60
|
+
the Step 3 false-positive gate ALWAYS run. *(B1)*
|
|
61
|
+
- `profile: "full"` (or missing) → full agent set, minus #4 if `skip_doc_reviewer`.
|
|
62
|
+
|
|
63
|
+
**Invariant**: lean mode NEVER suppresses the run itself — at minimum `code-reviewer` + Codex
|
|
64
|
+
adversarial + Step 3 FP-gate execute on every card. If the contract file is malformed/unreadable,
|
|
65
|
+
fall back to **full mode** and note it. **Consume-once**: delete `/tmp/codexreview-lean-<CARD-ID>.json`
|
|
66
|
+
immediately after parsing it, so a later standalone `/codexreview <CARD-ID>` on the same card never
|
|
67
|
+
inherits a stale lean contract from a prior `/new` run. Record the resolved mode in the Step 4 report
|
|
68
|
+
`Method` section, e.g. `lean: profile=light, architect=reused, doc-reviewer=skipped(Phase 3), cove=skipped`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
30
72
|
## Step 0 — Resolve Scope
|
|
31
73
|
|
|
32
74
|
For each provided card ID:
|
|
@@ -49,6 +91,11 @@ If a card cannot be found, mark it as `UNKNOWN` and ask the user before continui
|
|
|
49
91
|
|
|
50
92
|
## Step 1 — Architecture Baseline (MUST)
|
|
51
93
|
|
|
94
|
+
> **Lean skip (Step -0.5)**: if a contract file resolved `arch_baseline_path`, SKIP this step
|
|
95
|
+
> entirely — read the baseline from that file instead of invoking `codebase-architect`, carry it
|
|
96
|
+
> (plus the attached `diff_summary_path`) as the mandatory context for downstream agents, and go to
|
|
97
|
+
> Step 2. In full mode (no contract file), run this step as written.
|
|
98
|
+
|
|
52
99
|
Before running any review agents, invoke `codebase-architect` (via Task tool) for each card scope to map:
|
|
53
100
|
|
|
54
101
|
- Existing architecture and critical patterns
|
|
@@ -96,6 +143,10 @@ Launch these agents in parallel for each card:
|
|
|
96
143
|
4. `doc-reviewer` — spec/docs drift that can cause incorrect behavior
|
|
97
144
|
5. **Codex (GPT-5.5)** — adversarial review via `codex-companion.mjs`
|
|
98
145
|
|
|
146
|
+
> **Lean agent set (Step -0.5)**: when a contract file is active, launch only the agents the resolved
|
|
147
|
+
> mode permits — `skip_doc_reviewer` omits #4; `profile: light` additionally omits #2 and #3. Agents
|
|
148
|
+
> #1 (`code-reviewer`) and #5 (Codex) ALWAYS launch. In full mode (no contract file), launch all five.
|
|
149
|
+
|
|
99
150
|
**Codex invocation rules (agent #5):**
|
|
100
151
|
|
|
101
152
|
Codex is NOT invoked via the `Agent` tool (the `codex:codex-rescue` subagent_type is not registered in the harness). Instead, invoke it directly with Bash:
|
|
@@ -152,6 +203,10 @@ Only `VERIFIED` findings can be reported as bugs.
|
|
|
152
203
|
|
|
153
204
|
## Step 3.5 — Cross-Agent CoVe Pass (MUST — anti-echo, anti-ripple)
|
|
154
205
|
|
|
206
|
+
> **Lean skip (Step -0.5)**: when the contract file resolved `profile: light`, SKIP this CoVe pass.
|
|
207
|
+
> Light profile is only ever selected for 0-trigger + QA-light cards (small, non-refactor diffs)
|
|
208
|
+
> where the ripple surface is minimal. Full profile (and all standalone runs) execute it as written.
|
|
209
|
+
|
|
155
210
|
After Step 3 false-positive gating, the orchestrator runs ONE additional Chain-of-Verification pass on the consolidated VERIFIED pool. This catches two failure modes the per-agent CoVe doesn't:
|
|
156
211
|
|
|
157
212
|
1. **Cross-agent echo allucination**: 2+ agents independently produce the same hallucinated finding (e.g. wrong file path that "looks plausible") and reinforce each other through Step 3 cross-validation.
|
|
@@ -183,7 +238,7 @@ Write one consolidated report per run to `/tmp/codexreview-report-<TIMESTAMP>.md
|
|
|
183
238
|
Report structure:
|
|
184
239
|
|
|
185
240
|
1. `Scope Reviewed`
|
|
186
|
-
2. `Method` (agents used + validation flow; note whether Codex was available or `CODEX_UNAVAILABLE`; note whether Step 3.5 CoVe ran fully or hit budget cap)
|
|
241
|
+
2. `Method` (agents used + validation flow; note whether Codex was available or `CODEX_UNAVAILABLE`; note whether Step 3.5 CoVe ran fully or hit budget cap; in lean mode, record the resolved contract — `lean: profile=<light|full>, architect=<reused|spawned>, doc-reviewer=<skipped(Phase 3)|run>, cove=<skipped|run>`)
|
|
187
242
|
3. `Verified Bugs` (ordered: BLOCKER -> HIGH -> MEDIUM -> LOW; tag findings with `[codex]` if originating from Codex; tag `[ripple-expanded]` if Step 3.5 added `ripple_files`; tag `[cove_unverified]` if Step 3.5 budget exhausted before reaching it)
|
|
188
243
|
4. `Needs Manual Confirmation`
|
|
189
244
|
5. `False Positives Discarded` (with reason)
|
|
@@ -374,6 +374,7 @@ For each card, execute these phases in order:
|
|
|
374
374
|
- If **FIXES NEEDED**: apply corrections to the tracker notes for this card (do NOT modify the backlog YAML). Carry the corrected requirements into the Phase 2 briefing.
|
|
375
375
|
- If **PASS**: proceed.
|
|
376
376
|
5. **Update tracker**: phase = "1-claim DONE", log codebase-architect key findings (1-2 lines) and plan-auditor result (PASS or corrections applied).
|
|
377
|
+
5b. **Persist the architecture baseline for reuse (since v3.35.0)** — write the FULL codebase-architect findings (file paths, type signatures, patterns, high-risk paths) to `/tmp/arch-baseline-<CARD-ID>.md`. Phase 3.7's per-card `/codexreview` reuses this file instead of re-spawning `codebase-architect` (lean mode), so capture enough detail to ground a reviewer — not just the 1-2 tracker lines.
|
|
377
378
|
|
|
378
379
|
### Phase 2 — Implement (self-healing, up to 3 retries)
|
|
379
380
|
6. **Update tracker**: phase = "2-implement".
|
|
@@ -1151,6 +1152,12 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
1151
1152
|
- Check each invariant in the checklist above and flag any that are unmet
|
|
1152
1153
|
- Flag any doc in "Related docs to check" that should be stale but isn't marked yet
|
|
1153
1154
|
- Run your standard doc audit on all changed files
|
|
1155
|
+
- **Spec/docs-drift → bug lens (MANDATORY since v3.35.0)**: flag any place where the
|
|
1156
|
+
implementation contradicts a documented contract/spec in a way that can cause incorrect
|
|
1157
|
+
behavior (response shape diverging from `api/<module>.md`, field semantics diverging from
|
|
1158
|
+
`data-model.md`, etc.). This lens was previously exclusive to `/codexreview` agent #4; the
|
|
1159
|
+
per-card Phase 3.7 gate now skips that duplicate (lean mode), so THIS pass MUST carry it.
|
|
1160
|
+
Report as a doc finding with the conflicting code location + the doc it violates.
|
|
1154
1161
|
```
|
|
1155
1162
|
Doc-reviewer collects findings WITHOUT making changes.
|
|
1156
1163
|
14. **Obsidian Corpus Dispatch**: Parse section H from doc-reviewer findings. If `Trigger: YES`, dispatch the `obsidian-sync` agent (`.claude/agents/obsidian-sync.md`) with the listed paths after all fixes are applied (step 16). If `Trigger: NO`, skip. This is non-blocking -- do not wait for obsidian-sync to complete before proceeding.
|
|
@@ -1265,15 +1272,46 @@ Log the detector output in the tracker for traceability. **This step never short
|
|
|
1265
1272
|
|
|
1266
1273
|
#### Step C — Invoke per-card `/codexreview` (ALWAYS)
|
|
1267
1274
|
|
|
1268
|
-
For EVERY card (no conditional skip):
|
|
1275
|
+
For EVERY card (no conditional skip — the gate ALWAYS runs; only its DEPTH varies):
|
|
1276
|
+
|
|
1277
|
+
0. **Select the review profile (deterministic — Review Profile Selector, since v3.35.0)** from
|
|
1278
|
+
signals ALREADY computed (no new thresholds):
|
|
1279
|
+
- **`light`** ⟺ Step A matched **zero** high-risk triggers **AND** this card's QA profile
|
|
1280
|
+
(Phase 3.5) ∈ {`skip`, `light`}.
|
|
1281
|
+
- **`full`** ⟺ otherwise (any trigger matched, OR QA ∈ {`balanced`, `deep`}).
|
|
1282
|
+
|
|
1283
|
+
`light` is **NOT** a skip: `/codexreview` still runs `code-reviewer` + Codex adversarial + the
|
|
1284
|
+
Step 3 false-positive gate. It drops only the breadth agents (qa-sentinel, api-perf-cost-auditor,
|
|
1285
|
+
doc-reviewer) and Step 3.5 CoVe — exactly the passes that yield ~nothing on a 0-trigger, QA-light
|
|
1286
|
+
diff. The BUG-0530 invariant ("never miss a blocker on a low-risk path") holds because the Codex
|
|
1287
|
+
adversarial pass — the one that catches those — always runs.
|
|
1288
|
+
**DATA GATE (BLOCKING for `light`)**: until the consumer's telemetry (`docs/metrics/` Fix
|
|
1289
|
+
Application Log) confirms that `light`-eligible cards have NOT historically produced verified
|
|
1290
|
+
BLOCKER/HIGH at FULL depth, hard-code `profile = full` here. Flip to the selector only after the
|
|
1291
|
+
data passes (no config key — change the line).
|
|
1292
|
+
|
|
1293
|
+
Then dump the card diff (reviewer grounding) and write the lean contract `/codexreview` consumes:
|
|
1294
|
+
```bash
|
|
1295
|
+
cd <worktree-path>
|
|
1296
|
+
git diff develop...HEAD -- <card files from ownership map> > /tmp/diff-<CARD-ID>.txt 2>/dev/null \
|
|
1297
|
+
|| git diff HEAD~1 > /tmp/diff-<CARD-ID>.txt
|
|
1298
|
+
```
|
|
1299
|
+
Write `/tmp/codexreview-lean-<CARD-ID>.json`:
|
|
1300
|
+
```json
|
|
1301
|
+
{ "profile": "light|full", "arch_baseline_path": "/tmp/arch-baseline-<CARD-ID>.md",
|
|
1302
|
+
"diff_summary_path": "/tmp/diff-<CARD-ID>.txt", "skip_doc_reviewer": true }
|
|
1303
|
+
```
|
|
1304
|
+
`skip_doc_reviewer` is ALWAYS true: Phase 3 already ran `doc-reviewer` on this diff (with the
|
|
1305
|
+
spec/docs-drift→bug lens since v3.35.0), so `/codexreview` agent #4 would be a duplicate.
|
|
1269
1306
|
|
|
1270
1307
|
1. **Log gate invocation** in the tracker under `## Pre-Merge Codex Review`:
|
|
1271
1308
|
|
|
1272
1309
|
```
|
|
1273
1310
|
## Pre-Merge Codex Review
|
|
1274
1311
|
- Triggered: unconditional (mandatory pre-merge gate)
|
|
1312
|
+
- Profile: light | full — reason: <triggers=0, qa=light | trigger #N matched | qa=deep>
|
|
1275
1313
|
- Matched high-risk triggers: <list from detector, or "none">
|
|
1276
|
-
- Action: invoking /codexreview <CARD-ID> (per-card, pre-merge gate)
|
|
1314
|
+
- Action: invoking /codexreview <CARD-ID> (per-card, pre-merge gate; lean contract written)
|
|
1277
1315
|
```
|
|
1278
1316
|
|
|
1279
1317
|
2. **Invoke `/codexreview`** for the single card via the Skill tool:
|
|
@@ -1283,7 +1321,10 @@ For EVERY card (no conditional skip):
|
|
|
1283
1321
|
args: <CARD-ID>
|
|
1284
1322
|
```
|
|
1285
1323
|
|
|
1286
|
-
|
|
1324
|
+
`/codexreview` Step -0.5 consumes `/tmp/codexreview-lean-<CARD-ID>.json`: reuses the Phase-1
|
|
1325
|
+
architecture baseline (no re-spawn), drops the duplicate doc-reviewer, and for `profile: light`
|
|
1326
|
+
also drops qa-sentinel + api-perf-cost-auditor + Step 3.5 CoVe. Codex adversarial + Step 3
|
|
1327
|
+
FP-gate + Step 4 report ALWAYS run. (`full` = standard pipeline minus the duplicate doc-reviewer.)
|
|
1287
1328
|
|
|
1288
1329
|
3. **Read the consolidated report** from `/tmp/codexreview-report-<TIMESTAMP>.md`. Extract:
|
|
1289
1330
|
- Verified BLOCKER count
|
|
@@ -1359,6 +1400,21 @@ Never block the pipeline indefinitely — recover and continue per the rules abo
|
|
|
1359
1400
|
|
|
1360
1401
|
Once ALL cards are committed in the worktree:
|
|
1361
1402
|
|
|
1403
|
+
> **Final-review scope gate (deterministic, since v3.35.0)** — the per-card Phase 3.7 `/codexreview`
|
|
1404
|
+
> gate has ALREADY reviewed every card individually before its commit. The final review exists to
|
|
1405
|
+
> catch what a per-card pass structurally cannot: **cross-card interactions**. Branch on batch size:
|
|
1406
|
+
>
|
|
1407
|
+
> - **Single card (N=1)**: SKIP Steps F.1–F.4 (the lone card is byte-identical to what Phase 3.7
|
|
1408
|
+
> already passed — re-review adds nothing). Log:
|
|
1409
|
+
> `"Final review (F.1–F.4) skipped — single card already passed /codexreview at Phase 3.7 (PASS), code unchanged since commit"`.
|
|
1410
|
+
> Then jump to **Step F.5 and RUN the final build** (`npm run lint && npx tsc --noEmit && npm run build`)
|
|
1411
|
+
> — that gate is NOT skipped.
|
|
1412
|
+
> - **Multiple cards (N>1)**: run F.1–F.5 but **scope the review to the cross-card surface only**
|
|
1413
|
+
> (computed, not discretionary) — see F.1 step 4. If that surface is empty, log
|
|
1414
|
+
> `"cross-card review: no shared surface (intersection=∅, depends_on=∅) — F.3 limited to integration sanity"`
|
|
1415
|
+
> and run a minimal integration pass (build + type-check across the union) instead of a full
|
|
1416
|
+
> per-line re-review. This is a computed scope reduction, NEVER a model-decided skip.
|
|
1417
|
+
|
|
1362
1418
|
### Step F.1 — Resolve scope
|
|
1363
1419
|
|
|
1364
1420
|
1. **Read the tracker file** to get the full picture: card IDs, files changed, commit hashes.
|
|
@@ -1368,7 +1424,11 @@ Once ALL cards are committed in the worktree:
|
|
|
1368
1424
|
git diff --name-only <base-branch>...HEAD
|
|
1369
1425
|
```
|
|
1370
1426
|
3. Read each card's backlog YAML to collect `acceptance_criteria`, `files_likely_touched`.
|
|
1371
|
-
4. Build `review_scope_files` as union of card-indicated files + git-touched files.
|
|
1427
|
+
4. Build `review_scope_files` as union of card-indicated files + git-touched files. **For N>1 (per
|
|
1428
|
+
the Final-review scope gate above), then REDUCE `review_scope_files` to the cross-card subset**:
|
|
1429
|
+
files appearing in ≥2 cards' File Ownership Map entries ∪ files of any `depends_on`-linked card
|
|
1430
|
+
pair. Record both the full union and the reduced set in the tracker; downstream F.3 reviews the
|
|
1431
|
+
reduced set.
|
|
1372
1432
|
|
|
1373
1433
|
### Step F.2 — Architecture baseline
|
|
1374
1434
|
|
|
@@ -2020,6 +2080,11 @@ Return: file paths, type signatures, existing patterns. Max 30 lines.
|
|
|
2020
2080
|
|
|
2021
2081
|
This is the ONLY context the orchestrator accumulates per group. After passing it to the coders, it can be purged.
|
|
2022
2082
|
|
|
2083
|
+
**Persist for review reuse (since v3.35.0)** — also write these findings to
|
|
2084
|
+
`/tmp/arch-baseline-group-<FIRST-CARD-ID>.md`. D.4b's per-card `/codexreview` lean contract points
|
|
2085
|
+
its `arch_baseline_path` at this file, so the review reuses the group baseline instead of re-spawning
|
|
2086
|
+
`codebase-architect`.
|
|
2087
|
+
|
|
2023
2088
|
#### Step B: Spawn parallel coder agents
|
|
2024
2089
|
|
|
2025
2090
|
For each card in the current group, spawn a coder agent using the Agent tool. ALL agents for the group MUST be spawned in a **SINGLE message** (multiple Agent tool calls) to run truly in parallel.
|
|
@@ -2112,7 +2177,7 @@ After ALL agents in the group complete successfully:
|
|
|
2112
2177
|
|
|
2113
2178
|
1. **D.1 — Build verification (group)** — Run `npm run build` in the worktree to verify combined changes compile. If build fails, identify which card's changes broke it (from `git diff` per card), spawn a targeted fix-coder for those files only.
|
|
2114
2179
|
|
|
2115
|
-
2. **D.2 — Combined static review (group)** — Invoke **code-reviewer** + **doc-reviewer** IN PARALLEL (read-only
|
|
2180
|
+
2. **D.2 — Combined static review (group)** — Invoke **code-reviewer** + **doc-reviewer** IN PARALLEL (read-only) across ALL changes in the group. ONE combined pass — catches cross-card issues. The doc-reviewer MUST **attribute every finding to a specific card** (by file → ownership map) and apply the full Phase 3 mandate INCLUDING the spec/docs-drift→bug lens (since v3.35.0), so this single pass satisfies each card's doc gate. D.4a consumes these per-card findings (no second doc-reviewer spawn).
|
|
2116
2181
|
|
|
2117
2182
|
3. **D.3 — Apply fixes (group)** — If D.2 findings exist, spawn ONE fix-coder to apply all fixes in a single pass. Run build + lint after.
|
|
2118
2183
|
|
|
@@ -2124,9 +2189,9 @@ After ALL agents in the group complete successfully:
|
|
|
2124
2189
|
|
|
2125
2190
|
4. **D.4 — QA gate (group)** — Select the HIGHEST QA profile across all cards in the group (e.g., if one card is BALANCED and another is DEEP, use DEEP). Invoke **qa-sentinel** once for the group.
|
|
2126
2191
|
|
|
2127
|
-
4a. **D.4a —
|
|
2192
|
+
4a. **D.4a — Per-card doc gate (consumes D.2, since v3.35.0)** — Do NOT re-spawn `doc-reviewer`. For EACH card in the group, take that card's doc findings from D.2's per-card-attributed output; if any exist, spawn a targeted fix-coder for that card only. (Previously D.4a re-ran doc-reviewer per card — a duplicate of D.2; collapsed to a single attributed D.2 pass. D.4b's per-card `/codexreview` also skips its doc-reviewer via the lean contract, so doc-review now runs once per group instead of ~3× per card.)
|
|
2128
2193
|
|
|
2129
|
-
4b. **D.4b — Pre-Merge Codex Review Gate (per-card, MANDATORY — UNCONDITIONAL)** — Invoke `/codexreview` for EACH card in the group, one at a time, BEFORE any commit. This mirrors Phase 3.7 of the sequential path and is non-skippable regardless of file paths or perceived risk. Apply the same fix sub-loop: if the consolidated report shows verified BLOCKER/HIGH findings, spawn a fix-coder and re-run `/codexreview <CARD-ID>` (max 2 retries per card). If still BLOCKER/HIGH after retries, ask the user before proceeding to D.5. The D.5 commits MUST NOT happen until every card in the group has a PASS verdict (or explicit user override via `AskUserQuestion`). Log results in the tracker under `## Pre-Merge Codex Review` per card.
|
|
2194
|
+
4b. **D.4b — Pre-Merge Codex Review Gate (per-card, MANDATORY — UNCONDITIONAL)** — Invoke `/codexreview` for EACH card in the group, one at a time, BEFORE any commit. This mirrors Phase 3.7 of the sequential path and is non-skippable regardless of file paths or perceived risk. Apply the same fix sub-loop: if the consolidated report shows verified BLOCKER/HIGH findings, spawn a fix-coder and re-run `/codexreview <CARD-ID>` (max 2 retries per card). If still BLOCKER/HIGH after retries, ask the user before proceeding to D.5. The D.5 commits MUST NOT happen until every card in the group has a PASS verdict (or explicit user override via `AskUserQuestion`). Log results in the tracker under `## Pre-Merge Codex Review` per card. **Lean + profile (since v3.35.0)**: before each card's `/codexreview`, apply the same Review Profile Selector and write the same `/tmp/codexreview-lean-<CARD-ID>.json` contract as sequential Phase 3.7 Step C — `arch_baseline_path` pointing at `/tmp/arch-baseline-group-<FIRST-CARD-ID>.md`, `skip_doc_reviewer: true`, and `profile: light|full` from (0 triggers + QA ∈ {skip,light}). LIGHT still runs Codex adversarial + code-reviewer + FP-gate (subject to the same DATA GATE: keep `full` until telemetry confirms).
|
|
2130
2195
|
|
|
2131
2196
|
5. **D.5 — Commit** — One commit per card using explicit staging from the ownership map. **NO stash in worktrees** (stashes are globally shared via `refs/stash` — see Phase 4 WORKTREE COMMIT RULE):
|
|
2132
2197
|
```bash
|
|
@@ -2178,7 +2243,7 @@ Before starting group N, verify ALL cards in groups 0..N-1 are status: done. If
|
|
|
2178
2243
|
|
|
2179
2244
|
### Post-batch (same as sequential mode)
|
|
2180
2245
|
|
|
2181
|
-
After all groups are complete, run the same Final Review, Phase 6 (merge), and Phase 7 (production readiness) as documented above.
|
|
2246
|
+
After all groups are complete, run the same Final Review, Phase 6 (merge), and Phase 7 (production readiness) as documented above. Team mode is always N>1, so the Final Review applies its deterministic **cross-card-only scope** (per the Final-review scope gate) — not a full per-line re-review.
|
|
2182
2247
|
|
|
2183
2248
|
---
|
|
2184
2249
|
|
package/package.json
CHANGED
package/src/commands/add.js
CHANGED
|
@@ -14,6 +14,24 @@ async function add(repo, options) {
|
|
|
14
14
|
// rm -rf `.framework/` themselves, so `git.frameworkExists()` is false
|
|
15
15
|
// here and the destructive prompt is never reached.
|
|
16
16
|
const nonInteractive = options && options.yes === true;
|
|
17
|
+
// When `add` is invoked programmatically (e.g. `update --reset`) the caller
|
|
18
|
+
// wants failures to bubble up so it can roll back, instead of add()'s default
|
|
19
|
+
// `process.exit(1)` which would kill the parent before it can recover.
|
|
20
|
+
const throwOnError = options && options.throwOnError === true;
|
|
21
|
+
|
|
22
|
+
// Repo resolution safety net. The CLI entrypoint (bin/baldart.js) defaults
|
|
23
|
+
// `repo` to the upstream before calling here, but programmatic callers may
|
|
24
|
+
// pass undefined. Resolve from the consumer's ledger (preserves forks) so
|
|
25
|
+
// we never reach git.addSubtree() with an undefined repo (the crash that
|
|
26
|
+
// broke `update --reset`: "Cannot read properties of undefined").
|
|
27
|
+
if (!repo) repo = State.resolveRepo();
|
|
28
|
+
if (!repo) {
|
|
29
|
+
UI.error('No framework repository to install from.');
|
|
30
|
+
UI.info('Expected from .baldart/state.json → framework_repo. Recover with:');
|
|
31
|
+
UI.info(' npx baldart add <owner/repo> --yes');
|
|
32
|
+
if (throwOnError) throw new Error('add: repository unresolved (state.json framework_repo empty)');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
17
35
|
|
|
18
36
|
try {
|
|
19
37
|
// Step 1: Verify environment
|
|
@@ -114,10 +132,16 @@ async function add(repo, options) {
|
|
|
114
132
|
UI.error('.framework/ is STILL ignored after auto-heal — install would fail.');
|
|
115
133
|
UI.info(`Source: ${heal.recheck.source}:${heal.recheck.line} (${heal.recheck.pattern})`);
|
|
116
134
|
UI.info('Resolve by removing that rule, then re-run `baldart add`.');
|
|
117
|
-
|
|
135
|
+
// Deliberate abort — flag it so the local catch propagates instead of
|
|
136
|
+
// downgrading to a warning, and let the single outer catch decide
|
|
137
|
+
// throw (throwOnError) vs process.exit (CLI).
|
|
138
|
+
const e = new Error('.framework/ still ignored after auto-heal');
|
|
139
|
+
e.fatalInstall = true;
|
|
140
|
+
throw e;
|
|
118
141
|
}
|
|
119
142
|
}
|
|
120
143
|
} catch (err) {
|
|
144
|
+
if (err && err.fatalInstall) throw err;
|
|
121
145
|
UI.warning(`Could not check .gitignore for .framework: ${err.message}`);
|
|
122
146
|
}
|
|
123
147
|
|
|
@@ -139,11 +163,12 @@ async function add(repo, options) {
|
|
|
139
163
|
' • No internet connection',
|
|
140
164
|
' • Repository not accessible',
|
|
141
165
|
` • Invalid repository: ${repo}`,
|
|
166
|
+
' (resolved from .baldart/state.json → framework_repo, else default)',
|
|
142
167
|
'',
|
|
143
168
|
'Solution:',
|
|
144
169
|
' • Check your internet connection',
|
|
145
|
-
' • Verify repository exists',
|
|
146
|
-
' •
|
|
170
|
+
' • Verify the repository exists / is reachable',
|
|
171
|
+
' • Recover with an explicit repo: npx baldart add <owner/repo> --yes'
|
|
147
172
|
]);
|
|
148
173
|
throw error;
|
|
149
174
|
}
|
|
@@ -293,6 +318,13 @@ async function add(repo, options) {
|
|
|
293
318
|
: `Hooks missing after install: ${verify.missing.join(', ')}. Install is NOT complete.`
|
|
294
319
|
);
|
|
295
320
|
UI.info('Run `npx baldart doctor` to repair, then re-check `npx baldart status`.');
|
|
321
|
+
if (throwOnError) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
verify.malformed
|
|
324
|
+
? 'settings.json malformed — hooks unverifiable'
|
|
325
|
+
: `hooks missing after install: ${verify.missing.join(', ')}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
296
328
|
process.exit(1);
|
|
297
329
|
}
|
|
298
330
|
|
|
@@ -339,6 +371,9 @@ async function add(repo, options) {
|
|
|
339
371
|
|
|
340
372
|
} catch (error) {
|
|
341
373
|
UI.error(`Installation failed: ${error.message}`);
|
|
374
|
+
// Programmatic callers (update --reset) need the failure to bubble so they
|
|
375
|
+
// can roll back; the CLI entrypoint keeps the historical exit-1 behavior.
|
|
376
|
+
if (throwOnError) throw error;
|
|
342
377
|
process.exit(1);
|
|
343
378
|
}
|
|
344
379
|
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -36,7 +36,6 @@ const UpdateNotifier = require('../utils/update-notifier');
|
|
|
36
36
|
const cliPackageJson = require('../../package.json');
|
|
37
37
|
|
|
38
38
|
const FRAMEWORK_DIR = '.framework';
|
|
39
|
-
const REPO_DEFAULT = 'antbald/BALDART';
|
|
40
39
|
const CONFIG_FILE = 'baldart.config.yml';
|
|
41
40
|
|
|
42
41
|
// ──────────────────────────────────────────────────────────────────────
|
|
@@ -214,7 +213,7 @@ async function detectState(cwd, opts = {}) {
|
|
|
214
213
|
};
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
state.remote = await describeRemote(git, ledger.framework_repo ||
|
|
216
|
+
state.remote = await describeRemote(git, ledger.framework_repo || State.DEFAULT_REPO, !!opts.offline);
|
|
218
217
|
state.commitsAhead = await commitsAheadOfRemote(git, state.remote);
|
|
219
218
|
state.workingTreeDirty = await localFrameworkChanges(git);
|
|
220
219
|
|
|
@@ -360,7 +359,9 @@ function planActions(state) {
|
|
|
360
359
|
autoOk: false,
|
|
361
360
|
run: async () => {
|
|
362
361
|
const add = require('./add');
|
|
363
|
-
|
|
362
|
+
// Fork-aware: resolve from the ledger (preserves a consumer's fork)
|
|
363
|
+
// rather than hardcoding the upstream default.
|
|
364
|
+
await add(State.resolveRepo(state.cwd), { branch: 'main' });
|
|
364
365
|
},
|
|
365
366
|
});
|
|
366
367
|
return actions;
|
package/src/commands/update.js
CHANGED
|
@@ -255,6 +255,15 @@ async function runReset(git, options, autoYes) {
|
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
// Resolve the upstream repo BEFORE anything destructive. The reinstall hands
|
|
259
|
+
// off to add() programmatically (no bin-layer default), so an unresolved repo
|
|
260
|
+
// would surface as "Downloading from undefined" / "Cannot read properties of
|
|
261
|
+
// undefined" AFTER .framework/ was already wiped and committed. Resolving here
|
|
262
|
+
// (state.framework_repo → default, never undefined) makes that unreachable and
|
|
263
|
+
// preserves a consumer's fork.
|
|
264
|
+
const repo = State.resolveRepo();
|
|
265
|
+
UI.info(`Reinstall source: ${repo}`);
|
|
266
|
+
|
|
258
267
|
// Safety gate #3: backup tag before doing anything destructive.
|
|
259
268
|
const backupTag = await git.createBackupTag();
|
|
260
269
|
UI.success(`Backup tag created: ${backupTag}`);
|
|
@@ -310,12 +319,42 @@ async function runReset(git, options, autoYes) {
|
|
|
310
319
|
UI.newline();
|
|
311
320
|
UI.info('Re-installing framework via `baldart add`…');
|
|
312
321
|
const addCmd = require('./add');
|
|
313
|
-
// `add(repo, options)` —
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
|
|
322
|
+
// `add(repo, options)` — the repo MUST be threaded explicitly: the bin-layer
|
|
323
|
+
// default ('antbald/BALDART') only applies when add is invoked from the CLI,
|
|
324
|
+
// not on this programmatic path (the v3.28.1 comment claiming "undefined →
|
|
325
|
+
// default from package.json" was wrong and caused the undefined-repo crash).
|
|
326
|
+
// `throwOnError` makes add() re-throw instead of process.exit(1), so a failed
|
|
327
|
+
// reinstall lands in the catch below and we can roll back to the backup tag —
|
|
328
|
+
// the consumer is NEVER left committed-without-.framework/.
|
|
329
|
+
try {
|
|
330
|
+
await addCmd(repo, { yes: true, throwOnError: true });
|
|
331
|
+
} catch (err) {
|
|
332
|
+
UI.newline();
|
|
333
|
+
UI.error(`Reinstall failed: ${err.message}`);
|
|
334
|
+
UI.warning('Rolling back to pre-reset state…');
|
|
335
|
+
try {
|
|
336
|
+
await git.git.raw(['reset', '--hard', backupTag]);
|
|
337
|
+
// The clean-tree gate (hasCleanWorkingTree → status.isClean(), which counts
|
|
338
|
+
// untracked) guaranteed ZERO untracked files at backup-tag time, so every
|
|
339
|
+
// untracked file now is partial-install cruft created by the failed add()
|
|
340
|
+
// (symlinks, copied templates) — safe to sweep. `-d` dirs, no `-x` so
|
|
341
|
+
// gitignored paths are left untouched. Non-fatal: .framework/ is already
|
|
342
|
+
// restored by the reset above regardless.
|
|
343
|
+
try {
|
|
344
|
+
await git.git.raw(['clean', '-fd']);
|
|
345
|
+
} catch (cleanErr) {
|
|
346
|
+
UI.warning(`Could not sweep partial-install leftovers (${cleanErr.message}). .framework/ is restored; run \`git clean -fd\` to tidy.`);
|
|
347
|
+
}
|
|
348
|
+
UI.success(`Restored .framework/ from ${backupTag}. Working tree back to pre-reset state.`);
|
|
349
|
+
} catch (rollbackErr) {
|
|
350
|
+
UI.error(`Auto-rollback FAILED: ${rollbackErr.message}`);
|
|
351
|
+
UI.info(`Recover manually: git reset --hard ${backupTag}`);
|
|
352
|
+
}
|
|
353
|
+
UI.newline();
|
|
354
|
+
UI.info('Then retry, or reinstall explicitly:');
|
|
355
|
+
UI.info(` npx baldart add ${repo} --yes`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
319
358
|
|
|
320
359
|
// Post-restore sanity check — confirm nothing user-owned got clobbered.
|
|
321
360
|
const stillPresent = {
|
package/src/commands/version.js
CHANGED
|
@@ -3,7 +3,6 @@ const UI = require('../utils/ui');
|
|
|
3
3
|
const State = require('../utils/state');
|
|
4
4
|
const UpdateNotifier = require('../utils/update-notifier');
|
|
5
5
|
|
|
6
|
-
const REPO_DEFAULT = 'antbald/BALDART';
|
|
7
6
|
const FRAMEWORK_DIR = '.framework';
|
|
8
7
|
|
|
9
8
|
function fmtDate(iso) {
|
|
@@ -61,7 +60,7 @@ async function version(opts = {}) {
|
|
|
61
60
|
const verbose = opts.verbose === true;
|
|
62
61
|
|
|
63
62
|
// SSOT (v3.25.0+): one query, version-compare authority.
|
|
64
|
-
const repo = state.framework_repo ||
|
|
63
|
+
const repo = state.framework_repo || State.DEFAULT_REPO;
|
|
65
64
|
let status;
|
|
66
65
|
if (opts.offline === true) {
|
|
67
66
|
status = {
|
package/src/utils/git.js
CHANGED
|
@@ -144,6 +144,17 @@ class GitUtils {
|
|
|
144
144
|
// - "https://github.com/owner/repo" -> "https://github.com/owner/repo.git"
|
|
145
145
|
// - "https://github.com/owner/repo.git" -> unchanged
|
|
146
146
|
|
|
147
|
+
// Fail loud and legible instead of "Cannot read properties of undefined
|
|
148
|
+
// (reading 'startsWith')". A falsy repo here means a caller skipped repo
|
|
149
|
+
// resolution (see State.resolveRepo) — surface that, don't crash cryptically.
|
|
150
|
+
if (!repo || typeof repo !== 'string') {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`normalizeRepoUrl: repository non specificato (got ${JSON.stringify(repo)}). ` +
|
|
153
|
+
`Risolvilo via .baldart/state.json → framework_repo o passalo esplicito ` +
|
|
154
|
+
`(es. npx baldart add <owner/repo> --yes).`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
147
158
|
if (repo.startsWith('http://') || repo.startsWith('https://')) {
|
|
148
159
|
return repo.endsWith('.git') ? repo : `${repo}.git`;
|
|
149
160
|
}
|
package/src/utils/state.js
CHANGED
|
@@ -35,6 +35,13 @@ const STATE_FILE = path.join(STATE_DIR, 'state.json');
|
|
|
35
35
|
const STATE_VERSION = 1;
|
|
36
36
|
const HISTORY_LIMIT = 20;
|
|
37
37
|
|
|
38
|
+
// Upstream framework repository. SSOT for the "where do we install from"
|
|
39
|
+
// default — duplicated literals in version.js / doctor.js / recordInstall
|
|
40
|
+
// used to drift independently. Always resolve through `resolveRepo()` so a
|
|
41
|
+
// consumer's fork (recorded in state.framework_repo at install time) is
|
|
42
|
+
// preserved instead of being silently overridden by this constant.
|
|
43
|
+
const DEFAULT_REPO = 'antbald/BALDART';
|
|
44
|
+
|
|
38
45
|
function defaultState() {
|
|
39
46
|
return {
|
|
40
47
|
state_version: STATE_VERSION,
|
|
@@ -87,7 +94,7 @@ function recordInstall({ version, repo }, cwd = process.cwd()) {
|
|
|
87
94
|
const now = new Date().toISOString();
|
|
88
95
|
const wasFirstInstall = !state.installed_version;
|
|
89
96
|
state.installed_version = version;
|
|
90
|
-
state.framework_repo = repo || state.framework_repo ||
|
|
97
|
+
state.framework_repo = repo || state.framework_repo || DEFAULT_REPO;
|
|
91
98
|
if (wasFirstInstall) state.install_date = now;
|
|
92
99
|
state.history = appendHistory(state, {
|
|
93
100
|
event: wasFirstInstall ? 'install' : 'reinstall',
|
|
@@ -142,6 +149,20 @@ function recordPush({ from, to, description }, cwd = process.cwd()) {
|
|
|
142
149
|
return state;
|
|
143
150
|
}
|
|
144
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Resolve the upstream framework repository to install/update from. Cascade:
|
|
154
|
+
* `state.framework_repo` (recorded at install — preserves a consumer's fork)
|
|
155
|
+
* → DEFAULT_REPO. Never returns undefined, so callers that thread the result
|
|
156
|
+
* into git.addSubtree() / normalizeRepoUrl() can't trip the "undefined repo"
|
|
157
|
+
* crash that broke `update --reset` (it called add() with no repo).
|
|
158
|
+
*
|
|
159
|
+
* `baldart.config.yml` and the subtree's git remote are intentionally NOT
|
|
160
|
+
* consulted — neither stores the repo, so they'd be dead lookups.
|
|
161
|
+
*/
|
|
162
|
+
function resolveRepo(cwd = process.cwd()) {
|
|
163
|
+
return load(cwd).framework_repo || DEFAULT_REPO;
|
|
164
|
+
}
|
|
165
|
+
|
|
145
166
|
/**
|
|
146
167
|
* Read the framework's VERSION file from .framework/VERSION. Used by version /
|
|
147
168
|
* status commands to cross-check what the symlinked framework reports vs what
|
|
@@ -164,7 +185,9 @@ module.exports = {
|
|
|
164
185
|
recordUpdate,
|
|
165
186
|
recordPush,
|
|
166
187
|
reconcileInstalledVersion,
|
|
188
|
+
resolveRepo,
|
|
167
189
|
readFrameworkVersion,
|
|
190
|
+
DEFAULT_REPO,
|
|
168
191
|
STATE_FILE,
|
|
169
192
|
STATE_VERSION,
|
|
170
193
|
};
|