baldart 4.95.0 → 4.96.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/CHANGELOG.md +11 -0
- package/VERSION +1 -1
- package/framework/docs/ROOT-PRIMITIVES.md +1 -0
- package/framework/templates/baldart.config.template.yml +9 -0
- package/framework/templates/primitives/AGENTS.CHANGELOG.md +13 -0
- package/framework/templates/primitives/AGENTS.md +4 -2
- package/package.json +1 -1
- package/src/commands/configure.js +35 -0
- package/src/commands/update.js +6 -0
- package/src/utils/root-primitives.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ 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
|
+
## [4.96.0] - 2026-07-02
|
|
9
|
+
|
|
10
|
+
**Protected branch decoupled from the integration trunk — `git.protected_branch`.** The root `AGENTS.md` primitive expressed the "NEVER push directly to X" MUST via `{{ git.trunk_branch }}`, conflating two distinct roles that in a two-branch gitflow are different branches: the *integration trunk* (worktree base + feature PR target in `/prd` + `/new`) and the *protected branch* (production/release line that must not receive direct pushes). A consumer with `trunk_branch: develop` therefore had its agents (Claude + Codex) refuse a direct `develop` push — even though `develop` was the integration branch they push to and `main` was the truly-protected line. `trunk_branch` could not simply be flipped to `main` because it also drives worktree creation (`update.js`), which must branch from `develop`.
|
|
11
|
+
|
|
12
|
+
- **New optional config key `git.protected_branch`** — the branch the "never push directly" rule protects. **Defaults to `git.trunk_branch`** when omitted, so single-trunk repos are byte-unchanged; a two-branch gitflow sets it explicitly (e.g. `protected_branch: main` while `trunk_branch: develop` stays the worktree base + PR target).
|
|
13
|
+
- **Primitive `AGENTS.md` skeleton → 1.5.0**: the push MUST now resolves `{{ git.protected_branch }}` and explicitly permits direct pushes to other branches (including the integration trunk when it differs) when the owner authorizes it.
|
|
14
|
+
- **Slot resolution** (`src/utils/root-primitives.js`): `git.protected_branch` resolves to `git.protected_branch || git.trunk_branch || 'main'`.
|
|
15
|
+
- **Schema-change propagation** (per the rule): template (`baldart.config.template.yml` — new commented key under `git:`), `configure` (new `detectProtectedBranch` probe + serialized `git.protected_branch` + `_probes`), `update` detector (already diffs all `git.*` keys → surfaces `git.protected_branch`; added a dedicated advisory), CHANGELOG. No doctor diagnostic needed (the update advisory + configure autodetect cover it).
|
|
16
|
+
|
|
17
|
+
**MINOR** — new optional config key, back-compat default, no install-layout change. **Codex parity: portable** — pure slot resolution in the shared `AGENTS.md` skeleton (transpiled to `.codex/agents/*` unchanged; the generated `AGENTS.md` is the cross-tool SSOT read by both runtimes).
|
|
18
|
+
|
|
8
19
|
## [4.95.0] - 2026-07-02
|
|
9
20
|
|
|
10
21
|
**`/prd` — applier economico + EARS+verify (i due interventi strutturali dal forensics della deep analysis).**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.96.0
|
|
@@ -44,6 +44,7 @@ key was introduced:
|
|
|
44
44
|
| Commands table | `toolchain.commands.*` → `package.json` scripts (rows with no source are omitted) |
|
|
45
45
|
| Repo map | non-empty `paths.*` + a derived source root |
|
|
46
46
|
| `git.trunk_branch` | `git.trunk_branch` |
|
|
47
|
+
| `git.protected_branch` | `git.protected_branch` → `git.trunk_branch` (default) — the "never push directly" branch; differs from the trunk only in a two-branch gitflow |
|
|
47
48
|
| feature gates | `features.has_backlog` / `has_adrs` / `has_i18n` / `has_design_system` |
|
|
48
49
|
|
|
49
50
|
## Overlay
|
|
@@ -416,6 +416,15 @@ git:
|
|
|
416
416
|
# (falling back to asking the user). Default kept as "develop" for back-compat.
|
|
417
417
|
trunk_branch: develop
|
|
418
418
|
|
|
419
|
+
# Protected branch — the branch that MUST NOT receive direct pushes (the
|
|
420
|
+
# production/release line). Skills resolve this key for the "never push
|
|
421
|
+
# directly to X" rule. DEFAULTS to `trunk_branch` when omitted, so single-trunk
|
|
422
|
+
# repos need not set it. Set it explicitly ONLY for a two-branch gitflow where
|
|
423
|
+
# the protected line differs from the integration trunk — e.g.
|
|
424
|
+
# `protected_branch: main` while `trunk_branch: develop` (features integrate
|
|
425
|
+
# into develop; main is reached only via a release/hotfix PR).
|
|
426
|
+
protected_branch: develop
|
|
427
|
+
|
|
419
428
|
# How `/mw` lands the feature branch onto the trunk:
|
|
420
429
|
#
|
|
421
430
|
# pr (default) — push feature branch, open a PR via `gh pr create`,
|
|
@@ -4,6 +4,19 @@ Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://sem
|
|
|
4
4
|
Questo è il changelog INTERNO dello skeleton spedito (`primitive_version`), separato dal
|
|
5
5
|
file `AGENTS.md` generato nel consumer e indipendente dal `VERSION` del framework.
|
|
6
6
|
|
|
7
|
+
## 1.5.0 — 2026-07-02
|
|
8
|
+
|
|
9
|
+
- **Protected branch decoupled from the integration trunk**: the "NEVER push directly"
|
|
10
|
+
MUST now resolves the new `{{ git.protected_branch }}` slot instead of
|
|
11
|
+
`{{ git.trunk_branch }}`, and explicitly permits direct pushes to other branches
|
|
12
|
+
(including the integration trunk when it differs) when the owner authorizes it.
|
|
13
|
+
`git.protected_branch` defaults to `git.trunk_branch`, so single-trunk repos are
|
|
14
|
+
byte-unchanged; a two-branch gitflow sets it (e.g. `protected_branch: main` while
|
|
15
|
+
`trunk_branch: develop` stays the worktree base + PR target). Fixes the case where a
|
|
16
|
+
consumer with `trunk_branch: develop` had its agents refuse a direct `develop` push
|
|
17
|
+
even though `develop` was the integration branch and `main` was the protected line.
|
|
18
|
+
Cross-tool: pure slot resolution in the shared skeleton — portable (Claude + Codex).
|
|
19
|
+
|
|
7
20
|
## 1.4.0 — 2026-07-02
|
|
8
21
|
|
|
9
22
|
- **Analysis-profile vocabulary on the understand-before-implement MUST**: the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- baldart-primitive: name=AGENTS primitive_version=1.
|
|
1
|
+
<!-- baldart-primitive: name=AGENTS primitive_version=1.5.0
|
|
2
2
|
This is the SHIPPED SKELETON. The writer (src/utils/root-primitives.js) strips
|
|
3
3
|
this banner, resolves every {{ slot }} / {{> partial }} / {{#flag}} from
|
|
4
4
|
baldart.config.yml (+ package.json/README), merges .baldart/overlays/AGENTS.md,
|
|
@@ -55,7 +55,9 @@ mechanics — this file remains the single source of truth for the protocol.
|
|
|
55
55
|
- MUST use commit format `{{ git.commit_format }}`; small, traceable commits.
|
|
56
56
|
- MUST NOT commit with failing lint/type checks. Full build only before opening a PR.
|
|
57
57
|
- MUST pre-sync (`git fetch`, clean status, confirm branch, `--ff-only`) and NEVER push
|
|
58
|
-
directly to `{{ git.
|
|
58
|
+
directly to `{{ git.protected_branch }}` — the protected branch reaches its state only
|
|
59
|
+
via PR. Other branches (including the integration branch `{{ git.trunk_branch }}` when it
|
|
60
|
+
differs) MAY be pushed to directly when the owner authorizes it.
|
|
59
61
|
- MUST get owner approval before force-push/reset or deleting an UNMERGED branch (create a
|
|
60
62
|
`backup/<YYYYMMDD>-<reason>` tag first). Deleting an already-merged branch is routine.
|
|
61
63
|
- MUST NOT hardcode secrets / tokens / API keys (env vars only), expose internal stack
|
package/package.json
CHANGED
|
@@ -102,6 +102,38 @@ function detectTrunkBranch(cwd = process.cwd()) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Autodetect the protected branch (git.protected_branch) — the branch that MUST
|
|
107
|
+
* NOT receive direct pushes (production/release line). In a two-branch gitflow
|
|
108
|
+
* this differs from the integration `trunk_branch`: features integrate into the
|
|
109
|
+
* trunk (worktree base + PR target), while a separate protected branch is only
|
|
110
|
+
* reached via release/hotfix PRs.
|
|
111
|
+
* Heuristic: if trunk is NOT already `main`/`master` and such a branch exists,
|
|
112
|
+
* that's the protected line; otherwise the protected branch IS the trunk
|
|
113
|
+
* (single-trunk repo — the back-compat default, since protected_branch defaults
|
|
114
|
+
* to trunk_branch when absent).
|
|
115
|
+
*/
|
|
116
|
+
function detectProtectedBranch(cwd = process.cwd(), trunk = 'develop') {
|
|
117
|
+
try {
|
|
118
|
+
const { execSync } = require('child_process');
|
|
119
|
+
const opts = { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, encoding: 'utf8' };
|
|
120
|
+
const branches = (() => {
|
|
121
|
+
try { return execSync('git branch --format=%(refname:short)', opts).split('\n').map((s) => s.trim()); }
|
|
122
|
+
catch (_) { return []; }
|
|
123
|
+
})();
|
|
124
|
+
if (trunk !== 'main' && trunk !== 'master') {
|
|
125
|
+
for (const cand of ['main', 'master']) {
|
|
126
|
+
if (branches.includes(cand)) {
|
|
127
|
+
return { value: cand, reason: `two-branch gitflow: '${cand}' is the protected line (trunk is '${trunk}').` };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { value: trunk, reason: `single-trunk repo — protected branch is the trunk ('${trunk}').` };
|
|
132
|
+
} catch (_) {
|
|
133
|
+
return { value: trunk, reason: `probe failed — protected branch defaults to trunk ('${trunk}').` };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
105
137
|
/**
|
|
106
138
|
* Autodetect whether this project develops in parallel git worktrees
|
|
107
139
|
* (`git worktree list` shows linked worktrees beyond the main one). Used to
|
|
@@ -371,6 +403,7 @@ function detect(cwd = process.cwd()) {
|
|
|
371
403
|
: '';
|
|
372
404
|
|
|
373
405
|
const trunkBranchProbe = detectTrunkBranch(cwd);
|
|
406
|
+
const protectedBranchProbe = detectProtectedBranch(cwd, trunkBranchProbe.value);
|
|
374
407
|
const mergeStrategyProbe = detectMergeStrategy(cwd, trunkBranchProbe.value);
|
|
375
408
|
// Default schema_deploy_from_trunk_only ON when parallel worktrees are in use
|
|
376
409
|
// (the topology prone to migration-history desync). Otherwise keep the
|
|
@@ -532,6 +565,7 @@ function detect(cwd = process.cwd()) {
|
|
|
532
565
|
// Set to plain strings — the structured probe details (`protected`,
|
|
533
566
|
// `reason`) are kept on a sibling key so mergePreserving stays type-safe.
|
|
534
567
|
trunk_branch: trunkBranchProbe.value,
|
|
568
|
+
protected_branch: protectedBranchProbe.value,
|
|
535
569
|
merge_strategy: mergeStrategyProbe.value,
|
|
536
570
|
},
|
|
537
571
|
_probes: {
|
|
@@ -539,6 +573,7 @@ function detect(cwd = process.cwd()) {
|
|
|
539
573
|
// interactivePrompts to surface the autodetection reasoning.
|
|
540
574
|
merge_strategy: mergeStrategyProbe,
|
|
541
575
|
trunk_branch: trunkBranchProbe,
|
|
576
|
+
protected_branch: protectedBranchProbe,
|
|
542
577
|
},
|
|
543
578
|
};
|
|
544
579
|
|
package/src/commands/update.js
CHANGED
|
@@ -1551,6 +1551,12 @@ async function update(options = {}, unknownArgs = []) {
|
|
|
1551
1551
|
+ 'Until it is set, those skills autodetect the trunk (origin/HEAD → develop/main/master) '
|
|
1552
1552
|
+ 'and warn on each run; run `npx baldart configure` to persist it and make it deterministic.');
|
|
1553
1553
|
}
|
|
1554
|
+
if (allMissing.includes('git.protected_branch')) {
|
|
1555
|
+
UI.warning('`git.protected_branch` is the branch the "never push directly" rule protects '
|
|
1556
|
+
+ '(AGENTS.md). It DEFAULTS to `git.trunk_branch`, so single-trunk repos are unchanged; '
|
|
1557
|
+
+ 'set it (e.g. `main`) only for a two-branch gitflow where the protected line differs from '
|
|
1558
|
+
+ 'the integration trunk (e.g. trunk `develop`, protected `main`).');
|
|
1559
|
+
}
|
|
1554
1560
|
}
|
|
1555
1561
|
}
|
|
1556
1562
|
} catch (_) {
|
|
@@ -159,6 +159,11 @@ function resolveSlots(cwd, cfg) {
|
|
|
159
159
|
'stack.summary': stackSummary,
|
|
160
160
|
'stack.node_suffix': nodeSuffix,
|
|
161
161
|
'git.trunk_branch': git.trunk_branch || 'main',
|
|
162
|
+
// The branch that MUST NOT receive direct pushes (production/protected).
|
|
163
|
+
// Defaults to trunk_branch so single-trunk repos are unchanged; two-branch
|
|
164
|
+
// gitflows set it explicitly (e.g. protected_branch: main while
|
|
165
|
+
// trunk_branch: develop is the integration/PR target + worktree base).
|
|
166
|
+
'git.protected_branch': git.protected_branch || git.trunk_branch || 'main',
|
|
162
167
|
'git.feature_prefix': 'feat', // STATIC BALDART convention
|
|
163
168
|
'git.commit_format': '[FEAT-XXXX] description', // STATIC default (override via overlay)
|
|
164
169
|
'i18n.registry': paths.i18n_registry || 'the i18n registry',
|