@zerwiz/ymir 0.1.26 → 0.1.28
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/.agents/skills/groa-update/SKILL.md +30 -0
- package/CHANGELOG.md +44 -0
- package/README.md +47 -8
- package/bin/fixes-guard.sh +112 -0
- package/bin/fixes.sh +157 -0
- package/bin/ymir.js +35 -0
- package/docs/fixes/agents/unversioned-2026-09-11-chat-uses-the-root-pi-model-catalog.md +15 -0
- package/docs/fixes/agents/unversioned-2026-09-11-shared-well-for-every-harness-no-mock-clickable-memories.md +26 -0
- package/docs/fixes/agents/unversioned-2026-09-13-gleipnir-sees-when-an-agent-is-turned-off.md +26 -0
- package/docs/fixes/agents/unversioned-2026-09-14-the-eindri-brokk-wake-bridge-they-can-talk-back-now.md +23 -0
- package/docs/fixes/agents/unversioned-2026-09-16-a-sandboxed-worker-cannot-think-auto-stops-killing-agent.md +18 -0
- package/docs/fixes/agents/unversioned-2026-09-17-agents-md-states-the-one-law-no-private-data-in-the-publ.md +25 -0
- package/docs/fixes/agents/unversioned-2026-09-17-every-figure-loads-the-agent-directory-was-never-read.md +25 -0
- package/docs/fixes/agents/unversioned-2026-09-17-pi-gets-its-agents-and-a-dead-extension-comes-back.md +26 -0
- package/docs/fixes/agents/unversioned-2026-09-17-the-well-as-tools-ymir-s-first-custom-pi-extension.md +20 -0
- package/docs/fixes/gate/0.1.28-fixes-guard-replaces-the-changelog-gate.md +7 -0
- package/docs/fixes/gate/0.1.28-the-changelog-system-is-replaced-by-fix-notes.md +8 -0
- package/docs/fixes/gate/0.1.28-the-frozen-changelog-becomes-per-component-notes.md +9 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-11-automate-the-install-consent-gate-self-healing-prereqs-v.md +21 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-11-single-tenant-workspaces-shipped.md +15 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-13-the-cloth-reaches-all-three-halls-hlidskjalf-sm-ja-the-s.md +25 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-16-one-login-for-every-app-on-the-web-the-gate-fronts-them-.md +24 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-16-the-credential-reaches-the-gate-and-the-hall-stops-leaki.md +23 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-16-the-delivery-gates-install-themselves-rename-drift-mende.md +22 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-16-the-panels-that-crashed-the-gate-that-lied-and-the-well-.md +25 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-17-0-1-14-the-panels-reach-the-gate-and-the-fourth-window-s.md +9 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-17-models-providers-and-paths-are-the-user-s-and-the-gate-s.md +26 -0
- package/docs/fixes/hlidskjalf/unversioned-2026-09-17-the-northstar-gate-runs-nightly-the-platform-door-it-cau.md +16 -0
- package/docs/fixes/hoard/unversioned-2026-09-16-every-app-wears-its-own-rune.md +27 -0
- package/docs/fixes/hoard/unversioned-2026-09-16-hodd-lives-outside-the-repo-and-the-installer-makes-it-s.md +24 -0
- package/docs/fixes/hoard/unversioned-2026-09-16-the-icons-are-real-runes-now-eight-of-them-were-drawings.md +33 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-0-1-26-published-and-the-vault-door-named.md +8 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-one-well-in-the-hoard-every-reader-of-kaia-s-memory-reso.md +23 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-skrymir-opens-the-hoard-not-the-checkout.md +18 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-the-hoard-encrypts-its-secrets-at-rest.md +20 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-the-hoard-gets-a-guard-and-rule-04-stops-contradicting-t.md +19 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-the-home-s-re-laid-layout-taught-to-the-system.md +17 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-the-private-home-gets-the-ward-it-never-had.md +25 -0
- package/docs/fixes/hoard/unversioned-2026-09-17-the-schedule-is-the-user-s-cron-leaves-the-tree-lives-in.md +27 -0
- package/docs/fixes/install/0.1.28-a-stale-install-can-no-longer-hide.md +8 -0
- package/docs/fixes/install/unversioned-2026-09-11-desktop-shell-tunnel-and-temporary-auth.md +14 -0
- package/docs/fixes/install/unversioned-2026-09-11-smooth-install-provider-choice-no-forced-key-real-prereq.md +22 -0
- package/docs/fixes/install/unversioned-2026-09-16-apodex-joins-the-weave-research-planning-provider.md +27 -0
- package/docs/fixes/install/unversioned-2026-09-16-icons-the-operator-can-pin-and-one-pair-of-hands-for-the.md +29 -0
- package/docs/fixes/install/unversioned-2026-09-16-the-desktop-never-dials-out-cloudflare-stays-outside-ele.md +25 -0
- package/docs/fixes/install/unversioned-2026-09-17-0-1-12-the-icons-reach-the-live-tree-and-a-missing-surfa.md +10 -0
- package/docs/fixes/install/unversioned-2026-09-17-0-1-8-the-day-s-work-reaches-npm.md +25 -0
- package/docs/fixes/install/unversioned-2026-09-17-a-stale-install-cannot-hide-and-the-one-liner-cannot-be-.md +9 -0
- package/docs/fixes/install/unversioned-2026-09-17-both-shapes-a-clone-and-an-npm-install-and-the-patience-.md +26 -0
- package/docs/fixes/install/unversioned-2026-09-17-gr-a-knows-the-npm-half-how-to-renew-the-published-packa.md +9 -0
- package/docs/fixes/install/unversioned-2026-09-17-npm-only-the-hall-answers-on-its-own-port-and-the-marks-.md +27 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-apps-leave-the-monorepo-installation-pulls-them-from.md +23 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-doors-the-cloth-and-the-icons-that-were-not-there.md +27 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-editor-stops-being-banished-to-its-own-desktop.md +18 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-four-surfaces-arrive-by-npm-and-an-old-tree-gives-up.md +24 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-house-gains-an-unpublish-door.md +19 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-icons-reach-the-live-tree-and-a-missing-surface-is-a.md +26 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-install-asks-with-a-plan-and-the-operator-s-things-l.md +24 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-installer-actually-installs-the-whole-platform-then-.md +9 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-installer-has-a-design-of-its-own.md +9 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-npm-door-stands-the-surfaces.md +13 -0
- package/docs/fixes/install/unversioned-2026-09-17-the-one-liner-the-readme-promised-and-it-sets-the-path.md +9 -0
- package/docs/fixes/install/unversioned-2026-09-17-ymir-command-not-found-is-a-path-fault-and-here-is-the-c.md +8 -0
- package/docs/fixes/install/unversioned-2026-09-18-the-readme-leads-with-the-doors-that-work.md +13 -0
- package/docs/fixes/odrerir/unversioned-2026-09-14-rerir-forged-eindri-odrerir-pane-w3-p7.md +12 -0
- package/docs/fixes/odrerir/unversioned-2026-09-17-the-live-hall-s-board-becomes-true-the-snapshot-lands-on.md +18 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-gleipnir-lock-pid-binding-fix.md +17 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-hardcoded-per-page-feeds-header-stream.md +19 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-per-page-rolling-40-message-feeds.md +13 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-smi-ja-views-are-repo-local.md +17 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-stats-1-1-kaia-chat-forge-prompts.md +23 -0
- package/docs/fixes/runtime/unversioned-2026-09-11-the-well-is-real-mimirsbrunn-engram.md +21 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-fleet-preferences.md +11 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-house-law-rules-and-the-grein-naming.md +11 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-r-preference-moved-out-of-the-tracked-tree.md +10 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-the-dashboards-stop-crash-looping-on-the-igpu.md +20 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-the-well-actually-installs-it-never-did.md +23 -0
- package/docs/fixes/runtime/unversioned-2026-09-12-updater-forged-credential-moved-to-env.md +12 -0
- package/docs/fixes/runtime/unversioned-2026-09-14-a-vacant-helm-is-entered-not-punted-watcher-wake-repair.md +24 -0
- package/docs/fixes/runtime/unversioned-2026-09-14-the-echo-guard-no-wake-flood-through-gn-s-door.md +18 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-app-packaging-prepared-under-the-ymir-scope-amendment-c-.md +23 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-four-readers-that-still-pointed-at-the-pre-split-world.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-no-blue-the-forge-language-replaces-the-old-blue.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-omarchy-first-host-aware-windows-and-macos-get-a-door.md +25 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-smi-ja-moves-into-the-app-tree.md +17 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-the-changelog-guard-no-push-ships-untold.md +19 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-the-core-senses-the-real-host-not-omarchy-s-shadow.md +23 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-the-hearth-spreads-one-fire-three-more-surfaces.md +25 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-the-loader-stops-deleting-merge-never-overwrite.md +27 -0
- package/docs/fixes/runtime/unversioned-2026-09-16-the-mesh-reaches-teams-and-anchor-and-show-stops-guessin.md +24 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-0-1-10-the-hall-answers-on-its-port-and-the-marks-land.md +15 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-0-1-11-four-surfaces-their-marks-and-a-door-that-mends.md +18 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-glitnir-stops-hanging-and-one-process-is-one-row.md +15 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-main-is-repaired-and-the-report-wears-the-cloth.md +25 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-not-again-and-four-surfaces-in-one-raise.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-smoke-test-all-ui-two-checks-that-lied-about-their-own-s.md +20 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-changelog-stops-conflicting-fragments-folded-by-a-sc.md +25 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-doors-written-down-where-a-user-will-find-them.md +20 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-duplicate-extensions-removed-from-the-project-tree.md +17 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-extensions-deployed-without-their-modules.md +23 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-feed-follows-brokk-s-thinking-past-the-composer.md +17 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-fleet-stops-colliding-a-grid-fan-and-each-smith-s-ow.md +21 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-fork-stands-the-machine.md +12 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-fragment-convention-is-enforced-where-github-runs-it.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-layout-swept-every-reader-taught-the-new-doors.md +24 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-model-that-raised-itself-and-the-workhorse-that-repl.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-package-shipped-the-memory-well-one-version-now-excl.md +26 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-phantom-optional-pins-cut.md +9 -0
- package/docs/fixes/runtime/unversioned-2026-09-17-the-sweep-and-the-door-that-mends-itself.md +26 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-12-the-editor-keeps-its-own-seat.md +16 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-13-sessr-mnir-the-seat-hall.md +28 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-13-the-carved-cloth-reaches-the-seat-hall-sessr-mnir.md +27 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-13-the-new-tree-the-mesh-the-seat-hall.md +25 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-13-the-realm-seat-carved-wayof.md +24 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-16-apodex-seat-verified-the-weave-mended.md +27 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-16-the-visualizer-finds-its-db-and-the-seat-hall-actually-b.md +22 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-0-1-13-the-re-voiced-seat-arrives-and-the-packages-are-l.md +8 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-sessr-mnir-stops-wearing-pi-s-clothes.md +23 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-the-hearth-re-seeds-on-its-container-sessr-mnir-s-chat-f.md +23 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-the-packaged-seat-carries-the-watch.md +11 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-the-readme-lists-the-packages-and-the-pin-moves-to-the-r.md +16 -0
- package/docs/fixes/sessrumnir/unversioned-2026-09-17-the-seat-s-name-and-the-visualizer-s-home.md +12 -0
- package/docs/fixes/skills/unversioned-2026-09-11-galdr-tyr-assets-symlink-not-copies.md +11 -0
- package/docs/fixes/skills/unversioned-2026-09-11-load-the-owning-asset-before-editing-a-governed-path-5-l.md +21 -0
- package/docs/fixes/skills/unversioned-2026-09-11-omarchy-first-host-skills-machine-learning-native-fixes.md +27 -0
- package/docs/fixes/skills/unversioned-2026-09-13-the-imported-names-leave-the-halls-norse-naming-purge.md +26 -0
- package/docs/fixes/skills/unversioned-2026-09-16-gunnl-joins-the-hall-skillopt-integration.md +17 -0
- package/docs/fixes/skills/unversioned-2026-09-16-one-skills-tree-every-harness-the-config-stomp-mended.md +25 -0
- package/docs/fixes/skills/unversioned-2026-09-16-the-skill-loading-audit-wrong-skills-unloaded-the-index-.md +26 -0
- package/docs/fixes/skills/unversioned-2026-09-17-0-1-9-both-shapes-reach-the-registry.md +17 -0
- package/docs/fixes/skills/unversioned-2026-09-17-midgard-holds-public-assets-only-the-company-wiki-is-gon.md +23 -0
- package/docs/fixes/skills/unversioned-2026-09-17-the-forge-speaks-each-skill-s-own-text-in-the-index.md +16 -0
- package/docs/fixes/skills/unversioned-2026-09-17-the-local-git-round-joins-the-loom-the-stack-s-doors-are.md +19 -0
- package/docs/fixes/skills/unversioned-2026-09-17-the-registry-says-where-a-repo-lives-and-what-it-is-for.md +20 -0
- package/docs/fixes/smidja/unversioned-2026-09-16-the-pane-bridge-keeps-its-quoting-phase-3-verified.md +22 -0
- package/docs/fixes/smidja/unversioned-2026-09-16-the-roster-can-finally-run-an-agent-on-pi.md +23 -0
- package/docs/fixes/smidja/unversioned-2026-09-16-the-roster-s-effect-lands-and-the-backend-pin-stops-bein.md +19 -0
- package/docs/fixes/smidja/unversioned-2026-09-16-the-validator-stops-calling-a-dead-visualizer-healthy.md +20 -0
- package/docs/fixes/smidja/unversioned-2026-09-17-the-fleet-was-a-rack-of-terminals-now-it-is-the-roster.md +18 -0
- package/docs/fixes/smidja/unversioned-2026-09-17-the-plan-learns-the-smithy-s-package-name-and-the-instal.md +25 -0
- package/docs/fixes/smidja/unversioned-2026-09-17-the-smithy-can-see-the-well.md +20 -0
- package/docs/fixes/smidja/unversioned-2026-09-17-the-smithy-s-dependency-names-the-package-npm-will-actua.md +20 -0
- package/package.json +1 -1
|
@@ -91,6 +91,36 @@ This touches only the Brokk repo and its own worktrees, never anything under `pr
|
|
|
91
91
|
that is drift the update should have carried out — name it to the Allfather.
|
|
92
92
|
Owning asset: `.agents/skills/galdr-ymirsystem/assets/installation.md`.
|
|
93
93
|
|
|
94
|
+
## Updating Ymir on npm — the other half of renewing
|
|
95
|
+
|
|
96
|
+
Gróa fast-forwards the **tree**; the published **packages** need the same care, and
|
|
97
|
+
the traps here are quiet ones. In order:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
npm_renewal[6]{step,how,why}:
|
|
101
|
+
"1 choose the number","fetch the registry's latest FIRST (curl …/@zerwiz%2fymir, not memory) and bump one above it","the registry can be AHEAD of main when a publish went out from a branch — main said 0.1.18 while npm served 0.1.25"
|
|
102
|
+
"2 land the bump by PR","branch → commit → gh pr create → merge; never push main (branch-guard refuses)","the delivery gate: a change leaves by PR, and the Allfather's approval is the merge"
|
|
103
|
+
"3 publish","bin/npm-publish.sh — it opens the vault's door for the token","the token is in hodd/secrets/platform.env.age, never in ~/.npmrc (that one has been stale before)"
|
|
104
|
+
"4 if it says the token is absent","the DOOR is broken, not the key: with `age` present, age -d -i hodd/secrets/age.key hodd/secrets/platform.env.age | sed -n 's/^NPM_TOKEN=//p'","bin/hodd.sh emit currently returns empty for both spellings; the vault is fine, the emit path is not — mend it"
|
|
105
|
+
"5 verify","fetch the VERSION document (…/@zerwiz/ymir/<version>) — it answers before the packument does","the registry lags for minutes; the packument can 404 while the version and tarball already resolve"
|
|
106
|
+
"6 the four apps ride along","their pins move in the same release (hlidskjalf · odrerir · sessrumnir · smidja-factory)","the distro depends on them; a pin left behind keeps shipping the old app to every user"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**The traps, named — each one cost a night:**
|
|
110
|
+
|
|
111
|
+
- **A stale `latest` makes an install a silent no-op.** npm resolved a cached
|
|
112
|
+
`latest`, saw the same version, printed *"changed 200 packages"* and left the old
|
|
113
|
+
build in place. Publish and reinstall with **`@latest --prefer-online`**, and give
|
|
114
|
+
a stuck machine **`npm cache clean --force`**. The CLI now *says* when it is out
|
|
115
|
+
of date (once a day), so a stale install cannot hide.
|
|
116
|
+
- **npm gates install scripts.** Electron's postinstall never runs, so a fresh
|
|
117
|
+
install has web surfaces and no windows; each shell needs `npm rebuild electron`
|
|
118
|
+
(the installer's `step_desktop` does it, and `bin/electron-lib.sh` verifies).
|
|
119
|
+
- **A package that ships less than it needs is a fault in its own repo.** Declare
|
|
120
|
+
`name`, `files` and the absence of `private` where the package lives — never
|
|
121
|
+
rewrite a manifest at publish time (that is how `vite.config.ts` went missing and
|
|
122
|
+
every panel died on `Unexpected token '<'`).
|
|
123
|
+
|
|
94
124
|
## Safety
|
|
95
125
|
|
|
96
126
|
- **Fast-forward only.** Dirty, diverged, offline, or non-default-branch targets are skipped and reported, never forced or stashed. Nothing with unlanded work is ever discarded.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
> **Frozen 2026-09-18.** This file is the historical record and is no longer
|
|
2
|
+
> written to (Rule 06: a record is never rewritten). New work is recorded as fix
|
|
3
|
+
> notes — one file per fix, per component — under `docs/fixes/`, written with
|
|
4
|
+
> `bin/fixes.sh record` and guarded by `bin/fixes-guard.sh`.
|
|
5
|
+
|
|
6
|
+
## 2026-09-18 — the README leads with the doors that work
|
|
7
|
+
|
|
8
|
+
- **The npm page and GitHub both render `README.md`**, so a user meets it first — and
|
|
9
|
+
it buried the door that always works (`npx @zerwiz/ymir`) and the PATH cure inside
|
|
10
|
+
a later section. It now opens with the one-liner (install into a prefix you own, PATH
|
|
11
|
+
written, setup run, door proven), names `npx` second, and answers *"ymir: command not
|
|
12
|
+
found"* and the stale-install trap where they happen.
|
|
13
|
+
- The three doors and their honest reasons: `npx` needs no PATH at all; the one-liner
|
|
14
|
+
sets it once; `npm install -g` works wherever npm's global bin is on PATH.
|
|
15
|
+
|
|
16
|
+
## 2026-09-17 — Gróa knows the npm half: how to renew the published packages
|
|
17
|
+
|
|
18
|
+
- **Her skill gains the whole procedure** — choose the number from the registry (never from memory: it can be ahead of main), land the bump by PR, publish through the vault, verify by the *version document* (the packument lags), and move the four apps pins in the same release.
|
|
19
|
+
- **And the traps, each of which cost a night:** a stale cached `latest` makes `npm i -g` a silent no-op that still prints success; npm gates Electron s postinstall so windows need `npm rebuild electron`; and a package must declare its own `name`/`files`/privacy where it lives rather than having a manifest rewritten at publish time.
|
|
20
|
+
- The token lives in the encrypted vault; when a tool reports it absent, the DOOR is broken, not the key — and `age -d -i hodd/secrets/age.key hodd/secrets/platform.env.age` opens it while `bin/hodd.sh emit` does not.
|
|
21
|
+
|
|
22
|
+
## 2026-09-17 — a stale install cannot hide, and the one-liner cannot be stranded
|
|
23
|
+
|
|
24
|
+
- **The fault the Allfather hit:** `npm install -g @zerwiz/ymir` reported *"changed 200 packages"* and changed nothing — npm resolved a **cached `latest`**, saw the same version, and left the old build in place. The installed copy stayed 0.1.14 while the registry served 0.1.26, and nothing told the user. With 750+ downloads, that is not a detail.
|
|
25
|
+
- **The CLI now says when it is out of date** — once a day, on a terminal, silenced by the same `ymir config notice version off` as every other notice: *"a newer Ymir is on npm 0.1.14 → 0.1.26 · update: npm i -g @zerwiz/ymir"*.
|
|
26
|
+
- **The one-liner is cache-proof**: it installs `@latest` with `--prefer-online`, so a stale packument cannot strand a fresh machine.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## 2026-09-17 — the installer actually installs (the whole platform, then proves it)
|
|
30
|
+
|
|
31
|
+
- **Why pi.dev and opencode just work and Ymir did not.** They are one binary: JS only, no Electron, no build step, no services, no windows — and their `curl|sh` installer puts the command on PATH *and* runs onboarding. The npm path is the side door. Ymir is a platform: its install must place a CLI, four apps, their dependencies, four Electron runtimes (which npm gates), two SPA builds, desktop entries and icons, the services, and a home — eight things to go wrong, and npm fights half of them.
|
|
32
|
+
- **So the one-liner now finishes the job**, the way theirs does: it installs the command, then runs **the first setup**, then **proves what stands** and names the next door. `YMIR_SKIP_SETUP=1` installs the command alone, for anyone who wants the old behaviour.
|
|
33
|
+
- An installer that stops at a binary leaves the user with a tool that does nothing yet.
|
|
34
|
+
|
|
35
|
+
## 2026-09-17 — the one-liner the README promised, and it sets the PATH
|
|
36
|
+
|
|
37
|
+
- **`install.sh` did not exist.** The README's first door (\`curl -fsSL …/install.sh | bash\`) led nowhere — which is how a user on another machine ends up typing \`ymir\` in every combination and getting nothing.
|
|
38
|
+
- **It exists now, and it removes the whole class of failure:** a prefix the user owns (no \`sudo\`, no \`EACCES\`), the \`export PATH\` written into the shell's rc so it survives the session, and then it **proves the door** with \`ymir --version\` rather than promising it.
|
|
39
|
+
- The command was never missing from the package — the published tarball ships \`bin/ymir.js\` and declares \`bin: {ymir, ymir-install}\`. **The shell simply was not looking where npm put it**, and that is the most common "it does not work" in the Node ecosystem.
|
|
40
|
+
|
|
41
|
+
## 2026-09-17 — 0.1.26: published, and the vault door named
|
|
42
|
+
|
|
43
|
+
- **@zerwiz/ymir@0.1.26 is on the registry** — the latest main, published as `zerwiz`. The token was in the encrypted vault all along; `age` was missing from this machine, and `bin/hodd.sh emit` returns empty, so every reader reported the token absent. The door is the fix, not the key.
|
|
44
|
+
- **The bump clears the registry**: main carried 0.1.18 while the registry served 0.1.25 (other sessions published past main).
|
|
1
45
|
|
|
2
46
|
## 2026-09-17 — "ymir: command not found" is a PATH fault, and here is the cure
|
|
3
47
|
|
package/README.md
CHANGED
|
@@ -637,15 +637,54 @@ resolved from `$YMIR_HOME/secrets/platform.env` at runtime.
|
|
|
637
637
|
|
|
638
638
|
---
|
|
639
639
|
|
|
640
|
-
## Install
|
|
640
|
+
## Install
|
|
641
|
+
|
|
642
|
+
**One command. It installs into a prefix you own, writes the PATH into your shell,
|
|
643
|
+
runs the first setup, and then proves it.**
|
|
641
644
|
|
|
642
645
|
```bash
|
|
643
646
|
curl -fsSL https://raw.githubusercontent.com/zerwiz/ymir/main/install.sh | bash
|
|
644
|
-
npx @zerwiz/ymir install # no global install
|
|
645
|
-
git clone https://github.com/zerwiz/ymir.git ~/Ymir && cd ~/Ymir && bin/ymir-install.sh
|
|
646
|
-
npm install -g @zerwiz/ymir # the CLI, global
|
|
647
647
|
```
|
|
648
648
|
|
|
649
|
+
**No PATH, no prefix, nothing to configure — run it without installing:**
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
npx @zerwiz/ymir
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
**Already manage Node and npm yourself:**
|
|
656
|
+
|
|
657
|
+
```bash
|
|
658
|
+
npm install -g @zerwiz/ymir # then: ymir
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### If `ymir` is "command not found"
|
|
662
|
+
|
|
663
|
+
The command **is** installed — npm put it in its global bin directory, and your
|
|
664
|
+
shell does not look there. This is npm's behaviour, not Ymir's, and it is the most
|
|
665
|
+
common *"it does not work"* in the whole Node ecosystem.
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
npm prefix -g # where npm puts global commands
|
|
669
|
+
ls "$(npm prefix -g)/bin/ymir" # the command is there
|
|
670
|
+
export PATH="$(npm prefix -g)/bin:$PATH" # this shell
|
|
671
|
+
echo 'export PATH="$(npm prefix -g)/bin:$PATH"' >> ~/.bashrc # every shell after
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
**Or skip the question entirely** — `npx @zerwiz/ymir` works with no PATH at all,
|
|
675
|
+
and the one-liner above sets it once.
|
|
676
|
+
|
|
677
|
+
### If the version never changes
|
|
678
|
+
|
|
679
|
+
An install can *succeed* and change nothing: npm may resolve a cached `latest` and
|
|
680
|
+
report success while the old build stays. Clear it once:
|
|
681
|
+
|
|
682
|
+
```bash
|
|
683
|
+
npm cache clean --force && npm i -g @zerwiz/ymir@latest --prefer-online
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
The CLI itself now says when it is out of date, once a day.
|
|
687
|
+
|
|
649
688
|
**Who can install this today.** The Ymir distro is private while it is young —
|
|
650
689
|
so `curl`, `npx` and `git clone` work for the author and invited users, not for
|
|
651
690
|
the public (an unknown clone returns 404). If you would like access, ask:
|
|
@@ -736,11 +775,11 @@ alone if you want only that hall.
|
|
|
736
775
|
|
|
737
776
|
```
|
|
738
777
|
packages[5]{package,what,live}:
|
|
739
|
-
"@zerwiz/ymir","the distro — the runtime, the doors, the installer, the plan","0.1.
|
|
740
|
-
"@zerwiz/hlidskjalf","the high seat — the control plane, the gate and the landing","0.1.
|
|
741
|
-
"@zerwiz/odrerir","the live hall — the board on :4322","0.1.
|
|
778
|
+
"@zerwiz/ymir","the distro — the runtime, the doors, the installer, the plan","0.1.28"
|
|
779
|
+
"@zerwiz/hlidskjalf","the high seat — the control plane, the gate and the landing","0.1.1"
|
|
780
|
+
"@zerwiz/odrerir","the live hall — the board on :4322","0.1.1"
|
|
742
781
|
"@zerwiz/smidja-factory","the smithy — the agent factory, its skills and the visualizer on :8437","0.1.1"
|
|
743
|
-
"@zerwiz/sessrumnir","the seat-hall — the desktop where you speak with Brokk","0.1.
|
|
782
|
+
"@zerwiz/sessrumnir","the seat-hall — the desktop where you speak with Brokk","0.1.10"
|
|
744
783
|
```
|
|
745
784
|
|
|
746
785
|
```bash
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fixes-guard.sh — the delivery gate reads, and it checks the RECORD.
|
|
3
|
+
#
|
|
4
|
+
# What it replaces, and why: `changelog-guard` passed if a push range merely
|
|
5
|
+
# *touched* CHANGELOG.md — a stray line satisfied it, so it never verified that
|
|
6
|
+
# anything was recorded. And the hook in front of it FOLDED fragments into the
|
|
7
|
+
# monolith, mutating the tree mid-push and then demanding a commit for the edit it
|
|
8
|
+
# had just made.
|
|
9
|
+
#
|
|
10
|
+
# This gate only READS. A push must carry at least one new fix note
|
|
11
|
+
# (`docs/fixes/<component>/<version>-<slug>.md`), and it says which of the push's
|
|
12
|
+
# touched paths that note's component covers — a mismatch is reported, never
|
|
13
|
+
# silently accepted.
|
|
14
|
+
#
|
|
15
|
+
# fixes-guard.sh --install # seat it as the pre-push hook (replacing the old one)
|
|
16
|
+
# fixes-guard.sh --check # judge the current branch against its base, by hand
|
|
17
|
+
# git push # the hook runs it with the refs on stdin
|
|
18
|
+
#
|
|
19
|
+
# Override, loudly: YMIR_SKIP_FIXES_GUARD=1
|
|
20
|
+
#
|
|
21
|
+
# Exit: 0 the record is there, 1 it is not, 2 usage.
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
VERSION="1.0.0"
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
27
|
+
cd "$ROOT" || exit 1
|
|
28
|
+
|
|
29
|
+
[ "${YMIR_SKIP_FIXES_GUARD:-0}" = 1 ] && { printf 'fixes-guard[1]{gate,result}:\n "push","skipped (YMIR_SKIP_FIXES_GUARD=1)"\n'; exit 0; }
|
|
30
|
+
|
|
31
|
+
case "${1-}" in
|
|
32
|
+
-v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;;
|
|
33
|
+
-h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
34
|
+
--install)
|
|
35
|
+
H="$(git rev-parse --git-path hooks)/pre-push"
|
|
36
|
+
mkdir -p "$(dirname "$H")"
|
|
37
|
+
cat >"$H" <<EOF
|
|
38
|
+
#!/usr/bin/env bash
|
|
39
|
+
# pre-push — the delivery gate. Installed by bin/fixes-guard.sh --install.
|
|
40
|
+
# 1. branch-guard — never push a protected branch
|
|
41
|
+
# 2. fixes-guard — every push carries a fix note for what it changed (read-only)
|
|
42
|
+
set -u
|
|
43
|
+
refs="\$(mktemp)"; trap 'rm -f "\$refs"' EXIT
|
|
44
|
+
cat >"\$refs"
|
|
45
|
+
"$ROOT/bin/branch-guard.sh" <"\$refs" || exit 1
|
|
46
|
+
"$ROOT/bin/fixes-guard.sh" <"\$refs" || exit 1
|
|
47
|
+
EOF
|
|
48
|
+
chmod +x "$H"
|
|
49
|
+
printf 'fixes-guard[1]{gate,result}:\n "hook","installed at %s"\n' "${H#"$ROOT"/}"
|
|
50
|
+
exit 0 ;;
|
|
51
|
+
--check) ;;
|
|
52
|
+
"") ;;
|
|
53
|
+
*) printf 'error: unknown flag %s\nhelp: bin/fixes-guard.sh [--install|--check]\n' "$1" >&2; exit 2 ;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
# ── the judgement ────────────────────────────────────────────────────────────
|
|
57
|
+
# The range: from stdin when a hook calls us, else the branch against its base.
|
|
58
|
+
refs="$(cat 2>/dev/null || true)"
|
|
59
|
+
head_sha=""; remote_sha=""
|
|
60
|
+
if [ -n "$refs" ]; then
|
|
61
|
+
while read -r _local local_sha _remote remote_sha2; do
|
|
62
|
+
[ -n "${local_sha:-}" ] || continue
|
|
63
|
+
case "$local_sha" in 000000*) continue ;; esac
|
|
64
|
+
head_sha="$local_sha"; remote_sha="${remote_sha2:-}"
|
|
65
|
+
done <<<"$refs"
|
|
66
|
+
fi
|
|
67
|
+
[ -n "$head_sha" ] || head_sha="$(git rev-parse HEAD)"
|
|
68
|
+
|
|
69
|
+
base=""
|
|
70
|
+
if [ -n "$remote_sha" ] && [ "$remote_sha" != 0000000000000000000000000000000000000000 ]; then
|
|
71
|
+
base="$remote_sha"
|
|
72
|
+
else
|
|
73
|
+
for cand in origin/main main master; do
|
|
74
|
+
git rev-parse --verify -q "$cand" >/dev/null 2>&1 || continue
|
|
75
|
+
base="$(git merge-base "$head_sha" "$cand" 2>/dev/null || true)"
|
|
76
|
+
[ -n "$base" ] && break
|
|
77
|
+
done
|
|
78
|
+
fi
|
|
79
|
+
[ -n "$base" ] || { printf 'fixes-guard[1]{gate,result}:\n "push","no base to compare — first push of a branch is judged on its own"\n'; exit 0; }
|
|
80
|
+
|
|
81
|
+
range="${base}..${head_sha}"
|
|
82
|
+
notes="$(git diff --name-only --diff-filter=A "$range" 2>/dev/null | grep -E '^docs/fixes/[a-z]+/[0-9][^/]*\.md$' || true)"
|
|
83
|
+
touched="$(git diff --name-only "$range" 2>/dev/null | grep -v '^docs/fixes/' | grep -v '^CHANGELOG' || true)"
|
|
84
|
+
|
|
85
|
+
if [ -z "$notes" ]; then
|
|
86
|
+
printf 'fixes-guard[1]{gate,result}:\n "push","refused — no fix note in this range"\n' >&2
|
|
87
|
+
printf 'help: bin/fixes.sh record --component=<%s> --version=<v> --title="what changed"\n' \
|
|
88
|
+
"install|runtime|skills|agents|hlidskjalf|odrerir|sessrumnir|smidja|hoard|gate" >&2
|
|
89
|
+
printf 'note: a note is a file under docs/fixes/<component>/ — write one, commit it, push again.\n' >&2
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# The record is there: now say whether it speaks to what the push changed.
|
|
94
|
+
covered=0
|
|
95
|
+
for n in $notes; do
|
|
96
|
+
c="$(printf '%s' "${n#docs/fixes/}" | cut -d/ -f1)"
|
|
97
|
+
case "$touched" in
|
|
98
|
+
*"$c"*) covered=$((covered+1)) ;;
|
|
99
|
+
*bin/fixes*|*install*) [ "$c" = install ] && covered=$((covered+1)) ;;
|
|
100
|
+
*bin/ymir*|*bin/sessrumnir*|*bin/smidja*|*bin/npm-publish*|*.agents/skills/*) covered=$((covered+1)) ;;
|
|
101
|
+
esac
|
|
102
|
+
done
|
|
103
|
+
|
|
104
|
+
count="$(printf '%s\n' "$notes" | grep -c . || true)"
|
|
105
|
+
if [ "$covered" -gt 0 ]; then
|
|
106
|
+
printf 'fixes-guard[1]{gate,result}:\n "push","ok — %s note(s), and the record speaks to the change"\n' "$count"
|
|
107
|
+
else
|
|
108
|
+
printf 'fixes-guard[1]{gate,result}:\n "push","ok — %s note(s); none names a path this range touched"\n' "$count"
|
|
109
|
+
printf 'note: a note for another component is still a record — but say what THIS push changed.\n' >&2
|
|
110
|
+
printf ' components touched: %s\n' "$(printf '%s' "$touched" | head -4 | tr '\n' ' ')" >&2
|
|
111
|
+
fi
|
|
112
|
+
exit 0
|
package/bin/fixes.sh
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fixes.sh — the fix notes. ONE FILE PER FIX, and nothing to fold.
|
|
3
|
+
#
|
|
4
|
+
# The two systems this replaces both failed us, and for the same reason: they kept
|
|
5
|
+
# a MONOLITH (CHANGELOG.md) and fed it from fragments (CHANGELOG.d). The pre-push
|
|
6
|
+
# hook folded the fragments into the monolith, then demanded a commit for the edit
|
|
7
|
+
# it had just made — a push that mutates the tree it is pushing, and a gate that
|
|
8
|
+
# passed if a range merely *touched* the file.
|
|
9
|
+
#
|
|
10
|
+
# So there is no monolith and no fold here. A note IS a file:
|
|
11
|
+
#
|
|
12
|
+
# docs/fixes/<component>/<version>-<slug>.md
|
|
13
|
+
#
|
|
14
|
+
# Two branches never write the same path, so the collision is impossible. The
|
|
15
|
+
# history of a component is its directory, sorted. Nothing is ever assembled at
|
|
16
|
+
# push time, and the gate that guards it only READS (bin/fixes-guard.sh).
|
|
17
|
+
#
|
|
18
|
+
# bin/fixes.sh record --component=install --version=0.1.28 --title="…" \
|
|
19
|
+
# [--why="…"] [--files="a,b"] [--date=YYYY-MM-DD]
|
|
20
|
+
# bin/fixes.sh list [<component>] # the components, or one's notes
|
|
21
|
+
# bin/fixes.sh show <file|component> # a note, or the newest of a component
|
|
22
|
+
# bin/fixes.sh diff --component=X --from=V --to=V
|
|
23
|
+
# bin/fixes.sh validate # every note's header, and its order
|
|
24
|
+
# bin/fixes.sh components # just the names
|
|
25
|
+
#
|
|
26
|
+
# Exit: 0 ok, 1 error, 2 usage.
|
|
27
|
+
set -u
|
|
28
|
+
|
|
29
|
+
VERSION="1.0.0"
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
32
|
+
FIXES="$ROOT/docs/fixes"
|
|
33
|
+
|
|
34
|
+
# The components are the machine's own parts, not an invented list.
|
|
35
|
+
COMPONENTS="install runtime skills agents hlidskjalf odrerir sessrumnir smidja hoard gate"
|
|
36
|
+
|
|
37
|
+
usage() { sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'; }
|
|
38
|
+
die() { printf 'error: %s\n' "$1" >&2; [ -n "${2:-}" ] && printf 'help: %s\n' "$2" >&2; exit 1; }
|
|
39
|
+
known() { case " $COMPONENTS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
|
|
40
|
+
slugify() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-56; }
|
|
41
|
+
|
|
42
|
+
action="${1-}"; shift || true
|
|
43
|
+
case "$action" in
|
|
44
|
+
-v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;;
|
|
45
|
+
-h|--help|"") usage; exit 0 ;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
component=""; version=""; title=""; why=""; files=""; date=""; from=""; to=""
|
|
49
|
+
for a in "$@"; do
|
|
50
|
+
case "$a" in
|
|
51
|
+
--component=*) component="${a#*=}" ;;
|
|
52
|
+
--version=*) version="${a#*=}" ;;
|
|
53
|
+
--title=*) title="${a#*=}" ;;
|
|
54
|
+
--why=*) why="${a#*=}" ;;
|
|
55
|
+
--files=*) files="${a#*=}" ;;
|
|
56
|
+
--date=*) date="${a#*=}" ;;
|
|
57
|
+
--from=*) from="${a#*=}" ;;
|
|
58
|
+
--to=*) to="${a#*=}" ;;
|
|
59
|
+
-*) die "unknown argument $a" "bin/fixes.sh --help" ;;
|
|
60
|
+
*) pos="${a}" ;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
notes_of() { # <component> — its notes, newest first (the version decides the order)
|
|
65
|
+
local c="$1"
|
|
66
|
+
[ -d "$FIXES/$c" ] || return 0
|
|
67
|
+
ls -1 "$FIXES/$c"/*.md 2>/dev/null | sort -V -r
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case "$action" in
|
|
71
|
+
components)
|
|
72
|
+
printf 'components[%d]{name}:\n' "$(printf '%s\n' $COMPONENTS | wc -l | tr -d ' ')"
|
|
73
|
+
for c in $COMPONENTS; do printf ' "%s"\n' "$c"; done
|
|
74
|
+
;;
|
|
75
|
+
|
|
76
|
+
list)
|
|
77
|
+
c="${pos:-${component:-}}"
|
|
78
|
+
if [ -n "$c" ]; then
|
|
79
|
+
known "$c" || die "unknown component $c" "one of: $COMPONENTS"
|
|
80
|
+
n=0; while IFS= read -r f; do [ -n "$f" ] || continue; n=$((n+1)); printf ' "%s","%s","%s"\n' "$c" "$(basename "$f")" "$(sed -n '1s/^## [^·]*· \([^ ]*\) ·.*/\1/p' "$f")"; done < <(notes_of "$c")
|
|
81
|
+
[ "$n" -gt 0 ] || printf 'fixes: %s has no notes yet\n' "$c"
|
|
82
|
+
else
|
|
83
|
+
printf 'fixes[%d]{component,notes}:\n' "$(printf '%s\n' $COMPONENTS | wc -l | tr -d ' ')"
|
|
84
|
+
for c in $COMPONENTS; do
|
|
85
|
+
cnt=$(notes_of "$c" | grep -c . || true)
|
|
86
|
+
printf ' "%s",%s\n' "$c" "$cnt"
|
|
87
|
+
done
|
|
88
|
+
fi
|
|
89
|
+
;;
|
|
90
|
+
|
|
91
|
+
show)
|
|
92
|
+
what="${pos:-${component:-}}"; [ -n "$what" ] || die "what shall I show?" "bin/fixes.sh show <component|file>"
|
|
93
|
+
if [ -f "$what" ]; then f="$what"
|
|
94
|
+
elif [ -f "$FIXES/$what" ]; then f="$FIXES/$what"
|
|
95
|
+
else
|
|
96
|
+
known "$what" || die "unknown component $what" "one of: $COMPONENTS"
|
|
97
|
+
f="$(notes_of "$what" | head -1)"
|
|
98
|
+
[ -n "$f" ] || { printf 'fixes: %s has no notes yet\n' "$what"; exit 0; }
|
|
99
|
+
fi
|
|
100
|
+
cat "$f"
|
|
101
|
+
;;
|
|
102
|
+
|
|
103
|
+
record)
|
|
104
|
+
[ -n "$component" ] && [ -n "$version" ] && [ -n "$title" ] \
|
|
105
|
+
|| die "record needs --component, --version and --title" \
|
|
106
|
+
"bin/fixes.sh record --component=install --version=0.1.28 --title=\"…\""
|
|
107
|
+
known "$component" || die "unknown component $component" "one of: $COMPONENTS"
|
|
108
|
+
date="${date:-$(date -u +%Y-%m-%d)}"
|
|
109
|
+
dir="$FIXES/$component"; mkdir -p "$dir"
|
|
110
|
+
f="$dir/${version}-$(slugify "$title").md"
|
|
111
|
+
[ -e "$f" ] && die "that note already exists: ${f#"$ROOT"/}" "bin/fixes.sh show ${f#"$ROOT"/}"
|
|
112
|
+
{
|
|
113
|
+
printf '## %s · %s · %s — %s\n\n' "$component" "$version" "$date" "$title"
|
|
114
|
+
[ -n "$why" ] && printf '### Why\n%s\n\n' "$why"
|
|
115
|
+
printf '### Files\n'
|
|
116
|
+
if [ -n "$files" ]; then printf '%s' "$files" | tr ',' '\n' | sed 's/^ *//; s/^/- `/; s/$/`/'
|
|
117
|
+
else printf -- '- *(none named)*\n'; fi
|
|
118
|
+
} >"$f"
|
|
119
|
+
printf 'fixes[1]{component,version,note}:\n "%s","%s","%s"\n' "$component" "$version" "${f#"$ROOT"/}"
|
|
120
|
+
;;
|
|
121
|
+
|
|
122
|
+
diff)
|
|
123
|
+
[ -n "$component" ] || die "diff needs --component" "bin/fixes.sh diff --component=install --from=0.1.27 --to=0.1.28"
|
|
124
|
+
known "$component" || die "unknown component $component"
|
|
125
|
+
n=0
|
|
126
|
+
while IFS= read -r f; do
|
|
127
|
+
[ -n "$f" ] || continue
|
|
128
|
+
b="$(basename "$f")"
|
|
129
|
+
case "$b" in
|
|
130
|
+
"${from}"-*|"${to}"-*) n=$((n+1)); printf '\n'; cat "$f" ;;
|
|
131
|
+
esac
|
|
132
|
+
done < <(notes_of "$component")
|
|
133
|
+
[ "$n" -gt 0 ] || printf 'fixes: nothing between %s and %s for %s\n' "$from" "$to" "$component"
|
|
134
|
+
;;
|
|
135
|
+
|
|
136
|
+
validate)
|
|
137
|
+
fails=0; checked=0
|
|
138
|
+
[ -d "$FIXES" ] || { printf 'fixes: docs/fixes/ does not exist yet\n'; exit 0; }
|
|
139
|
+
for d in "$FIXES"/*/; do
|
|
140
|
+
[ -d "$d" ] || continue
|
|
141
|
+
c="$(basename "$d")"
|
|
142
|
+
known "$c" || { printf ' "%s","unknown component directory"\n' "$c"; fails=$((fails+1)); continue; }
|
|
143
|
+
for f in "$d"*.md; do
|
|
144
|
+
[ -e "$f" ] || continue
|
|
145
|
+
checked=$((checked+1))
|
|
146
|
+
hdr="$(sed -n '1p' "$f")"
|
|
147
|
+
printf '%s' "$hdr" | grep -qE "^## ${c} · ([0-9]+\.[0-9]+\.[0-9]+[^ ]*|unversioned) · [0-9]{4}-[0-9]{2}-[0-9]{2} — .+" \
|
|
148
|
+
|| { printf ' "%s","malformed header: %s"\n' "${f#"$ROOT"/}" "$hdr"; fails=$((fails+1)); }
|
|
149
|
+
grep -q '^### Files$' "$f" || { printf ' "%s","no Files section"\n' "${f#"$ROOT"/}"; fails=$((fails+1)); }
|
|
150
|
+
done
|
|
151
|
+
done
|
|
152
|
+
if [ "$fails" = 0 ]; then printf 'fixes[1]{state,checked}:\n "valid",%s\n' "$checked"
|
|
153
|
+
else printf 'fixes[1]{state,failed}:\n "invalid",%s\n' "$fails"; exit 1; fi
|
|
154
|
+
;;
|
|
155
|
+
|
|
156
|
+
*) die "unknown action ${action:-}" "bin/fixes.sh [record|list|show|diff|validate|components]" ;;
|
|
157
|
+
esac
|
package/bin/ymir.js
CHANGED
|
@@ -82,6 +82,40 @@ function versionNotice() {
|
|
|
82
82
|
} catch { /* a version notice must never break a door */ }
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// ── is this copy current? ────────────────────────────────────────────────────
|
|
86
|
+
// An install can succeed and change NOTHING: npm resolves a cached `latest`, sees
|
|
87
|
+
// the same version, and says "changed 200 packages" while the old build stays. A
|
|
88
|
+
// user cannot tell — so the tool says it, once a day, where a human is watching.
|
|
89
|
+
function checkForUpdate() {
|
|
90
|
+
if (!process.stderr.isTTY || process.env.YMIR_NO_UPDATE_CHECK === '1') return;
|
|
91
|
+
if (noticeState('version') === 'off') return;
|
|
92
|
+
try {
|
|
93
|
+
const os = require('node:os');
|
|
94
|
+
const stamp = path.join(STATE_DIR, 'update-check');
|
|
95
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
96
|
+
if (fs.existsSync(stamp) && fs.readFileSync(stamp, 'utf8').trim() === today) return;
|
|
97
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
98
|
+
fs.writeFileSync(stamp, today + '\n');
|
|
99
|
+
const https = require('node:https');
|
|
100
|
+
const req = https.get('https://registry.npmjs.org/@zerwiz/ymir/latest', { timeout: 1500 }, (res) => {
|
|
101
|
+
let body = '';
|
|
102
|
+
res.on('data', (d) => { body += d; });
|
|
103
|
+
res.on('end', () => {
|
|
104
|
+
try {
|
|
105
|
+
const latest = JSON.parse(body).version;
|
|
106
|
+
if (latest && latest !== pkg.version) {
|
|
107
|
+
process.stderr.write('\n' + bronze(' a newer Ymir is on npm ') + faint(`${pkg.version} → `) + bronze(latest) + '\n' +
|
|
108
|
+
faint(' update: npm i -g @zerwiz/ymir (no PATH? npx @zerwiz/ymir)') + '\n' +
|
|
109
|
+
(noticeState('hints') === 'off' ? '' : faint(' (not again: ymir config notice version off)') + '\n'));
|
|
110
|
+
}
|
|
111
|
+
} catch { /* a notice must never break a door */ }
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
req.on('error', () => {});
|
|
115
|
+
req.on('timeout', () => req.destroy());
|
|
116
|
+
} catch { /* never break a door */ }
|
|
117
|
+
}
|
|
118
|
+
|
|
85
119
|
// ── the doors ────────────────────────────────────────────────────────────────
|
|
86
120
|
// verb → { script, args } — args are prepended to whatever the operator passes,
|
|
87
121
|
// so a verb can be a doorway to a sub-verb of a script that has several.
|
|
@@ -143,6 +177,7 @@ const argv = process.argv.slice(2);
|
|
|
143
177
|
const first = argv[0];
|
|
144
178
|
|
|
145
179
|
versionNotice();
|
|
180
|
+
checkForUpdate();
|
|
146
181
|
if (!first) run(DOORS.install.script, []);
|
|
147
182
|
if (first === '--version' || first === '-v' || first === '-V') {
|
|
148
183
|
process.stdout.write(`${pkg.version}\n`);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-11 — Chat uses the root Pi model catalog
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
- **Fix:** the Kaia chat guessed a llama.cpp model id by regex and fell through
|
|
5
|
+
to the Bifrost bridge (`:4603` → 401) when the guess failed.
|
|
6
|
+
- **Now:** the gate API reads the operator's root Pi catalog
|
|
7
|
+
(`~/.pi/agent/models.json`) for the exact provider → base URL, model id, and
|
|
8
|
+
key (llama.cpp router `:8080`, `sk-not-key-required`). A chosen local model
|
|
9
|
+
is tried only on its own base; the default is the operator's Pi default
|
|
10
|
+
(`qwen3.6-35b-a3b@iq3_s`). `/api/chat/models` lists the real connected set.
|
|
11
|
+
- **Verified:** `gemma-4-12b@q3_k_s`, `qwen3.6-35b-a3b@iq3_s` (default), and
|
|
12
|
+
`qwen3.5-9b@q4_k_s` all answered live; no 401.
|
|
13
|
+
|
|
14
|
+
### Files
|
|
15
|
+
- *(carried from the frozen CHANGELOG.md)*
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-11 — Shared well for every harness; no mock; clickable memories
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
- **Harnesses:** the engram MCP server is now registered for all four OpenCode
|
|
5
|
+
accounts (`opencode`, `opencode-rd`, `opencode-work`, `opencode-oczer`), Pi
|
|
6
|
+
(`~/.pi/agent/settings.json` + `mcp.json` + `.pi/mcp.json`), Claude, Cursor,
|
|
7
|
+
and Codex. Registered **unscoped** so every harness reads the one shared well
|
|
8
|
+
(per-call `agent_id` still attributes writes).
|
|
9
|
+
- **No mock:** purged every test/mock episode (`smoke`/`Test observation`) from
|
|
10
|
+
both the engram store and `episodes.jsonl` → 367 true episodes. Test writes
|
|
11
|
+
never enter the well.
|
|
12
|
+
- **UI:** Well memories are now **clickable** — `GET /api/well/episode?id=` and
|
|
13
|
+
a bridge `/episode` endpoint feed a modal with the full memory text.
|
|
14
|
+
- **Bridge fix:** `/recent` and `/recall` restored; added `/episode`.
|
|
15
|
+
- **Lifecycle fix:** `bifrost-bridge.sh` / `mimir-bridge.sh` now stop by process
|
|
16
|
+
match when the PID file is missing, and record the PID when the port is
|
|
17
|
+
already up — so `scripts/stop.sh` truly lowers the whole system and
|
|
18
|
+
`scripts/start.sh` truly raises it.
|
|
19
|
+
- **Galdr:** new asset `.agents/skills/galdr-ymirsystem/assets/memory-well.md` (store,
|
|
20
|
+
bridge, MCP, harness matrix, laws, verify), routed in `SKILL.md`; registry +
|
|
21
|
+
harness README updated; mirrored to tyr.
|
|
22
|
+
- **Verified:** MCP recall 1.0 / stats 367, full stop→start cycle (3888/3889/
|
|
23
|
+
4602/4603/8437), compliance 8/8, smoke 8/8.
|
|
24
|
+
|
|
25
|
+
### Files
|
|
26
|
+
- *(carried from the frozen CHANGELOG.md)*
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-13 — Gleipnir sees when an agent is turned off
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
- **Problem:** the machine lock stuck “held by another Brokk session” forever.
|
|
5
|
+
Nothing released it ( `gleipnir_lock_release` had zero callers; Pi's exit
|
|
6
|
+
hook only stopped the arm child), and liveness was `kill(0)` alone — which
|
|
7
|
+
reports a zombie (dead but unreaped) and a recycled pid as alive, so an
|
|
8
|
+
agent you turned off kept the helm until its pid happened to look gone.
|
|
9
|
+
Worse, the Sessrúmnir desktop RPC session holds the live lock yet never
|
|
10
|
+
arms supervision (no turn ever calls `gna_watch_arm`), stranding every
|
|
11
|
+
other session read-only.
|
|
12
|
+
- **Fix:** `bin/gleipnir-lock-lib.sh` now records the owner's starttime in a
|
|
13
|
+
`brokk.lock.starttime` sidecar and reaps a holder whose `/proc/<pid>/stat`
|
|
14
|
+
state is `Z`/`X` or whose starttime no longer matches (pid reuse) — dead,
|
|
15
|
+
zombie, and recycled holders are cleared at session start/acquire. Gná
|
|
16
|
+
(`gna-pi-watch.ts`) mirrors that liveness, reclaims a stale lock directly in
|
|
17
|
+
`gna_watch_arm` (`missing` no longer punts to saga-session-start.sh), and
|
|
18
|
+
drops its own lock on real process exit (`releaseLockIfOwned`); the turn-end
|
|
19
|
+
guard (`syn-turnend-guard.ts`) uses the same zombie-aware check.
|
|
20
|
+
- **Impact:** turning an agent off — cleanly, by kill (zombie), or after pid
|
|
21
|
+
reuse — frees the machine lock for the next session. The live desktop seat
|
|
22
|
+
still holds it until that app is closed or its own session arms; that is the
|
|
23
|
+
law (on
|
|
24
|
+
|
|
25
|
+
### Files
|
|
26
|
+
- *(carried from the frozen CHANGELOG.md)*
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-14 — The Eindri→Brokk wake bridge (they can talk back now)
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
- **Ask:** the seated smiths' reports lived in their panes only; the Allfather
|
|
5
|
+
wanted a feature that wakes Brokk when an Eindri reports, so the fleet's
|
|
6
|
+
doings reach the primary's session — "we are in control, Brokk".
|
|
7
|
+
- **Built:** `bin/eindri-seen.sh` (condition — a report file landed or herdr
|
|
8
|
+
shows the smith left `working`), `bin/eindri-acclaim.sh` (action — files the
|
|
9
|
+
report durably under state/eindri-reports/, marks done under
|
|
10
|
+
state/eindri-done/, appends the wake to state/.wake-queue for Sága's drain,
|
|
11
|
+
sounds the desktop note), and `bin/eindri-watch.sh` (the control door —
|
|
12
|
+
`arm | retire | list | reconcile`, one when-source per smith on the Norns'
|
|
13
|
+
loom via fm-procevent-when.sh, action hash-bound, fires once on a stable
|
|
14
|
+
true, terminal, re-armable).
|
|
15
|
+
- **Armed live:** when-odrerir (already fired — his report was filed) and
|
|
16
|
+
when-sessrumnir-cloth (still working). `fm-procement.sh reconcile` started=2.
|
|
17
|
+
- **First report delivered through the wire:** odrerir's Óðrerir saga — deck
|
|
18
|
+
green at :4322, 52 Chromium checks, commit 38a326b on yggdrasil/odrerir, main
|
|
19
|
+
untouched, plus the hall.ymir.zerwiz.org server story (setup-hall.sh) and
|
|
20
|
+
three asks awaiting the Allfather (go live, landing mobile wart, PLAN log).
|
|
21
|
+
|
|
22
|
+
### Files
|
|
23
|
+
- *(carried from the frozen CHANGELOG.md)*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-16 — a sandboxed worker cannot think: `auto` stops killing agents
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
Spawning an Eindri with the default `--isolation auto` produced an **empty pane
|
|
5
|
+
and no status line**. The cause: `auto` chose Utgard whenever the image existed,
|
|
6
|
+
and Utgard runs with `--network none` — so the worker could reach neither its
|
|
7
|
+
cloud model (OpenCode Go) nor a local one (llama.cpp) and died at launch, silently.
|
|
8
|
+
|
|
9
|
+
- **`auto` now keeps the worker in its worktree.** A spawned Eindri is a
|
|
10
|
+
model-driven worker: it must reach a model endpoint to think. Utgard is for
|
|
11
|
+
untrusted *code*, not for the agent's own brain, so the automatic choice is
|
|
12
|
+
`off`, and it says why.
|
|
13
|
+
- **`--isolation on` warns loudly.** It stays available for a sandbox that can
|
|
14
|
+
actually reach a model, but it now prints that Utgard has no network and the
|
|
15
|
+
agent will die silently without one.
|
|
16
|
+
|
|
17
|
+
### Files
|
|
18
|
+
- *(carried from the frozen CHANGELOG.md)*
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## agents · unversioned · 2026-09-17 — AGENTS.md states the one law: no private data in the public repo
|
|
2
|
+
|
|
3
|
+
### Why
|
|
4
|
+
- **The law is now first.** A `first_law` block sits directly under the mandate
|
|
5
|
+
in `AGENTS.md`: never store personal or private data in this repo — not a
|
|
6
|
+
secret, a key, a name, a plan, a schedule, a client, a credential, or a note.
|
|
7
|
+
The repo is public; private data lives at `$YMIR_HOME`, under `hodd/`.
|
|
8
|
+
- **The private-data section was rebuilt against the disk.** It had duplicated
|
|
9
|
+
`secrets/` and `identity/` lines, no `hodd/` level, and a layout that no longer
|
|
10
|
+
matched the home. It now shows the real tree, names `$YMIR_HOME/hodd/` as *the*
|
|
11
|
+
private data path, and records that `bin/hoard-lib.sh` is the one source of
|
|
12
|
+
truth for it.
|
|
13
|
+
- **Every `$YMIR_HOME/...` path in the file was corrected** to `$YMIR_HOME/hodd/...`
|
|
14
|
+
— the directory rules table, the registry reference, the secrets reference, and
|
|
15
|
+
the append-only ledger path all pointed at the old flat layout.
|
|
16
|
+
- **Two wards are now documented**: `bin/secret-guard.sh` (a secret entering the
|
|
17
|
+
repo) and Eir's `hoard` surface (private data drifting outside the hoard, or a
|
|
18
|
+
`.ymir-layout.yaml` naming a path that does not exist).
|
|
19
|
+
- **Staging discipline is written down.** Stage named files in the home, never
|
|
20
|
+
`git add -A` — a scratch file written seconds earlier gets swept into a commit
|
|
21
|
+
and pushed. This happened this day and cost a history rewrite.
|
|
22
|
+
- **`hodd/AGENTS.example.md`** carried the same stale flat paths and a wrong
|
|
23
|
+
|
|
24
|
+
### Files
|
|
25
|
+
- *(carried from the frozen CHANGELOG.md)*
|