@inixiative/config 0.5.175 → 0.5.176
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/README.md +26 -3
- package/dist/cli.js +37 -12
- package/package.json +1 -1
- package/versions.json +10 -2
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ One version of `@inixiative/config` names one coherent ecosystem state:
|
|
|
58
58
|
- `agentic` — the agentic lane's blessed set
|
|
59
59
|
- `consumers` — private repos that follow the blessed sets but never publish, keyed by GitHub `owner/repo`: `lanes` names the lanes each consumes, `upstream` the repo it syncs from
|
|
60
60
|
- `candidates` — published lane versions newer than their blessed one, waiting for every dependent to adopt them (see `train`); absent when there are none
|
|
61
|
+
- `holds` — lane packages kept below a version, keyed by package (see Holds below); absent when there are none
|
|
61
62
|
|
|
62
63
|
```jsonc
|
|
63
64
|
"consumers": {
|
|
@@ -86,7 +87,7 @@ bunx @inixiative/config ports [project]
|
|
|
86
87
|
|
|
87
88
|
`ports` prints each project's local ports from `ports.json` (also exported as `@inixiative/config/ports.json`). A project's port for a service is the service's usual default plus 100 times the project's block, so no two projects collide. Zealot alone sits at block 0, the defaults (Aron, 2026-10-20: it already runs there); every other project is off the defaults: Archive (block 7) serves HTTP on 4700 with Postgres on 6132; Kingdom (block 2) has web 3200, API 8200, Postgres 5632, Redis 6579. Reserved ports (macOS AirPlay on 5000 and 7000) are never assigned. Give a new project the next free block (never 0), and a new kind of service its usual default; the tests fail on any collision.
|
|
88
89
|
|
|
89
|
-
`check` is read-only and exits non-zero on drift — run it in CI after a frozen-lockfile install. It verifies toolchain pins — declared once, at the root, pinned exactly: a workspace member declaring one is an error, and `bun.lock` must resolve each only to its pin (no second copy) — toolchain peer ranges (they must admit the pin and no older major; `sync` writes `>=<pin major>.<pin minor>.0`), `"latest"` ranges, `packageManager`/`.bun-version`, legacy `bun.lockb`, a committed (tracked, un-ignored) `bun.lock`, a gitignored `tmp/` (scratch space; `sync` adds `/tmp/*`, which keeps re-included files tracked), required scripts (`check`/`typecheck`/`lint`/`test`), lefthook (dep + `lefthook.yml` extending the shared hooks + `prepare` script; git repos only), stub `extends`, presence of this package, and for every ecosystem dependency — in the root or any workspace member, of either lane — that the declared range admits the blessed version and the lockfile actually resolves to it (the stale-lockfile class). A package with a candidate also accepts the candidate and anything between: a range admitting either passes, and so does a lock from the blessed version up to the candidate. A range that does not admit the blessed version is rewritten to `^<blessed>`, except an exact pin, which stays exact and moves to the blessed version. An npm alias (`"@inixiative/session-archive": "npm:@inixiative/archive@^0.2.1"`) is held to its target's blessed version and rewritten as an alias; `workspace:`, `file:` and `link:` ranges are a repo linking itself and are left alone.
|
|
90
|
+
`check` is read-only and exits non-zero on drift — run it in CI after a frozen-lockfile install. It verifies toolchain pins — declared once, at the root, pinned exactly: a workspace member declaring one is an error, and `bun.lock` must resolve each only to its pin (no second copy) — toolchain peer ranges (they must admit the pin and no older major; `sync` writes `>=<pin major>.<pin minor>.0`), `"latest"` ranges, `packageManager`/`.bun-version`, legacy `bun.lockb`, a committed (tracked, un-ignored) `bun.lock`, a gitignored `tmp/` (scratch space; `sync` adds `/tmp/*`, which keeps re-included files tracked), required scripts (`check`/`typecheck`/`lint`/`test`), lefthook (dep + `lefthook.yml` extending the shared hooks + `prepare` script; git repos only), stub `extends`, presence of this package, and for every ecosystem dependency — in the root or any workspace member, of either lane — that the declared range admits the blessed version and the lockfile actually resolves to it (the stale-lockfile class). A package with a candidate (one no hold covers) also accepts the candidate and anything between: a range admitting either passes, and so does a lock from the blessed version up to the candidate. A range that does not admit the blessed version is rewritten to `^<blessed>`, except an exact pin, which stays exact and moves to the blessed version. An npm alias (`"@inixiative/session-archive": "npm:@inixiative/archive@^0.2.1"`) is held to its target's blessed version and rewritten as an alias; `workspace:`, `file:` and `link:` ranges are a repo linking itself and are left alone.
|
|
90
91
|
|
|
91
92
|
`sync` applies every fix (deleting member toolchain entries and their stale member links), re-locks via `bun install` (`--force` when a second toolchain copy survives), and runs `bun update` on stale ecosystem entries in the directory that declares them. It refuses to run on a dirty working tree without `--force`. The react preset is inferred from a `react` dependency; override with `--preset`. Missing scripts are filled with defaults; existing script bodies are never touched.
|
|
92
93
|
|
|
@@ -102,9 +103,31 @@ Blessing is two-phase, so a dependent that needs work never deadlocks a release:
|
|
|
102
103
|
2. **Adopt.** A repo (lane checkout or consumer) whose check fails against the candidates it was moved onto doesn't stop the train. Its checkout is put back as it was, and the train opens `train/adopt-<pkg>-<version>` in a worktree (the repo's own `worktree:create <base> <branch>` when it declares one, so a template-based repo gets its slot's databases, released with its `worktree:destroy`; otherwise a temporary linked worktree; either way with the repo's ignored `.env*` files copied in where missing): the same bump and re-lock, then the repo's `train:adopt` script if its package.json declares one (Foundry's re-records cassettes with `bun run test:live`), all committed, and re-locked onto the new config release so the PR's CI judges it against the candidates. A lane checkout that adopts publishes nothing this run. A consumer that passes against candidates gets the same branch name: its default branch is still on the previous versions until the branch merges. If the adoption's commit is refused (its hooks typecheck), the branch is deleted and the summary says to adopt by hand.
|
|
103
104
|
3. **Promote.** At the end of each run a candidate is blessed (moved into its lane's section) unless something holds it: its lane stopped; a repo has an adoption branch for it that is not yet on its default branch (opened this run, unmerged, or committed but never pushed); or a repo that declares it was not walked to an outcome this run — another lane's checkout, a skipped or failed one, a consumer without a checkout. A later train finds a waiting adoption branch and opens nothing new; once it merges, the repo is on the candidate and the train promotes it. A candidate every dependent already accepts is blessed in the run that published it. An adoption PR closed without merging holds its candidates until its branch is deleted.
|
|
104
105
|
|
|
105
|
-
The summary lists what was blessed, every candidate with what holds it, and each pending adoption branch with its PR when `gh` can find one.
|
|
106
|
+
The summary lists what was blessed, every candidate with what holds it, every hold with its reason, and each pending adoption branch with its PR when `gh` can find one.
|
|
106
107
|
|
|
107
|
-
A
|
|
108
|
+
**Holds.** A hold keeps a package's breaking line out of the ecosystem while its dependents can't take it yet (signet 0.12 waiting on Kingdom's grants cutover), without stopping the package's own releases or anyone else's:
|
|
109
|
+
|
|
110
|
+
```jsonc
|
|
111
|
+
"holds": {
|
|
112
|
+
"@inixiative/signet": {
|
|
113
|
+
"below": "0.12.0",
|
|
114
|
+
"reason": "0.12 breaks the grants contract that prod Kingdom (0.11) speaks",
|
|
115
|
+
"until": "the Kingdom grants cutover", // optional
|
|
116
|
+
"owner": "inixiative-bf" // optional: who lifts it
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Every version at or above `below` is held; versions below it ride the train as usual, so a patch on the old line is still recorded and blessed. A held version:
|
|
122
|
+
|
|
123
|
+
- still publishes when the package's local version is ahead of npm, and is never recorded;
|
|
124
|
+
- is never rediscovered as a candidate when it is already on npm at the local version;
|
|
125
|
+
- is never applied or blessed when it is already recorded under `candidates`: no lane checkout or consumer is moved onto it, and it stays where it is;
|
|
126
|
+
- is never accepted by `check`, which holds the package to its blessed version and any candidate below the hold.
|
|
127
|
+
|
|
128
|
+
`scan` and the train's summary print each hold with its reason, and name a held candidate as not applied. Lifting a hold is deleting its entry: the next train records the held version found on npm at the local version (or applies the held candidate) and adopts it like any other.
|
|
129
|
+
|
|
130
|
+
A root package publishes with `npm publish`; a workspace member is packed with `bun pm pack`, which rewrites `workspace:` ranges to the versions being released, and the tarball is published with `npm publish`, so OTP prompts pass through either way. A package already on npm at its local version but neither blessed nor a candidate — published by hand, or by a train whose lane later failed — becomes a candidate on the next run, unless a hold covers that version. Likewise, an `@inixiative/config` version committed ahead of npm (a release whose publish failed) is published by the next run, even when it blesses nothing new.
|
|
108
131
|
|
|
109
132
|
Consumers run once, after every selected lane, upstreams first, against the full blessed set: a consumer of both lanes ends coherent with both whichever lane ran. They get the same bump → re-lock → `check`, never a publish, and their changes go through review: the train commits them on a `train/ecosystem-sync-<date>` branch, in a worktree, and puts the checkout back on its default branch, clean.
|
|
110
133
|
|
package/dist/cli.js
CHANGED
|
@@ -53,8 +53,23 @@ var blessedVersions = (manifest2) => ({
|
|
|
53
53
|
...manifest2.agentic
|
|
54
54
|
});
|
|
55
55
|
var laneOf = (manifest2, name) => LANES.find((lane2) => name in lanePackages(manifest2, lane2)) ?? null;
|
|
56
|
+
var holdOf = (manifest2, name, version) => {
|
|
57
|
+
const hold = manifest2.holds?.[name];
|
|
58
|
+
return hold && compare(version, hold.below) >= 0 ? hold : null;
|
|
59
|
+
};
|
|
60
|
+
var activeCandidates = (manifest2) => Object.fromEntries(
|
|
61
|
+
Object.entries(manifest2.candidates ?? {}).filter(
|
|
62
|
+
([name, version]) => !holdOf(manifest2, name, version)
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
var holdLines = (manifest2) => Object.entries(manifest2.holds ?? {}).map(([name, hold]) => {
|
|
66
|
+
const terms = [hold.until && `until ${hold.until}`, hold.owner && `owner ${hold.owner}`].filter(Boolean).join("; ");
|
|
67
|
+
const candidate = manifest2.candidates?.[name];
|
|
68
|
+
const parked = candidate && holdOf(manifest2, name, candidate) ? `; candidate ${candidate} not applied` : "";
|
|
69
|
+
return `held: ${name} below ${hold.below} \u2014 ${hold.reason}${terms ? ` (${terms})` : ""}${parked}`;
|
|
70
|
+
});
|
|
56
71
|
var adoptionTarget = (manifest2) => {
|
|
57
|
-
const candidates = manifest2
|
|
72
|
+
const candidates = activeCandidates(manifest2);
|
|
58
73
|
const overlay = (section) => Object.fromEntries(
|
|
59
74
|
Object.entries(section).map(([name, version]) => [name, candidates[name] ?? version])
|
|
60
75
|
);
|
|
@@ -69,7 +84,7 @@ var promoteCandidates = (manifest2, names) => {
|
|
|
69
84
|
for (const name of names) {
|
|
70
85
|
const version = manifest2.candidates?.[name];
|
|
71
86
|
const lane2 = laneOf(manifest2, name);
|
|
72
|
-
if (!version || !lane2 || !manifest2.candidates) continue;
|
|
87
|
+
if (!version || !lane2 || !manifest2.candidates || holdOf(manifest2, name, version)) continue;
|
|
73
88
|
laneSection(manifest2, lane2)[name] = version;
|
|
74
89
|
delete manifest2.candidates[name];
|
|
75
90
|
promoted.push(`${name}@${version}`);
|
|
@@ -734,7 +749,7 @@ function inspect(dir2, manifest2, presetOverride) {
|
|
|
734
749
|
}
|
|
735
750
|
}
|
|
736
751
|
const blessedSet = blessedVersions(manifest2);
|
|
737
|
-
const candidateSet = manifest2
|
|
752
|
+
const candidateSet = activeCandidates(manifest2);
|
|
738
753
|
const staleKeys = /* @__PURE__ */ new Set();
|
|
739
754
|
for (const target of rangeTargets) {
|
|
740
755
|
for (const field of DEP_FIELDS) {
|
|
@@ -1181,9 +1196,10 @@ if (command === "scan") {
|
|
|
1181
1196
|
const missing = [...plan.lanes.flatMap((lane2) => lane2.missing), ...plan.missingConsumers];
|
|
1182
1197
|
const ambiguous = [...plan.lanes.flatMap((lane2) => lane2.ambiguous), ...plan.ambiguousConsumers];
|
|
1183
1198
|
const drifted = [];
|
|
1184
|
-
for (const [name, version] of Object.entries(manifest
|
|
1199
|
+
for (const [name, version] of Object.entries(activeCandidates(manifest))) {
|
|
1185
1200
|
console.log(`candidate: ${name}@${version} (blessed ${blessedVersions(manifest)[name]})`);
|
|
1186
1201
|
}
|
|
1202
|
+
for (const line of holdLines(manifest)) console.log(line);
|
|
1187
1203
|
for (const checkout of checkouts) {
|
|
1188
1204
|
console.log(`
|
|
1189
1205
|
${checkout.name} \u2014 ${checkout.dir} (${laneLabel(checkout)})`);
|
|
@@ -1387,7 +1403,7 @@ if (command === "train") {
|
|
|
1387
1403
|
spawnSync2("bun", ["install", "--frozen-lockfile"], { cwd: checkout.dir, stdio: "inherit" });
|
|
1388
1404
|
});
|
|
1389
1405
|
const movesOf = (checkout) => {
|
|
1390
|
-
const candidates2 = manifest
|
|
1406
|
+
const candidates2 = activeCandidates(manifest);
|
|
1391
1407
|
const names = inspect(checkout.dir, target()).findings.flatMap(
|
|
1392
1408
|
(finding) => (finding.kind === "ecosystem-range" || finding.kind === "stale-lock") && finding.target && finding.target in candidates2 ? [finding.target] : []
|
|
1393
1409
|
);
|
|
@@ -1643,15 +1659,23 @@ if (command === "train") {
|
|
|
1643
1659
|
walked.add(checkout.dir);
|
|
1644
1660
|
followers.push({ checkout, onBranch: false });
|
|
1645
1661
|
for (const { entry, local, remote, ahead: ahead2 } of releases) {
|
|
1646
|
-
if (ahead2)
|
|
1647
|
-
|
|
1648
|
-
|
|
1662
|
+
if (ahead2) publish(checkout, entry, local, remote);
|
|
1663
|
+
const hold = holdOf(manifest, entry.name, local);
|
|
1664
|
+
if (hold) {
|
|
1665
|
+
console.log(
|
|
1666
|
+
`\u23F8 ${entry.name}@${local} ${ahead2 ? "published" : "on npm"}, held below ${hold.below} (${hold.reason}) \u2014 not recorded`
|
|
1667
|
+
);
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
if (!ahead2 && section[entry.name] === local) {
|
|
1649
1671
|
console.log(`\u2713 ${entry.name}@${local} already on npm and blessed`);
|
|
1650
1672
|
continue;
|
|
1651
|
-
}
|
|
1673
|
+
}
|
|
1674
|
+
if (!ahead2 && manifest.candidates?.[entry.name] === local) {
|
|
1652
1675
|
console.log(`\u2713 ${entry.name}@${local} already on npm and a candidate`);
|
|
1653
1676
|
continue;
|
|
1654
|
-
}
|
|
1677
|
+
}
|
|
1678
|
+
if (!ahead2) {
|
|
1655
1679
|
console.log(`\u2713 ${entry.name}@${local} already on npm \u2014 recording it as a candidate`);
|
|
1656
1680
|
}
|
|
1657
1681
|
manifest.candidates = { ...manifest.candidates, [entry.name]: local };
|
|
@@ -1715,7 +1739,7 @@ if (command === "train") {
|
|
|
1715
1739
|
console.error(`\u2717 ${error.message} \u2014 its checkout is back on its default branch`);
|
|
1716
1740
|
}
|
|
1717
1741
|
}
|
|
1718
|
-
const candidateNames = Object.keys(manifest
|
|
1742
|
+
const candidateNames = Object.keys(activeCandidates(manifest));
|
|
1719
1743
|
if (candidateNames.length > 0) {
|
|
1720
1744
|
const everything = trainPlan(dir, manifest, LANES);
|
|
1721
1745
|
for (const checkout of [
|
|
@@ -1764,7 +1788,7 @@ if (command === "train") {
|
|
|
1764
1788
|
};
|
|
1765
1789
|
console.log(`
|
|
1766
1790
|
blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
|
|
1767
|
-
const candidates = Object.entries(manifest
|
|
1791
|
+
const candidates = Object.entries(activeCandidates(manifest));
|
|
1768
1792
|
if (candidates.length > 0) {
|
|
1769
1793
|
console.log("candidates (check accepts them; not yet blessed):");
|
|
1770
1794
|
for (const [name, version] of candidates) {
|
|
@@ -1772,6 +1796,7 @@ blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
|
|
|
1772
1796
|
console.log(` ${name}@${version}${on.length === 0 ? "" : ` \u2014 waiting on ${on.join(", ")}`}`);
|
|
1773
1797
|
}
|
|
1774
1798
|
}
|
|
1799
|
+
for (const line of holdLines(manifest)) console.log(line);
|
|
1775
1800
|
if (waiting.length > 0) {
|
|
1776
1801
|
console.log("pending (merge each adoption PR, then run the train again to bless):");
|
|
1777
1802
|
for (const entry of waiting) {
|
package/package.json
CHANGED
package/versions.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@types/bun"
|
|
14
14
|
],
|
|
15
15
|
"ecosystem": {
|
|
16
|
-
"@inixiative/config": "0.5.
|
|
16
|
+
"@inixiative/config": "0.5.176",
|
|
17
17
|
"@inixiative/json-rules": "3.16.0",
|
|
18
18
|
"@inixiative/permissions": "0.18.0",
|
|
19
19
|
"@inixiative/transitions": "0.6.0",
|
|
@@ -53,6 +53,14 @@
|
|
|
53
53
|
"@inixiative/agent-session": "0.8.0",
|
|
54
54
|
"@inixiative/archive": "0.31.0",
|
|
55
55
|
"@inixiative/atlas": "0.5.0",
|
|
56
|
-
"@inixiative/signet": "0.
|
|
56
|
+
"@inixiative/signet": "0.11.1"
|
|
57
|
+
},
|
|
58
|
+
"holds": {
|
|
59
|
+
"@inixiative/signet": {
|
|
60
|
+
"below": "0.12.0",
|
|
61
|
+
"reason": "0.12 breaks the grants contract that prod Kingdom (0.11) speaks",
|
|
62
|
+
"until": "the Kingdom grants cutover",
|
|
63
|
+
"owner": "inixiative-bf"
|
|
64
|
+
}
|
|
57
65
|
}
|
|
58
66
|
}
|