@zerwiz/ymir 0.1.5 → 0.1.8
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/agents/huginn-researcher.md +1 -1
- package/.agents/agents/kvasir-scout.md +1 -1
- package/.agents/agents/sindri-developer.md +1 -1
- package/.agents/backend/README.md +74 -0
- package/.agents/backend/model-bridge.py +74 -4
- package/.agents/config/cron.yaml +2 -0
- package/.agents/config/eindri-dispatch.json +6 -6
- package/.agents/migrations/0003-private-data-separation.sh +11 -1
- package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
- package/.agents/skills/README.md +1 -1
- package/.agents/skills/galdr-ymirsystem/SKILL.md +2 -1
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +73 -10
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +44 -1
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +290 -24
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +3 -1
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +39 -1
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +4 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +7 -2
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +1 -1
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +2 -2
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +16 -11
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +147 -4
- package/.agents/skills/groa-update/SKILL.md +20 -0
- package/.agents/skills/hamr-adapters/SKILL.md +2 -2
- package/.agents/skills/lifecycle/smoke_test.sh +7 -2
- package/.agents/skills/ymir-host/SKILL.md +2 -1
- package/.agents/skills/ymir-host/assets/install.md +69 -0
- package/AGENTS.md +78 -34
- package/CHANGELOG.md +1362 -1
- package/README.md +1 -1
- package/RULES/02-agents.md +2 -2
- package/RULES/04-hoard.md +32 -0
- package/RULES/06-append-only.md +50 -0
- package/RULES/07-config.md +56 -0
- package/RULES/09-electron.md +58 -0
- package/RULES/README.md +2 -0
- package/bin/a2a-mcp.sh +42 -7
- package/bin/agents-config.sh +30 -7
- package/bin/apodex-smoke-test.sh +14 -1
- package/bin/bifrost-bridge.sh +25 -6
- package/bin/bootstrap-macos.sh +6 -1
- package/bin/brokk-send.sh +14 -1
- package/bin/changelog-assemble.sh +154 -0
- package/bin/changelog-guard.sh +39 -7
- package/bin/crash-sense.sh +14 -1
- package/bin/design-check.sh +80 -0
- package/bin/design-icon.sh +156 -0
- package/bin/docs-guard.sh +2 -2
- package/bin/editor-place.sh +33 -15
- package/bin/eindri-acclaim.sh +14 -1
- package/bin/eindri-seat.sh +83 -0
- package/bin/eindri-seen.sh +14 -1
- package/bin/eindri-start.sh +14 -1
- package/bin/einherjar-spawn.sh +9 -3
- package/bin/eir-doctor.sh +89 -1
- package/bin/electron-lib.sh +34 -0
- package/bin/feature-inventory.sh +73 -0
- package/bin/fleet-apply.sh +15 -2
- package/bin/github-deploy.sh +14 -1
- package/bin/gjallarhorn-expose.sh +43 -9
- package/bin/gjallarhorn-notify.sh +14 -1
- package/bin/gjallarhorn-purge.sh +14 -1
- package/bin/gjallarhorn-tunnel.sh +16 -3
- package/bin/groa-update.sh +9 -1
- package/bin/hall-snapshot.sh +21 -2
- package/bin/herdr-run.sh +23 -2
- package/bin/hlidskjalf-agents.sh +172 -0
- package/bin/hlidskjalf-usage.sh +196 -0
- package/bin/hoard-guard.sh +135 -0
- package/bin/hoard-lib.sh +91 -11
- package/bin/hodd.sh +28 -4
- package/bin/huginn-research-worker.sh +14 -1
- package/bin/local-model-lock.sh +22 -3
- package/bin/mimir-bridge.py +33 -0
- package/bin/mimir-bridge.sh +14 -1
- package/bin/mimir-reflect.sh +27 -0
- package/bin/mjollnir-webhook.sh +14 -1
- package/bin/mjollnir.sh +22 -3
- package/bin/model-hardware.sh +17 -3
- package/bin/model-resolve.sh +14 -1
- package/bin/nornir-job-hall-snapshot.sh +28 -0
- package/bin/nornir-job-nsr-compliance.sh +54 -0
- package/bin/npm-publish.sh +136 -0
- package/bin/omarchy-install.sh +18 -5
- package/bin/omarchy-plugins.sh +15 -2
- package/bin/omarchy-sense.sh +14 -1
- package/bin/pi-model-agnostic.sh +71 -0
- package/bin/private-guard.sh +20 -0
- package/bin/project-git.sh +18 -4
- package/bin/public-guard.sh +3 -2
- package/bin/sessrumnir-ensure.sh +1 -1
- package/bin/sessrumnir.sh +16 -3
- package/bin/smidja-board.sh +136 -0
- package/bin/smidja-bootstrap.sh +5 -1
- package/bin/smidja-lib.sh +49 -0
- package/bin/syn-asset-pretool-check.sh +14 -1
- package/bin/syn-spawn-pretool-check.sh +58 -0
- package/bin/tailscale-sync.sh +15 -2
- package/bin/telegram-bot.sh +14 -1
- package/bin/ui-truth-check.sh +75 -0
- package/bin/valknut-load.sh +42 -4
- package/bin/wedge-notify.sh +14 -1
- package/bin/workspace-rag.sh +14 -1
- package/bin/ymir-install.sh +257 -57
- package/bin/ymir-plan.sh +390 -0
- package/bin/ymir-say.sh +14 -1
- package/bin/ymir-setup-auth.sh +12 -1
- package/bin/ymir-style.sh +108 -0
- package/bin/ymir-validate.sh +64 -11
- package/bin/ymir.js +82 -34
- package/docs/design.md +2 -2
- package/docs/lore.md +5 -1
- package/docs/runbooks/agents.md +1 -1
- package/package.json +13 -3
- package/scripts/electron.sh +74 -15
- package/scripts/lower.sh +34 -0
- package/scripts/raise.sh +42 -0
- package/scripts/start.sh +65 -3
- package/.agents/config/agents.yaml +0 -50
- package/.agents/config/tailscale-sync.yaml +0 -27
- package/.agents/memory/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/well/episodes.jsonl +0 -367
- package/.agents/memory/well/workspace.jsonl +0 -27
|
@@ -28,6 +28,9 @@ Quick rules:
|
|
|
28
28
|
## Location & stack
|
|
29
29
|
|
|
30
30
|
- App: `apps/hlidskjalf` — **React 19 + Vite + TypeScript**, state via **Zustand**.
|
|
31
|
+
- The app lives in **its own repo** (`zerwiz/hlidskjalf`, registered in the home
|
|
32
|
+
registry) — the monorepo never tracks it; `bin/ymir-install.sh`'s `apps` step
|
|
33
|
+
clones it into `apps/hlidskjalf` at install, or fast-forwards a present clone.
|
|
31
34
|
- Gate API: `apps/hlidskjalf/server/index.ts` — **Bun + `bun:sqlite`**, read-only,
|
|
32
35
|
bound to the runtime and the smithy trace; Vite proxies `/api` → `:3889`.
|
|
33
36
|
- Raise everything with the scripts (do not hand-start each process):
|
|
@@ -95,6 +98,13 @@ Rules that hold it honest:
|
|
|
95
98
|
|
|
96
99
|
- `.shell` grid: `236px rail | 1fr`; rows `56px topbar · stage · stream`
|
|
97
100
|
(the stream height is draggable, persisted in `streamHeight`).
|
|
101
|
+
- The **Fleet graph** (`gates/Fleet.tsx`) lays the roster on a 100×100 canvas:
|
|
102
|
+
Brokk as the hub, everyone else in a square-ish grid fan (`cols =
|
|
103
|
+
ceil(sqrt(n))`, pitch ≥ one ring + label) so 20 agents never collide; rings
|
|
104
|
+
carry the house glyph from `DOMAINS[agent.domain]` with a `ymirlabs`
|
|
105
|
+
fallback, and the live cards come from `bin/hlidskjalf-agents.sh` — which
|
|
106
|
+
reads each figure's `domain:` frontmatter (never a hardcoded `ymirlabs` for
|
|
107
|
+
everyone).
|
|
98
108
|
- `.rail` (brand · gates · tenants · status), `.topbar` (realm chip · search ·
|
|
99
109
|
accent · density · trace index · account), `main.stage` (the gate), `.stream`
|
|
100
110
|
(Ratatoskr + Runes, pausable).
|
|
@@ -156,6 +166,18 @@ router `:8080`, Bifrost `:4603`), never only the first — a dead engine falls
|
|
|
156
166
|
through to a live one. Recall reads `.agents/memory/well/episodes.jsonl`
|
|
157
167
|
directly, so restoring that file revives the chat's memory without a restart.
|
|
158
168
|
|
|
169
|
+
### The EmberBackground hearth (the shared fire)
|
|
170
|
+
|
|
171
|
+
The animated glow behind the shell, the login, and Sessrúmnir's chat comes from
|
|
172
|
+
**one** hearth, not four copies: `midgard/design-system/ember.js` (`startEmbers`),
|
|
173
|
+
with Sessrúmnir's `ember-background.tsx` as its React port. Both draw the same
|
|
174
|
+
embers + haze, respect reduced-motion, and — since the Sep 17 mend — re-seed
|
|
175
|
+
via **ResizeObserver on the container**, not just `window.resize`: a split pane
|
|
176
|
+
or toggled sidebar changes the chat column's size without a window resize, and a
|
|
177
|
+
canvas that sized itself once at a stale dimension never shows the fire. The
|
|
178
|
+
shared module watches the host; the port watches its parent; re-seeding fires
|
|
179
|
+
only when the measured size actually changed (never per-frame).
|
|
180
|
+
|
|
159
181
|
### The gate API reads cheaply (added 2026-09-16)
|
|
160
182
|
|
|
161
183
|
The gate reads append-only ledgers (Runes, the well's `episodes.jsonl`, masterplan
|
|
@@ -201,7 +223,10 @@ API on the same port (`scripts/start.sh` raises it). Details: `assets/smidja.md`
|
|
|
201
223
|
## Agents, skills & mythological naming
|
|
202
224
|
|
|
203
225
|
- The Forge gate (`src/gates/Forge.tsx`) creates/edits **Eindri** and **skills**,
|
|
204
|
-
and reads/edits the smithy's prompts.
|
|
226
|
+
and reads/edits the smithy's prompts. The skill list shows each skill's own
|
|
227
|
+
text: the name + aett rune on the head row, and the description line
|
|
228
|
+
underneath (`forge-item-desc`, dimmed until hover) — the description comes
|
|
229
|
+
from the live `/api/skills` index, truncated to 240 chars by the gate API.
|
|
205
230
|
- Naming law: `src/data/mythology.ts` maps a craft/capability → the Norse figure
|
|
206
231
|
whose myth matches it (smith→Sindri, skald→Bragi, sage→Huginn, judge→Tyr,
|
|
207
232
|
forger→Brokk …). Skills take an **aett** prefix.
|
|
@@ -253,6 +278,17 @@ npm run build # must be green
|
|
|
253
278
|
- When the shell, tokens, gates, or the gate API change, update this asset and
|
|
254
279
|
`docs/design.md` together.
|
|
255
280
|
|
|
281
|
+
### The agent path the Fleet gate reads (2026-09-17)
|
|
282
|
+
|
|
283
|
+
`server/index.ts` decides whether a figure is *registered* by testing for its
|
|
284
|
+
profile in the OpenCode agent directory. That directory is **`.opencode/agents/`
|
|
285
|
+
— PLURAL**, which is what OpenCode actually loads. The singular
|
|
286
|
+
`.opencode/agent/` was never read by the harness, so twenty correct symlinks sat
|
|
287
|
+
in a directory no loader opened and the gate reported figures unregistered while
|
|
288
|
+
the tree held them all. Any code that resolves an agent profile must use the
|
|
289
|
+
plural path; the loader (`bin/valknut-load.sh`) migrates a legacy singular dir
|
|
290
|
+
forward and binds the plural one.
|
|
291
|
+
|
|
256
292
|
## Domains, not houses (Rule 01/03)
|
|
257
293
|
|
|
258
294
|
The eight Labs are **domains (Greinar)** — the knowledge axes — not houses. A
|
|
@@ -292,6 +328,13 @@ the password comes from `HLIDSKJALF_AUTH` in `.env.local`, never inline. A
|
|
|
292
328
|
hardcoded name here is both a leak into the public tree and wrong for any other
|
|
293
329
|
operator — `bin/public-guard.sh` exists to catch exactly that class of mistake.
|
|
294
330
|
|
|
331
|
+
**The gate must actually receive it.** `scripts/start.sh` loads `.env.local`
|
|
332
|
+
(mode `0600`, gitignored) into the environment before raising the gate, because
|
|
333
|
+
the bun process reads `process.env.HLIDSKJALF_AUTH` and nothing else was loading
|
|
334
|
+
it — so a credential set by `bin/ymir-setup-auth.sh` never took effect, and an
|
|
335
|
+
empty `GATE_AUTH` makes the gate treat every request as authenticated. After
|
|
336
|
+
setting a credential, restart the gate: `scripts/stop.sh; scripts/start.sh`.
|
|
337
|
+
|
|
295
338
|
### One login, and it is the one with the lore (2026-09-12)
|
|
296
339
|
|
|
297
340
|
There used to be **two** ways in, and the second was a mistake:
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
> page is the map.
|
|
6
6
|
|
|
7
7
|
One command sets up the whole system for the user; it self-heals what it can and
|
|
8
|
-
reports what it cannot. It **asks for consent first**
|
|
9
|
-
|
|
8
|
+
reports what it cannot. It **asks for consent first** — with a plan it computes on
|
|
9
|
+
this host, not a recited paragraph — and it **validates at the end** that what it
|
|
10
10
|
claims is actually running.
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
bin/ymir-install.sh # the first setup (idempotent; asks to proceed)
|
|
14
|
+
bin/ymir-install.sh --plan # the plan, probed — changes nothing (--json too)
|
|
14
15
|
bin/ymir-install.sh --check # report only, no writes, no prompt
|
|
15
16
|
bin/ymir-install.sh --yes # non-interactive (accept the plan)
|
|
16
17
|
bin/ymir-install.sh --skip-engines --skip-services
|
|
@@ -18,25 +19,267 @@ bin/ymir-install.sh --no-desktop # don't open the desktop apps at the end
|
|
|
18
19
|
bin/ymir-install.sh --status # alias of --check
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
## The plan comes first — `bin/ymir-plan.sh`
|
|
23
|
+
|
|
24
|
+
The consent a real install asks for is a **computed plan**, one row per step, each
|
|
25
|
+
carrying its state and the reason for it. A hardcoded paragraph cannot know the
|
|
26
|
+
host: the old one named an Omarchy version on a Mac, promised a workspace tree that
|
|
27
|
+
already stood, and never mentioned that no application had been installed at all.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
bin/ymir-plan.sh # the plan (TOON)
|
|
31
|
+
bin/ymir-plan.sh --json # the same, for automation
|
|
32
|
+
bin/ymir-plan.sh --phase 5 # one phase
|
|
33
|
+
bin/ymir-plan.sh --blocked # only what cannot proceed, and why
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
plan_states[5]{state,means}:
|
|
38
|
+
"DO","a change will be made"
|
|
39
|
+
"SKIP","already satisfied — nothing to do"
|
|
40
|
+
"INFO","a fact about this host, discovered; no change implied"
|
|
41
|
+
"BLOCKED","cannot run — the reason names what is missing"
|
|
42
|
+
"CONSENT","needs the operator's word (a credential, an invite, the shells)"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
plan_phases[9]{n,name,gate}:
|
|
47
|
+
"0","resolve","this host, the container engine, the code root, and the home"
|
|
48
|
+
"1","code","the tree is intact — and holds nothing of the operator's"
|
|
49
|
+
"2","home","the operator's world, outside the tree"
|
|
50
|
+
"3","runtimes","git/python3 · bun/uv/mcp<2> · pi · hermes · the terminal backend"
|
|
51
|
+
"4","engines","treehouse · no-mistakes · sandcastle · the Utgard image"
|
|
52
|
+
"5","apps","hlidskjalf · odrerir · sessrumnir · smidja — web build required, shell gated"
|
|
53
|
+
"6","wire","the way in (auth · invite) and the launcher entries"
|
|
54
|
+
"7","raise","every port listening, then the desktop apps"
|
|
55
|
+
"8","verify","what stands, honestly"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`ymir-install.sh` prints the plan at the consent prompt; `--plan`/`--dry-run` prints
|
|
59
|
+
it and exits without writing. The plan is recomputed on every run, so it cannot
|
|
60
|
+
drift behind the code the way a paragraph did. `bin/ymir-plan.sh` is the ward for
|
|
61
|
+
the law below: its phase-1 `purity` row names any of the operator's things found in
|
|
62
|
+
the code tree.
|
|
63
|
+
|
|
64
|
+
## Where things live — the package is code, the home is the operator's
|
|
65
|
+
|
|
66
|
+
**Law (Rule 04):** the package carries the **core systems** — everything needed to
|
|
67
|
+
run the programs. Everything the **operator** owns goes to `$YMIR_HOME`: their
|
|
68
|
+
info, their records, their documents, their settings, their state, their
|
|
69
|
+
credentials. A packaged install (npm) treats its tree as read-only; the next
|
|
70
|
+
upgrade replaces it, so anything of theirs kept there is kept at its peril.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
roots[5]{root,resolves_from,holds}:
|
|
74
|
+
"ymir_home_root","$YMIR_HOME → the choice recorded at installation → one documented default","everything private the operator owns"
|
|
75
|
+
"hoard_root","$YMIR_HOARD → <home>/hodd","docs · secrets · identity · tenants · memory"
|
|
76
|
+
"hoard_data_dir","$YMIR_DATA_DIR → <hoard>/data","this machine's records: operator, fleet, machines, the host profile"
|
|
77
|
+
"hoard_state_dir","$YMIR_STATE_DIR → <home>/state","runtime state: pids, logs, locks, caches"
|
|
78
|
+
"hoard_settings_dir","$YMIR_SETTINGS_DIR → <home>/config","settings: agents.yaml, cron.yaml, tailscale-sync, the wedge channel"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Plus `hoard_local_env` → `$YMIR_HOME/.env.local`: the operator's **credentials**
|
|
82
|
+
(`HLIDSKJALF_AUTH`, OAuth keys, tokens), never in the tree.
|
|
83
|
+
|
|
84
|
+
**The home is chosen, not assumed.** A real interactive install asks once
|
|
85
|
+
(`step_home`), records the answer as machine state under `~/.config/ymir/home`
|
|
86
|
+
(the same place `engram-python` and `accounts.json` live), and every later script
|
|
87
|
+
resolves it through `bin/hoard-lib.sh`. `--check` never writes; `--yes` takes what
|
|
88
|
+
is recorded, else the documented default.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bin/hoard-lib.sh # the lib (source-safe; functions only)
|
|
92
|
+
ymir_home_root HOME # → the home
|
|
93
|
+
hoard_settings_dir SETTINGS # → <home>/config
|
|
94
|
+
hoard_local_env ENVFILE # → <home>/.env.local
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Rule for new code:** never write `$ROOT/data`, `$ROOT/state`, `$ROOT/config`,
|
|
98
|
+
`$ROOT/.env.local` or `$ROOT/workspace`. Resolve the root through the lib. A script
|
|
99
|
+
that points an operator's thing into the tree is the bug the `purity` row exists to
|
|
100
|
+
catch.
|
|
101
|
+
|
|
102
|
+
### Carrying an existing tree home — migration 0005
|
|
103
|
+
|
|
104
|
+
Every *writer* resolves the home now, but an installation made before this change
|
|
105
|
+
still holds the operator's things in the tree. `.agents/migrations/0005-roots-out-of-tree.sh`
|
|
106
|
+
carries them:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
what moves from to
|
|
110
|
+
------------------ --------------------- ----------------------------
|
|
111
|
+
this machine's data/ <hoard>/data
|
|
112
|
+
records
|
|
113
|
+
runtime state state/ <home>/state
|
|
114
|
+
(pids · logs · (including the (running services keep their open
|
|
115
|
+
locks · the applied-migrations fds across the move — `mv` keeps
|
|
116
|
+
applied-marker) marker) the inode)
|
|
117
|
+
credentials the local env file $YMIR_HOME/.env.local (0600)
|
|
118
|
+
settings .agents/config/* <home>/config
|
|
119
|
+
that git does NOT track
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
One name, two files — the migration decides by evidence, never by assumption:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
collision[3]{case,what_happens,nothing_lost}:
|
|
126
|
+
"identical content","the tree's copy is removed","the content is provably at home already"
|
|
127
|
+
"different content","the home's keeps the name; the tree's is carried beside it as <name>.stale-<UTC>","both are real, so both are kept — never merged, never discarded"
|
|
128
|
+
"not a plain file","left in place and reported","a directory is not silently swallowed"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Templates and defaults never move: `*.example` (and `*.example.*`), `.gitkeep`, and
|
|
132
|
+
any settings file **git tracks** — that is the distro's shipped default, not the
|
|
133
|
+
operator's. The plan's `purity` row applies the same rule, asking git about the
|
|
134
|
+
real path (the tree's `config` is a symlink into `.agents/config`, and git tracks
|
|
135
|
+
what the index holds, not the link). Idempotent: a second run changes nothing.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
bin/ymir-migrate.sh status # pending / applied
|
|
139
|
+
bin/ymir-migrate.sh apply --dry-run # name the migration, touch nothing
|
|
140
|
+
bin/ymir-migrate.sh apply # carry it
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Stop the runtime first if you want no stale pid files; nothing is lost either way.
|
|
144
|
+
|
|
145
|
+
## Operating it afterwards — the doors, the cloth, and the shapes
|
|
146
|
+
|
|
147
|
+
### The doors (the CLI), named for the figure who does the work
|
|
148
|
+
|
|
149
|
+
`npm install -g @zerwiz/ymir` puts **two** commands on PATH: `ymir` (the front
|
|
150
|
+
door) and `ymir-install` (the bare installer). Everything else the operator
|
|
151
|
+
needs is a door on `ymir` — never a script path inside `node_modules`.
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
doors[14]{verb,door,what}:
|
|
155
|
+
"ymir","first setup (a bare call installs)","the plan, then consent, then the work"
|
|
156
|
+
"ymir install [...]","bin/ymir-install.sh","the same, said out loud"
|
|
157
|
+
"ymir plan","bin/ymir-plan.sh","what an install would do here — writes nothing"
|
|
158
|
+
"ymir raise / lower","scripts/start.sh · stop.sh","lift the hall, or lay it down"
|
|
159
|
+
"ymir eir","bin/eir-doctor.sh","what stands, and mend what does not (the healer)"
|
|
160
|
+
"ymir groa","bin/groa-update.sh","take the latest, then mend this home forward"
|
|
161
|
+
"ymir groa migrate","bin/ymir-migrate.sh","heal this home's structure"
|
|
162
|
+
"ymir heimdall","bin/ymir-setup-auth.sh","the way in — status · set · github (the guardian)"
|
|
163
|
+
"ymir invite","bin/ymir-invite.sh","let someone else in — mint · list · revoke"
|
|
164
|
+
"ymir smidja","bin/smidja-board.sh","the smithy's board on :8437 — build · start · stop · status"
|
|
165
|
+
"ymir hlidskjalf","scripts/electron.sh start --view hlidskjalf","the high seat's window"
|
|
166
|
+
"ymir sessrumnir","scripts/electron.sh start --view sessrumnir","the seat-hall's window"
|
|
167
|
+
"ymir mimir","bin/mimir.sh","the memory well"
|
|
168
|
+
"ymir sense","bin/host-sense.sh","what THIS machine is"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
A name the law has not given a home is still answered, once, with the name that
|
|
172
|
+
has it: `ymir doctor` → *the door is named `ymir eir` now*. Colour appears only
|
|
173
|
+
where a human watches.
|
|
174
|
+
|
|
175
|
+
### The cloth — `bin/ymir-style.sh`
|
|
176
|
+
|
|
177
|
+
Ymir had correct output and no design. The cloth is cut from the same stone as
|
|
178
|
+
the halls (`midgard/design-system/tokens.css`): **bone** `#cfc3a9` for words,
|
|
179
|
+
**bronze** `#c9973f` for what acts, **steel** `#96a0a8` for what stands, **blood**
|
|
180
|
+
`#c2584a` for what is wrong, and a mark per state (`◆` do · `·` already · `—`
|
|
181
|
+
fact · `✕` blocked · `?` needs your word · `✓` proved).
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
cloth_rules[4]{rule,why}:
|
|
185
|
+
"colour and marks only when stderr is a terminal and NO_COLOR is unset","a pipeline never parses a decoration"
|
|
186
|
+
"data on stdout, the human rendering on stderr","the TOON row is the data; the coloured line is for the eye"
|
|
187
|
+
"the words are never conditional — only the colour is","hiding information to save colour is the wrong trade"
|
|
188
|
+
"no banner over four lines, no rule longer than its text","density first (Monoline TUI · cli-guidelines)"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`bin/ymir-plan.sh --colour` renders the plan in the cloth on stderr while the
|
|
192
|
+
TOON stays on stdout. The installer prints it at the consent, and ends with the
|
|
193
|
+
next steps — *a reaction for every action, a next step for every ending*.
|
|
194
|
+
|
|
195
|
+
### Two libs the packaged tree needs
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
libs[2]{path,resolves}:
|
|
199
|
+
"bin/smidja-lib.sh","the smithy: apps/smidja-factory in a clone, node_modules/@zerwiz/smidja-factory in a package — `smidja_factory_dir`, `smidja_visualizer_dir`"
|
|
200
|
+
"bin/electron-lib.sh","whether a shell's runtime VERIFIES: `electron_runtime_state` (ok · partial · absent) and the exact remedy"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Why they exist.** A packaged tree has no `apps/`: the smithy arrives as a
|
|
204
|
+
dependency and the skill symlink dangles. Four call sites assumed the clone's
|
|
205
|
+
layout, so the visualizer read as unbuildable in a package. They resolve through
|
|
206
|
+
`bin/smidja-lib.sh` now.
|
|
207
|
+
|
|
208
|
+
**And a shell is never declared ready on a directory's presence.** npm gates
|
|
209
|
+
install scripts; a skipped Electron postinstall leaves `dist/` partial and
|
|
210
|
+
`path.txt` unwritten, so the web app builds, every check passes, and the window
|
|
211
|
+
never opens. `electron_runtime_state` verifies, and the plan's `electron` row
|
|
212
|
+
says *the runtime is PARTIAL* with the command that mends it.
|
|
213
|
+
|
|
214
|
+
## The app packages — how the four surfaces arrive
|
|
215
|
+
|
|
216
|
+
The distro depends on the surfaces as their own npm packages, so one
|
|
217
|
+
`npm install -g @zerwiz/ymir` fetches them into the tree:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
app_packages[4]{package,repo,what}:
|
|
221
|
+
"@zerwiz/hlidskjalf","zerwiz/hlidskjalf","the control plane — dist/ ships built, served on :3888"
|
|
222
|
+
"@zerwiz/odrerir","zerwiz/odrerir","the live hall — dist/ ships built"
|
|
223
|
+
"@zerwiz/sessrumnir","zerwiz/sessrumnir","the seat-hall desktop — out/ ships built (a fork of pi-desktop)"
|
|
224
|
+
"@zerwiz/smidja-factory","zerwiz/smidja","the smithy and its visualizer — the factory plus the UI's source, built at install"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
They are **optionalDependencies**, deliberately: a broken app package must never
|
|
228
|
+
stop the CORE from installing, and a package not yet on the registry is skipped by
|
|
229
|
+
npm and starts arriving the moment it is published. The plan's phase-5 rows report
|
|
230
|
+
each surface separately — installed, declared-but-not-fetched, or no package at all
|
|
231
|
+
— so a silent skip cannot hide.
|
|
232
|
+
|
|
233
|
+
A global install nests them under the distro's own `node_modules`
|
|
234
|
+
(`<prefix>/lib/node_modules/@zerwiz/ymir/node_modules/@zerwiz/<app>`); a local one
|
|
235
|
+
hoists them to `node_modules/@zerwiz/<app>`. The plan checks both shapes.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
bin/npm-publish.sh # the platform only
|
|
239
|
+
bin/npm-publish.sh --all # the platform + every app package
|
|
240
|
+
bin/npm-publish.sh --dry-run --all # what would go out, and from where
|
|
241
|
+
bin/npm-publish.sh --unpublish @zerwiz/ymir@0.1.5 # take ONE version back
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**A version can be taken back, and the window is short.** npm permits unpublishing
|
|
245
|
+
**one version for 72 hours** after it was published; past that it is npm support's
|
|
246
|
+
door. So `--unpublish` demands a spec that names the version — never a bare name —
|
|
247
|
+
and the row says where the CDN may still serve the tarball for a while afterwards.
|
|
248
|
+
Publishing a newer version is the other half of the repair: it moves the `latest`
|
|
249
|
+
tag off the bad build at once, even before the removal propagates.
|
|
250
|
+
|
|
251
|
+
**A `files[]` entry that names a directory overrides `.gitignore`.** This is how
|
|
252
|
+
`@zerwiz/ymir@0.1.5` shipped the memory well: `files: [".agents/"]` packed that
|
|
253
|
+
subtree *including* the gitignored stores, and the repo stayed clean while the
|
|
254
|
+
artefact did not. Name what ships, exclude what must not, and prove it with
|
|
255
|
+
`npm pack --dry-run` — the repo's cleanliness says nothing about the tarball.
|
|
256
|
+
|
|
257
|
+
The app repos are cloned into `apps/` by the install's `apps` step (from the
|
|
258
|
+
registry's `repo: apps/<path>` blocks), which is where `--all` reads their
|
|
259
|
+
manifests. The token comes from the hoard, never from `~/.npmrc`.
|
|
260
|
+
|
|
21
261
|
## The steps
|
|
22
262
|
|
|
23
263
|
```
|
|
24
|
-
install[
|
|
264
|
+
install[23]{step,what,self-heals}:
|
|
25
265
|
"panes","the run shown in a herdr pane","bin/herdr-run.sh sits a pane beside the caller when inside herdr; inline otherwise — a pane that cannot be raised never loses the work"
|
|
26
266
|
"prereqs","git python3 bun docker|podman gh · mcp<2","bin/prereq-ensure.sh installs bun+uv+mcp in user space; engram is an honest optional SKIP"
|
|
27
267
|
"memory-well","the engram engine (Mimirsbrunn)","optional; reported with the exact next command, never a fake fix"
|
|
28
|
-
"
|
|
268
|
+
"home","the home the operator CHOOSES, recorded under ~/.config/ymir/home","asks once, records the answer; --check never writes, --yes takes what is recorded, else the documented default"
|
|
269
|
+
"tree","workspace/{work,personal}/<domains>, companies/, workspaces.yaml, projects.yaml, and the hoard OUTSIDE the repo (secrets/ · docs/ · identity/ · tenants/ at the hoard root under the chosen home)","creates if missing; hoard_root resolves through bin/hoard-lib.sh so no script can point the hoard inside the checkout (Rule 04), and an empty secrets/platform.env (0600) is seeded so bin/hodd.sh emit resolves"
|
|
270
|
+
"apps","the app repos (the app split) — hlidskjalf · hlidskjalf-mobile · odrerir · sessrumnir · smidja","reads $HOARD/identity/projects.yaml (never guesses a remote); clones a missing apps/<path> from its registered git{} block, fast-forwards a present one, and stamps the smithy engine (apps/smidja) from the cloned factory's templates"
|
|
29
271
|
"engines","treehouse · sandcastle · no-mistakes","installs treehouse + no-mistakes from their installers"
|
|
30
272
|
"hermes","the Nous Research agent runtime","installs via bin/hermes-ensure.sh when absent"
|
|
31
|
-
"sessrumnir","the Sessrúmnir desktop GUI (
|
|
273
|
+
"sessrumnir","the Sessrúmnir desktop GUI (its own repo; lands via the `apps` step at apps/sessrumnir)","bin/sessrumnir-ensure.sh installs deps + builds on first run (deps are never committed); launch via bin/sessrumnir.sh"
|
|
32
274
|
"backend","Þjazi — herdr (protocol 14+) or tmux","bin/herdr-ensure.sh detects/tests version, installs via the pinned installer or falls back to tmux"
|
|
33
|
-
"host","this machine —
|
|
275
|
+
"host","this machine — sensed on EVERY host","bin/host-sense.sh senses the setup on ANY host (Rule 05); the Omarchy layer then RECORDS it (bin/omarchy-sense.sh observe), places the apps (bin/desktop-place.sh), installs the post-update hook and the wedge-alarm channel, and (on Omarchy) offers the suggested shell plugins — listed, never installed unbidden; seeds the private config/agents.yaml from its example"
|
|
34
276
|
"sandbox","utgard-runner:latest image","builds via bin/utgard.sh build on Docker or rootless Podman; distinguishes an unreachable engine from a build failure"
|
|
35
277
|
"memory","engram store + harness MCP registrations","raises the bridge; reports MCP coverage"
|
|
36
278
|
"smidja","smidja/smidja_data/smidja.db","bin/smidja-bootstrap.sh creates it from the tracer schema + a bootstrap session"
|
|
37
279
|
"visualizer","the Smíðja visualizer UI (Vue, served on :8437)","builds ./dist with bun when absent — the API serves the UI from dist, and without it the API answers but shows no interface"
|
|
38
280
|
"loaders","agents/skills into the harnesses","runs bin/valknut-load.sh"
|
|
39
281
|
"gates","the git delivery gates — secret-guard (pre-commit), branch-guard + changelog-guard (pre-push)","bin/secret-guard.sh --install and bin/changelog-guard.sh --install seat the versioned guards into .git/hooks, so the gate is live from the first commit of a fresh clone; idempotent"
|
|
282
|
+
"marks","each app's rune icon + .desktop entry into the operator's own desktop, and the Ymir contract into pi's agent home","bin/design-icon.sh mint --all + install writes to $HOME/.local/share (never a session sandbox), so every app is dockable and pinnable; the contract symlink means every pi session, in ANY folder, loads Brokk"
|
|
40
283
|
"invite","the way in for anyone else — an invite code","bin/ymir-invite.sh ensure mints one only when nothing is live, so the step is idempotent; the code is printed at the end of the run and again in workspace/INSTALL.md"
|
|
41
284
|
"register","workspace/INSTALL.md","writes the record"
|
|
42
285
|
"services","gate API, SPA, Nornir, bridges, visualizer","raises via scripts/start.sh (which builds the visualizer UI when ./dist is absent)"
|
|
@@ -44,7 +287,7 @@ install[20]{step,what,self-heals}:
|
|
|
44
287
|
"validate","the running system","bin/ymir-validate.sh — live port/store/process checks"
|
|
45
288
|
```
|
|
46
289
|
|
|
47
|
-
**
|
|
290
|
+
**25** `step_*` functions are defined (`home` asks, `tree` builds). A step is not a row: one step may emit
|
|
48
291
|
several. `prereqs` also emits `memory-well`, `host` also emits `agents-config`,
|
|
49
292
|
`smidja` also emits `visualizer`, and `spa` also emits `hlidskjalf`. `--check`
|
|
50
293
|
skips the runtime-only steps (`services`, `desktop`, `validate`), which have
|
|
@@ -94,8 +337,10 @@ now build it when absent:
|
|
|
94
337
|
(cd .agents/skills/smidja-factory/apps/visualizer && bun run build) # vue-tsc + vite
|
|
95
338
|
```
|
|
96
339
|
|
|
97
|
-
`bin/ymir-validate.sh` reports `visualizer` FAIL when `./dist` is missing
|
|
98
|
-
|
|
340
|
+
`bin/ymir-validate.sh` reports `visualizer` FAIL when `./dist` is missing **and**
|
|
341
|
+
when the build exists but nothing is listening on `:8437`. A PASS means the UI is
|
|
342
|
+
built *and* the API is up — so a built-but-dead visualizer (a bad `CMD_DB`, a
|
|
343
|
+
crashed API) can no longer read as green.
|
|
99
344
|
|
|
100
345
|
## Desktop placement (Omarchy desktops, not monitors)
|
|
101
346
|
|
|
@@ -163,13 +408,25 @@ non-interactive callers; without it a non-interactive `add` refuses with exit 3)
|
|
|
163
408
|
The installer only *offers*.
|
|
164
409
|
|
|
165
410
|
## Consent
|
|
166
|
-
A real install prints
|
|
167
|
-
(exit 3). `--check`
|
|
168
|
-
refused rather than silently
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
411
|
+
A real install prints **the plan it computed** (`bin/ymir-plan.sh`) and waits for
|
|
412
|
+
`[y/N]`. Declining changes nothing (exit 3). `--check` and `--plan` never prompt.
|
|
413
|
+
A non-interactive caller without `--yes` is refused rather than silently
|
|
414
|
+
proceeding.
|
|
415
|
+
|
|
416
|
+
The plan names every step with its state and the reason for it — including the
|
|
417
|
+
home the operator is asked to choose, the four app surfaces and whether each can
|
|
418
|
+
be installed at all, the terminal backend, and what will be skipped and why. The
|
|
419
|
+
state vocabulary is `DO · SKIP · INFO · BLOCKED · CONSENT`.
|
|
420
|
+
|
|
421
|
+
`--check` writes nothing — and that includes the migrations. `bin/ymir-migrate.sh
|
|
422
|
+
apply` **moves private data**, so the step chain runs it only on a real run;
|
|
423
|
+
a preview leaves the home exactly as it found it. (It used to apply them even
|
|
424
|
+
under `--check`, which moved a home during a "report only" pass.)
|
|
425
|
+
|
|
426
|
+
`--check` writes nothing — and that includes the migrations. `bin/ymir-migrate.sh
|
|
427
|
+
apply` **moves private data**, so the step chain runs it only on a real run;
|
|
428
|
+
a preview leaves the home exactly as it found it. (It used to apply them even
|
|
429
|
+
under `--check`, which moved a home during a "report only" pass.)
|
|
173
430
|
|
|
174
431
|
## The operator's way in (auth) — `bin/ymir-setup-auth.sh`
|
|
175
432
|
|
|
@@ -225,7 +482,7 @@ part is off. `--json` for machine consumption.
|
|
|
225
482
|
| **sandcastle** (`mattpocock/sandcastle`) | Utgard | Docker/Podman/Vercel sandboxes |
|
|
226
483
|
| **no-mistakes** (`kunchenguid/no-mistakes`) | Mjollnir · Glitnir | clean-PR validation gate |
|
|
227
484
|
| **Hermes** (`NousResearch/hermes-agent`, MIT) | — (product name) | worker agent runtime: own brain, memory, skills, subagents, sandbox backends |
|
|
228
|
-
| **pi-desktop** (`FaqFirebase/pi-desktop`, Apache-2.0) | **Sessrúmnir** (
|
|
485
|
+
| **pi-desktop** (`FaqFirebase/pi-desktop`, Apache-2.0) | **Sessrúmnir** (cloned from its own repo to `apps/sessrumnir` at install) | desktop GUI for the Pi/OMP coding agents — the seat-hall, themed with the Ymir way-of palette |
|
|
229
486
|
|
|
230
487
|
## Missing dependencies
|
|
231
488
|
|
|
@@ -298,14 +555,16 @@ company is the operator's to name; Ymir ships no default.
|
|
|
298
555
|
bin/ymir-install.sh --check # all steps OK/WARN
|
|
299
556
|
bin/ymir-validate.sh # the running system actually works
|
|
300
557
|
bin/herdr-ensure.sh status # the Þjazi backend and its protocol floor
|
|
301
|
-
bin/
|
|
558
|
+
bin/host-sense.sh # sense THIS machine (any host)
|
|
559
|
+
bin/omarchy-sense.sh status # the Omarchy recording (Omarchy hosts)
|
|
302
560
|
bin/saga-session-start.sh # the session digest
|
|
303
561
|
bash .agents/skills/galdr-ymirsystem/scripts/compliance-check.sh
|
|
304
562
|
```
|
|
305
563
|
|
|
306
|
-
On
|
|
307
|
-
(`bin/omarchy-sense.sh`)
|
|
308
|
-
|
|
564
|
+
On **every** host the installer senses the machine with `bin/host-sense.sh`. On
|
|
565
|
+
an **Omarchy** host the layer also *records* it (`bin/omarchy-sense.sh observe`)
|
|
566
|
+
and installs a `post-update` hook so Ymir re-learns it every time Omarchy
|
|
567
|
+
updates. On a non-Omarchy host that layer step is a clean SKIP.
|
|
309
568
|
|
|
310
569
|
Rule: the installer is **idempotent** — running it again changes nothing but
|
|
311
570
|
fills gaps. It never overwrites real user data.
|
|
@@ -470,7 +729,7 @@ machines may run the runtime.
|
|
|
470
729
|
| Layer | Runs on | What it owns |
|
|
471
730
|
|---|---|---|
|
|
472
731
|
| **Portable core** | Linux, macOS, Windows (WSL2; MSYS best-effort) | the runtime — session digest, lock, watch, cron, skills — the installer's user-space prerequisites, the capability shim, generated machine config |
|
|
473
|
-
| **Omarchy layer** | Omarchy only |
|
|
732
|
+
| **Omarchy layer** | Omarchy only | recording the host (`omarchy-sense observe`), placing each app on its own numbered Hyprland desktop (`desktop-place`), the launcher entries (`.desktop`), the suggested shell plugins, the post-update hook |
|
|
474
733
|
|
|
475
734
|
The core never grows a Hyprland branch, and the Omarchy layer is never stretched
|
|
476
735
|
into pretending it is portable. An Omarchy-specific step is gated on the host and
|
|
@@ -480,8 +739,15 @@ reports a clean skip anywhere else:
|
|
|
480
739
|
[ -d /usr/share/omarchy ] && ... || add host OK "... (not an Omarchy host)"
|
|
481
740
|
```
|
|
482
741
|
|
|
483
|
-
That is why
|
|
484
|
-
|
|
742
|
+
That is why the only Omarchy branches left in the core installer are the ones
|
|
743
|
+
that call the layer (`step_omarchy`) and desktop placement (`step_desktop`):
|
|
744
|
+
`step_host` senses the host portably with `host-sense`, and everywhere else the
|
|
745
|
+
same code path runs on any host.
|
|
746
|
+
|
|
747
|
+
The **consent preamble** the installer prints before it acts says the same thing —
|
|
748
|
+
*"learn this machine (OS, desktop, packages, configs, monitors, scale — Omarchy
|
|
749
|
+
hosts recorded first-class)"* — so the operator is told what will happen in the
|
|
750
|
+
portable terms the code now uses, not the Omarchy-only terms it used before.
|
|
485
751
|
|
|
486
752
|
### What the Omarchy layer installs
|
|
487
753
|
|
|
@@ -154,7 +154,9 @@ hardcoded timeline is gone.
|
|
|
154
154
|
- **First `observe` slow (~20–30 s)** → the embedding model is warming; later
|
|
155
155
|
writes are instant.
|
|
156
156
|
- **Bridge down / `COLD`** → `bin/mimir-bridge.sh --start`; check
|
|
157
|
-
|
|
157
|
+
`$YMIR_STATE_DIR/mimir-bridge.log` — runtime state lives in the home the
|
|
158
|
+
operator chose (`hoard_state_dir` via `bin/hoard-lib.sh`), never in the code
|
|
159
|
+
tree: a packaged install replaces its tree on upgrade.
|
|
158
160
|
|
|
159
161
|
**Portability.** The bridge signals processes through `ymir_kill_matching` from
|
|
160
162
|
`bin/ymir-platform.sh` rather than calling `pkill` directly, because `pkill` is
|
|
@@ -62,7 +62,7 @@ same day. That is deliberate: cron is idempotent-by-date, not retrying-by-failur
|
|
|
62
62
|
|---|---|---|
|
|
63
63
|
| `BROKK_ROOT_OVERRIDE` | script's parent | Root when `BROKK_HOME` unset. |
|
|
64
64
|
| `BROKK_HOME` | `$ROOT` | Home owning the jobs and state. |
|
|
65
|
-
| `BROKK_STATE_OVERRIDE` | `$
|
|
65
|
+
| `BROKK_STATE_OVERRIDE` | `$YMIR_STATE_DIR` (`<home>/state`, via `bin/hoard-lib.sh`) | Scheduler state directory — in the home the operator chose, never in the code tree. |
|
|
66
66
|
| `BROKK_CONFIG_OVERRIDE` | `$BROKK_HOME/config` | Where `cron.yaml` lives. |
|
|
67
67
|
| `BROKK_CRON_LOG_MAX_BYTES` | `1048576` | Rotation threshold. |
|
|
68
68
|
| `BROKK_REALM` | `""` | Realm exported to jobs (jobs also fall back to `data/realm.md`). |
|
|
@@ -153,6 +153,44 @@ removed and the failure is reported plainly.
|
|
|
153
153
|
| `BROKK_MEMORY_PRUNE` | `0` (disabled) |
|
|
154
154
|
| `BROKK_MEMORY_PRUNE_DAYS` | `7` |
|
|
155
155
|
|
|
156
|
+
### 3.5 Óðrerir — Live Hall snapshot (`bin/nornir-job-hall-snapshot.sh`, 08:00)
|
|
157
|
+
|
|
158
|
+
The Live Hall is a glass: it reads `apps/odrerir/public/livehall.json`
|
|
159
|
+
(same-origin, `cache: no-store`). This job writes that snapshot from real state
|
|
160
|
+
via `bin/hall-snapshot.sh` — runes, the project registry, the cron gauge, the
|
|
161
|
+
wake queue, standing smiths, armed `when-` sources, and the landed errands —
|
|
162
|
+
then carves Rune `odrerir / hall.snapshot`. Scheduled at 08:00 so it follows
|
|
163
|
+
the 06:00 observer and the 07:00 briefing: the morning board carries the day's
|
|
164
|
+
fresh runes.
|
|
165
|
+
|
|
166
|
+
| Reads | Writes |
|
|
167
|
+
|---|---|
|
|
168
|
+
| runes ledger · `hodd/identity/projects.yaml` · `config/cron.yaml` · `state/.wake-queue` · herdr agent list · armed when-sources · `state/eindri-reports/archive/` | `apps/odrerir/public/livehall.json` (gitignored runtime) · Rune `odrerir / hall.snapshot` |
|
|
169
|
+
|
|
170
|
+
- The snapshot is **runtime, never repo**: `apps/odrerir/public/livehall.json`
|
|
171
|
+
is gitignored, so the job never dirties a branch.
|
|
172
|
+
- Absent `livehall.json` on the Hall is *not* a build failure — the page paints
|
|
173
|
+
the saga's own count and says so. This job is what makes the board true.
|
|
174
|
+
- Idempotent by nature: each run rewrites the same snapshot from the same
|
|
175
|
+
inputs; the cron date-guard suppresses repeat dispatch within a day.
|
|
176
|
+
|
|
177
|
+
### 3.6 Tyr — NSR compliance round (`bin/nornir-job-nsr-compliance.sh`, 02:30)
|
|
178
|
+
|
|
179
|
+
The NorthStar deterministic gate, run nightly in the quiet hours so sunrise
|
|
180
|
+
finds the doors mended or the Rune already says which broke. Runs every
|
|
181
|
+
`.compliance/gates/check_*.sh` (danger · env · paths · platform · wiring)
|
|
182
|
+
and carves one Rune with the verdict — `nornir / nsr.compliance` on a clean
|
|
183
|
+
round, `nornir / nsr.compliance.failed` (exit 1) naming the failed gates.
|
|
184
|
+
|
|
185
|
+
| Reads | Writes |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `.compliance/gates/check_*.sh` (deterministic, no network) | Rune `nornir / nsr.compliance[.failed]` · `state/last` line |
|
|
188
|
+
|
|
189
|
+
- The morning briefing reads the ledger, so a FAIL is seen at 07:00, not
|
|
190
|
+
found by accident.
|
|
191
|
+
- Idempotent: gates are pure checks; the cron date-guard suppresses repeat
|
|
192
|
+
dispatch within a day; safe to invoke by hand (`bash bin/nornir-job-nsr-compliance.sh`).
|
|
193
|
+
|
|
156
194
|
### 3.4 Yggdrasil — git sync (`bin/nornir-job-git-sync.sh`, 00:00)
|
|
157
195
|
|
|
158
196
|
The world-tree kept in order. Two modes, both non-destructive:
|
|
@@ -110,6 +110,10 @@ snapshot[1]{command,out}:
|
|
|
110
110
|
- **Ports are sacred:** `:4322` is Óðrerir's alone. Do not move it into a
|
|
111
111
|
Hlidskjalf route or a launcher call — its door is a plain anchor in a new tab
|
|
112
112
|
(`.hall-btn`, rune **ᛟ**, "To the Hall") and its own window.
|
|
113
|
+
- **`.astro/dev.json` is runtime state, never tracked.** Astro rewrites it on
|
|
114
|
+
every start with the live `pid` and the host's own addresses (LAN + tailnet),
|
|
115
|
+
so committing it leaks a private IP per run. It is gitignored; the generated
|
|
116
|
+
types beside it (`content.d.ts`, `types.d.ts`) stay tracked.
|
|
113
117
|
|
|
114
118
|
## 5. Files you will touch
|
|
115
119
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
# PI Profile —
|
|
1
|
+
# PI Profile — the SHAPE of the Brokk harness profile, not a machine's values.
|
|
2
|
+
#
|
|
3
|
+
# Rule 07A: a model id is the USER's, not the repo's. Every operator's differ, so
|
|
4
|
+
# this template carries a placeholder; the live values come from the user's own
|
|
5
|
+
# roster at $YMIR_HOME/hodd/config/agents.yaml, resolved through
|
|
6
|
+
# bin/agents-config.sh. Never commit a real model id here.
|
|
2
7
|
|
|
3
8
|
profile:
|
|
4
9
|
name: "pi"
|
|
@@ -6,7 +11,7 @@ profile:
|
|
|
6
11
|
version: "1.0.0"
|
|
7
12
|
|
|
8
13
|
agent:
|
|
9
|
-
model: "
|
|
14
|
+
model: "<your-model-id>" # resolved from $YMIR_HOME/hodd/config/agents.yaml
|
|
10
15
|
temperature: 0.7
|
|
11
16
|
max_tokens: 8192
|
|
12
17
|
system_prompt: "BROKK — SYSTEM OPERATING MANUAL (see .opencode/agents/brokk.md)"
|
|
@@ -292,7 +292,7 @@ The house law lives in `RULES/`; Galdr must know it (Rule 01/02/03):
|
|
|
292
292
|
|
|
293
293
|
- **Eindri are the specialists** (marketer, builder, researcher, planner,
|
|
294
294
|
reviewer, documenter, scout, …); each names its **domain** and its **craft**.
|
|
295
|
-
- **Agents live only in `.agents/agents/*.md`.** `.opencode/
|
|
295
|
+
- **Agents live only in `.agents/agents/*.md`.** `.opencode/agents` and
|
|
296
296
|
`.pi/agents` are **symlinks** to the canonical profiles — never edit them;
|
|
297
297
|
edit `.agents/agents/<profile>.md` and run `bin/valknut-load.sh --all`.
|
|
298
298
|
- **No mock agents.** Ids, names, domains, models, status are real and sourced;
|
|
@@ -32,7 +32,7 @@ siblings of this file (`eindri-orchestration.md`, `nornir-jobs.md`). The runtime
|
|
|
32
32
|
| 11 | Secrets never committed | secret scan + ignore audit | No secret literal; ignore rules cover env files. |
|
|
33
33
|
| 12 | Governed assets current | `compliance-check.sh` (`assets` check) | A governed path changed in the working tree has its owning asset changed too. |
|
|
34
34
|
| 13 | **Governed paths resolve** | `compliance-check.sh` (`governed` check) | Every path in `AGENTS.md`'s `governed[]` table exists (or its glob matches something). |
|
|
35
|
-
| 14 | **Harness surfaces resolve** | `compliance-check.sh` (`harnesses` check) | Every link in `.claude/agents`, `.codex/agents`, `.cursor/agents`, `.pi/agents`, `.opencode/
|
|
35
|
+
| 14 | **Harness surfaces resolve** | `compliance-check.sh` (`harnesses` check) | Every link in `.claude/agents`, `.codex/agents`, `.cursor/agents`, `.pi/agents`, `.opencode/agents` lands (one hop) in `.agents/agents/`, and no nested `SKILL.md` carries frontmatter — a phantom skill. |
|
|
36
36
|
| 15 | **Skill index true** | `compliance-check.sh` (`skillindex` check) | Every real skill dir is named in `.agents/skills/README.md`, and every `.agents/skills/<name>` path the assets cite exists. |
|
|
37
37
|
|
|
38
38
|
### Governed paths — load the asset before you edit
|
|
@@ -88,7 +88,7 @@ Two failures hide in the harness layer, and neither is visible from the code:
|
|
|
88
88
|
|
|
89
89
|
1. **A symlink that no longer resolves.** Rule 02 says `.agents/agents` is
|
|
90
90
|
canonical and every harness dir (`.claude/agents`, `.codex/agents`,
|
|
91
|
-
`.cursor/agents`, `.pi/agents`, `.opencode/
|
|
91
|
+
`.cursor/agents`, `.pi/agents`, `.opencode/agents`) is a link into it. The
|
|
92
92
|
`galdr-cli` → `galdr-ymirsystem` rename left `.agents/agents/galdr.md`
|
|
93
93
|
dangling, so Galdr's agent surface did not exist in *any* harness — and
|
|
94
94
|
nothing failed loudly; the harness simply had no such agent.
|